【问题标题】:How do I apply gravity to my bouncing ball application?如何将重力应用到我的弹跳球应用程序?
【发布时间】:2010-09-25 10:12:52
【问题描述】:

我编写了一个相当简单的 java 应用程序,它允许您拖动鼠标,并根据您拖动鼠标的长度,它会朝那个方向射一个球,并在移动时从墙壁上反弹。

这是一个快速截图:
alt text http://img222.imageshack.us/img222/3179/ballbouncemf9.png

屏幕上的每个圆圈都是一个 Ball 对象。球的运动被分解为一个 x 和 y 向量;

public class Ball {
    public int xPos;
    public int yPos;
    public int xVector;
    public int yVector;

    public Ball(int xPos, int yPos, int xVector, int yVector) {
        this.xPos = xPos;
        this.yPos = yPos;
        this.xVector = xVector;
        this.yVector = yVector;
    }

    public void step()
    {
        posX += xVector;
        posY += yVector;

        checkCollisions();
    }

    public void checkCollisions()
    {
        // Check if we have collided with a wall
        // If we have, take the negative of the appropriate vector
        // Depending on which wall you hit
    }

    public void draw()
    {
        // draw our circle at it's position
    }
}

这很好用。所有的球都从一堵墙反弹到另一堵墙。

但是,我已经决定我希望能够包含重力的影响。我知道物体以 9.8m/s 的速度向地球加速,但我不直接知道这应该如何转化为代码。我意识到 yVector 会受到影响,但我对此的实验并没有达到我想要的效果。

理想情况下,我希望能够为这个程序添加一些重力效果,并让球在落到“地面”之前反弹几次。

如何创建这种弹跳有弹性的重力效果?我必须如何在每一步操纵球的速度矢量?当它撞到“地面”时必须做什么才能让它再次弹起,但比上次短一些?

感谢任何帮助,为我指明正确的方向。


感谢大家的cmets!它已经很好用了!

在我的 step() 中,我正在向我的 yVector 添加一个重力常数,就像人们建议的那样,这是我的 checkCollision():

public void checkCollision()
{
    if (posX - radius < 0)              // Left Wall?
    {
        posX = radius;              // Place ball against edge
        xVector = -(xVector * friction);
    }
    else if (posX + radius > rightBound) // Right Wall?
    {
        posX = rightBound - radius;     // Place ball against edge
        xVector = -(xVector * friction);
    }

    // Same for posY and yVector here.
}

但是,球将继续在地板上滑动/滚动。我认为这是因为我只是在每次反弹时仅占其向量的百分比(90%),而且它永远不会真正为零。我应该添加一个检查,如果 xVector 变为某个绝对值,我应该将其更改为零吗?

【问题讨论】:

  • 如果你把显示器倒过来怎么办?
  • 这些天对 iPhone 和所有设备的评论非常有效......
  • 您能否将您的结束代码发布为仅供参考?
  • 如果这些球在 iphone 上,如果你把屏幕倒过来,它们根本不会反弹..

标签: language-agnostic physics gravity


【解决方案1】:

你需要做的是不断地从你的 yVector 中减去一个小的常数(代表你的 9.8 m/s)。当球向下时(yVector 已经是负数),这会使它跑得更快。当它上升时(yVector 为正),它会减慢速度。

这不会解释摩擦,所以事情应该会永远反弹。

编辑1: 为了解释摩擦,每当它反转(并且你反转符号)时,将绝对数字降低一点。就像它在 yVector=-500 处命中一样,当您反转符号时,将其设为 +480 而不是 +500。您可能应该对 xVector 执行相同的操作以阻止它左右弹跳。

编辑2: 此外,如果您希望它对“空气摩擦”做出反应,则每次调整时都将两个向量减少一个非常小的量。

编辑3: 关于永远在底部滚动的东西——根据你的数字有多高,它可能是两件事之一。要么你的数字很大,而且似乎需要很长时间才能完成,要么你正在四舍五入,你的向量总是 5 或其他东西。 (5 的 90% 是 4.5,所以它可能会四舍五入到 5)。

我会打印出一个调试语句,看看向量编号是什么样的。如果它们到达 5 左右并停留在那里,那么你可以使用一个函数将你的分数截断为 4 而不是四舍五入回到 5。如果它继续下降并最终停止,那么你可能不得不提高你的摩擦系数.

如果您找不到简单的“舍入”函数,您可以使用 (0.9 * Vector) - 1,从现有方程中减去 1 应该会做同样的事情。

【讨论】:

    【解决方案2】:

    当球都在地面上滚动时,是的,检查速度是否低于某个最小值,如果是,则将其设置为零。如果您查看这种理想化运动背后的物理原理并与现实世界中发生的情况进行比较,您会发现无法使用单个方程来解释真实球停止运动的事实。

    顺便说一句,您所做的称为数值积分的欧拉方法。它是这样的:

    • 从运动学方程开始:
      x(t) = x0 + vx*t + 0.5*axt^2
      y(t) = y0 + vy
      t + 0.5*ayt^2
      vx(t) = vx0 + ax
      t
      vy(t) = vy0 + ay*t
      其中 x 和 y 是位置,vx 和 vy 是速度,ax 和 ay 是加速度,t 是时间。 x0、y0、vx0 和 vy0 是初始值。 这描述了物体在没有任何外力的情况下的运动。

    • 现在应用重力:
      ay = -9.8 m/s^2
      在这一点上,没有必要做任何棘手的事情。我们可以随时使用这个方程求解每个球的位置。

    • 现在添加空气摩擦:由于它是一个球形球,我们可以假设它的摩擦系数为 c。对于如何模拟空气摩擦,通常有两种选择。它可以与速度或速度的平方成正比。让我们使用正方形:
      ax = -cvx^2
      ay = -c
      vy^2 - 9.8
      因为加速度现在取决于速度,它不是恒定的,我们必须积分。这很糟糕,因为没有办法手动解决这个问题。我们必须在数字上进行整合。

    • 我们采用离散时间步长,dt。对于欧拉方法,我们只需将上述方程中所有出现的 t 替换为 dt,并使用前一个时间步的值代替初始值 x0、y0 等。所以现在我们的方程看起来像这样(在伪代码中) :

      // 保存以前的值
      xold = x;
      yold = y;
      vxold = vx;
      vyold = vy;

      // 更新加速度
      斧头 = -cvxold^2;
      ay = -c
      vyold^2 - 9.8;

      // 更新速度
      vx = vxold + axdt;
      vy = vyold + ay
      dt;

      // 更新位置
      x = xold + vxold*dt + 0.5*axdt^2;
      y = yold + vyold
      dt + 0.5*ay*dt^2;

    这是一个近似值,所以它不会完全正确,但看起来还不错。问题是对于更大的时间步长,误差会增加,所以如果我们想要准确地模拟一个真实球的移动方式,我们必须使用非常小的 dt 值,这会导致计算机的准确性问题。为了解决这个问题,有更复杂的技术。但是如果你只是想同时看到看起来像重力和摩擦的行为,那么欧拉的方法是可以的。

    【讨论】:

      【解决方案3】:

      每个时间片,您都必须通过向下加速球来应用重力效果。正如比尔 K 建议的那样,这就像从“yVector”中减去一样简单。当球击中底部时,yVector = -yVector,所以现在它向上移动但仍在向下加速。如果你想让球最终停止弹跳,你需要使碰撞稍微缺乏弹性,基本上是通过在 y 向上移除一些速度,可能不是“yVector = -yVector”,而是让它成为“yVector = -0.9 * yVector"。

      【讨论】:

      • 每次反弹都需要在两个维度上失去速度,而不仅仅是垂直于地板。
      【解决方案4】:
      public void step()
      {
          posX += xVector;
          posY += yVector;
      
          yVector += g //some constant representing 9.8
      
          checkCollisions();
      }
      

      在 checkCollisions() 中,当 yVector 在地面上反弹时,您应该将 yVector 反转并乘以 0 到 1 之间的数字。这应该会给你想要的效果

      【讨论】:

        【解决方案5】:

        这是一种弹道运动。所以你得到了 x 轴上的线性运动和 y 轴上的匀速加速运动。

        基本思想是y轴将遵循等式:

        y = y0 + v0 * t + (0.5)*a*t^2
        

        或者,在 C 代码中,例如:

        float speed = 10.0f, acceleration = -9.8f, y = [whatever position];
        
        y += speed*t + 0.5f*acceleration*t^2;
        

        我在这里使用 tiem 参数化。但是你可以使用 Torricelli:

        v = sqrt(v0^2 + 2*acceleration*(y-y0));
        

        而且,在这个模型上,您必须保持 v 和 y 的最后一个值。

        最后,我使用第一个模型做了类似的事情,其中​​ dt(时间差)固定为 1/60 秒(60 FPS)。

        嗯,两个模型都给出了很好的真实结果,但是 sqrt(),例如,很昂贵。

        【讨论】:

          【解决方案6】:

          您真的想模拟重力的作用——它所做的只是产生随时间变化的力来改变物体的速度。每次你迈出一步,你都会稍微改变你的球的速度,以便将它“拉”向小部件的底部。

          为了处理无摩擦/弹跳的球落点问题,您需要使“地面”碰撞产生与严格反射不同的效果 - 它应该从球中移除一些能量,使其弹跳撞击地面后以比其他方式更小的速度返回。

          在这些类型的有弹性的可视化中,您通常想要做的另一件事是也给地面一些横向摩擦,这样当它一直撞击地面时,它最终会滚动停止。

          【讨论】:

          • 给地面一些横向摩擦是什么意思?
          【解决方案7】:

          我同意“Bill K”所说的话,并补充说,如果您希望它们“稳定”,您将需要随着时间的推移减少 x 和 y 向量(施加阻力)。这一次必须是非常小的数量,因此您可能必须将向量从 int 更改为浮点类型,或者每隔几秒将它们减少 1。

          【讨论】:

            【解决方案8】:

            您要做的是更改 xVector 和 yVector 的值来模拟重力和摩擦力。这真的很简单。 (需要将所有变量更改为浮点数。绘制时,只需将浮点数四舍五入即可。)

            在你的 step 函数中,更新球的位置后,你应该这样做:

            yVector *= 0.95;
            xVector *= 0.95;
            yVector -= 2.0;
            

            这会稍微降低 X 和 Y 的速度,让你的球最终停止移动,然后对 Y 值应用一个恒定的向下“加速度”,这将比“减速”更快地累积并导致球下落.

            这是您真正想做的近似值。你真正想要的是保留一个代表你的球的加速度的向量。然后,您将每一步都将该向量与恒定的重力向量进行点积,以稍微改变球的加速度。但我认为我比你想得到的更复杂,除非你正在寻找更真实的物理模拟。

            【讨论】:

            • 通过在 step 函数中乘以 yVector,您并不代表像重力一样的恒定加速度。
            • 通过在每一步中放慢球的速度,您将让球看起来像是在通过非常粘稠的东西移动。更现实的效果是只有在弹跳时才会失去能量。
            【解决方案9】:

            当它击中时必须做什么 “接地”,这样我就可以让它 再次反弹

            如果你假设一个完美的碰撞(即所有能量都是守恒的),你所要做的就是反转速度标量之一的符号,这取决于被撞到的墙壁。

            例如,如果球击中右侧或左侧墙壁,则修改 x 标量分量并保持 y 标量分量不变:

             this.xVector = -this.xVector;
            

            如果球击中顶壁或底壁,则反转 y 标量分量并保持 x 标量分量不变:

             this.yVector = -this.yVector;
            

            但比上一个短一些 时间?

            在这种情况下,一些能量会在与墙壁的碰撞中损失掉,所以只需添加一个损失因子以在每次撞到墙壁时获取一些速度:

             double loss_factor = 0.99;
             this.xVector = -(loss_factor * this.xVector);
             this.yVector = -(loss_factor * this.yVector;
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-09-29
              • 2013-05-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多