3

这可能是一个非常基本的问题,但我似乎找不到任何其他文章。

无论如何,我已经用 Java 编写了一个小的弹跳球程序来尝试扩展我的基本技能。该程序只是一个简单的弹跳球,它会掉落并希望弹跳一段时间。原始程序运行良好,但现在我尝试在程序中添加重力。重力实际上可以正常工作一段时间,但是一旦反弹变得非常小,动画就会在很短的时间内变得不稳定,然后球的位置就会不断减小。我试图找出问题所在,但我就是看不到它。任何帮助都将受到欢迎。

public final class Ball extends Rectangle {
float xspeed = 1.0f; float yspeed = 1.0f; float gravity = 0.4f;


public Ball(float x, float y, float width, float height) {
    super(x, y, width, height);
}

public void update(){
    yspeed += gravity;

    move(xspeed, yspeed);

    if(getX() < 0){
        xspeed = 1;
    }
    if(getX() + getWidth() > 320){
        xspeed = -1;
    }
    if(getY() < 0){
        yspeed = 1;
    }
    if(getY() + getHeight() > 200 && yspeed > 0){
        yspeed *= -0.98f;
    }
    if(getY() + getHeight() > 200 && yspeed < 0){
        yspeed *= 0.98f;
    }

}

public void move(float x, float y){
    this.setX(getX()+x);
    this.setY(getY()+y);
}

}

编辑:谢谢,似乎已经对不稳定的运动进行了分类。我仍在努力寻找如何在球停止弹跳时阻止它向下移动。现在它会移动停止弹跳然后继续向下移动通过“地板”。我认为这与我的 yspeed += 重力线有关。我只是看不出我将如何停止运动。

4

5 回答 5

3

当你这样做

yspeed += gravity;

假设球有空间移动距离 dx = v_i * t + 1/2 (-g) t^2。当您非常靠近地板时,这可能不是真的。如果出现以下情况,它会失败:

  • 你离地板足够近并且向下移动
  • 您非常靠近地板并且速度很低(例如当球失去大部分能量时)

此错误会导致您的模拟停止保存能量,从而导致您在低幅度下看到的不稳定行为。

您可以通过使用较小的时间步来减少问题,如果您进行测试计算以注意到您何时没有空间并为该迭代选择一个安全的时间步(即始终使用您的默认值,除非有问题,则计算最佳时间步长)。

但是,您在此处使用的基本近似值也存在其他问题。在任何数值分析文本中查找关于数值求解微分方程的讨论。

于 2009-03-04T02:24:06.070 回答
1

我怀疑这是因为当球反弹时,它实际上会略低于“地面”,并且在低速时,它不会在一个滴答声中移回地面 - 所以下一次 update() 会看到它仍然低于地面,然后再次反弹 - 但这次向下,所以循环继续。

当球反弹时,您需要将球移回地面,如下所示:

    if(getY() + getHeight() > 200){
            yspeed *= -0.981;
            setY(200 - getHeight());
    }
于 2009-03-04T02:24:42.650 回答
1

首先要做的事情:当你在窗口顶部弹跳时将 y-speed 设置为 1 是不正确的,你应该将 yspeed 设置为 -yspeed (但如果你从边界内开始,它无论如何都不应该弹到顶部)。

其次,在底部弹跳时乘以 -0.981 是可以的,但我担心每次迭代都会向 yspeed 添加恒定的 0.4 重力。我认为这就是导致您在底部摆动的原因,因为您在检查可能导致球跌至地面以下之前进行了移动。

我会尝试通过替换移动来确保 y 值永远不会低于地面:

if (getY() + getHeight() + yspeed > 200) {
    move(xspeed, 200 - getY() - getHeight());
} else {
    move(xspeed, yspeed);
}
于 2009-03-04T02:25:24.647 回答
1

问题是当反弹变得非常小时,

yspeed *= -0.981;

线路将在短时间内被调用。球会低于底部,然后开始反弹,但最终仍会低于底部(因为 0.981 < 1.0),并且它的表现会非常糟糕。以下是您修复它的方法:

if(getY() + getHeight() > 200){
  yspeed *= -0.981;
  setY(400 - getY() - getHeight()); // I believe this is right.
}

通过固定位置,您不会在减少和增加之间快速交替,也不会陷入它总是在减少的情况,因为它总是低于界限​​。

于 2009-03-04T02:25:40.310 回答
1

[编辑:我想我误解了,所以这可能没什么用:)]

if(getY() + getHeight() > 200){
  yspeed *= -0.981;
}

您正在否定每次更新的垂直速度。我可能会尝试在更新大小的切片中处理重力。假设您每秒进行 30 次更新(对于 30fps),可能类似于

// Define some constants
SecondsPerUpdate = (1.0f / 30);
AccelDueToGravity = 0.981;

if(getY() + getHeight() > 200){
  yspeed -= (AccelDueToGravity * SecondsPerUpdate);
}
于 2009-03-04T02:25:45.927 回答