如何将重力应用于弹跳球应用?

时间:2008-12-04 21:42:32

标签: language-agnostic physics gravity

我编写了一个相当简单的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会受到影响,但我对此的实验没有达到我想要的效果。

理想情况下, 我希望能够为此程序添加一些重力效果,并且还可以让球在稳定到“地面”之前反弹几次。

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

在向我指出正确的方向时,我们非常感谢任何帮助。


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

在我的步骤()中,我正在向我的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变成某个绝对值,我应该把它改成零吗?

9 个答案:

答案 0 :(得分:15)

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

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

EDIT1: 为了解释摩擦,无论何时反转(并且你反转符号),都要稍微降低绝对数量。就像它在yVector = -500时命中一样,当你反转符号时,将其设为+480而不是+500。您可能应该对xVector执行相同的操作,以防止它从一侧到另一侧弹跳。

EDIT2: 此外,如果您希望它对“空气摩擦”作出反应,请在每次调整时将两个矢量减少很少量。

EDIT3: 关于永远在底部滚动的东西 - 根据您的数字有多高,它可能是两件事之一。要么你的数字很大而且似乎需要永远完成,或者你正在四舍五入,你的向量总是5或者什么。 (5%的90%是4.5,所以最多可以达到5)。

我打印出一个调试语句,看看Vector数字是什么样的。如果它们到达5左右的某个地方并且只是停留在那里,那么你可以使用一个函数将你的分数截断到4而不是回到5。如果它继续下降并最终停止,那么你可能需要提高你的摩擦系数

如果你找不到一个简单的“舍入”函数,你可以使用(0.9 *向量) - 1,从你现有的等式中减1应该做同样的事情。

答案 1 :(得分:13)

当球在地面上滚动时,是的,检查速度是否低于某个最小值,如果是,则将其设置为零。如果你看一下这种理想化运动背后的物理特性,并与现实世界中的情况进行比较,你会发现单个方程不能用来说明真实球停止运动的事实。

顺便说一下,你所做的是被称为数字积分的欧拉方法。它是这样的:

  • 从运动的运动方程开始:
    x(t)= x0 + vx * t + 0.5 * ax t ^ 2
    y(t)= y0 + vy
    t + 0.5 * ay t ^ 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 = -c vx ^ 2
    ay = -c
    vy ^ 2 - 9.8
    因为加速度现在取决于速度,这不是常数,我们必须整合。这很糟糕,因为手无法解决这个问题。我们必须在数字上进行整合。

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

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

    //更新加速度
    ax = -c vxold ^ 2;
    ay = -c
    vyold ^ 2 - 9.8;

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

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

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

答案 2 :(得分:1)

每次切片都需要通过向下加速球来施加重力效果。比尔K建议,这就像从你的“yVector”中减去一样简单。当球击中底部时,yVector = -yVector,所以现在它向上移动但仍然向下加速。如果你想让球最终停止弹跳,你需要使碰撞略微无弹性,基本上是通过在y-up方向上移除一些速度,可能是通过代替“yVector = -yVector”,使其成为“yVector = -0.9” * yVector“。

答案 3 :(得分:1)

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

    yVector += g //some constant representing 9.8

    checkCollisions();
}

在检查碰撞()中,当它在地面上反弹时,你应该将Vector反转并乘以0到1之间的数字。这应该会给你带来预期的效果

答案 4 :(得分:1)

这是一场弹道运动。因此,您在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()价格昂贵。

答案 5 :(得分:0)

你真的想要模拟引力的作用 - 它所做的就是创造力随着时间的推移而改变物体的速度。每当你迈出一步,你就会稍微改变你的球的速度,以便将它“拉”到小部件的底部。

为了应对无摩擦/弹跳球的问题,你需要让“地面”碰撞产生不同于严格反射的不同效果 - 它应该从球上移除一些能量,使其反弹在撞击地面后以较小的速度返回。

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

在你的步进功能中,在更新球的位置后,你应该做这样的事情:

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

这会略微缩小X和Y的速度,让你的球最终停止移动,然后向Y值施加恒定的向下“加速度”,这将比“减速”更快地积累并导致球落下

这是您真正想要做的事情的近似值。你真正想要的是保持一个代表球加速度的矢量。然后,每一步,您都会使用恒定的重力矢量对该矢量进行点测,以略微改变球的加速度。但我认为除非你正在寻找更逼真的物理模拟,否则我会比你想要的更复杂。

答案 8 :(得分:0)

  

当它击中时必须做什么   “地面”,以便我可以允许它   再次反弹

如果你假设一个完美的碰撞(即所有的能量都是守恒的),那么你必须根据撞击的墙壁反转其中一个速度标量的符号。

例如,如果球击中右侧或左侧墙壁,则会显示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;