我写了一个相当简单的java应用程序,它允许你拖动你的鼠标,并根据你所做的鼠标拖动的长度,它将向那个方向射击一个球,随着它的移动而从墙壁上弹回.
这是一个快速截图:
替代文字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变成某个绝对值,我应该把它改成零吗?
你要做的就是不断从你的yVector中减去一个小的常数(代表你的9.8 m/s).当球落下时(yVector已经为负),这将使其变得更快.当它上升时(yVector为正)它会减慢速度.
这不会解释摩擦,所以事情应该永远反弹.
edit1:为了解决摩擦问题,只要它反转(并且你反转符号),请稍微降低绝对数.就像它在yVector = -500时命中一样,当你反转符号时,将其设为+480而不是+500.您可能应该对xVector执行相同的操作,以防止它从一侧到另一侧弹跳.
edit2:另外,如果你想让它对"空气摩擦"作出反应,每次调整都会减少两个向量.
编辑3:关于永远在底部滚动的东西 - 根据您的数字有多高,它可能是两件事之一.要么你的数字很大而且它似乎需要永远完成,或者你正在四舍五入,你的向量总是5或者什么.(5%的90%是4.5,因此它可能会达到5).
我打印出一个调试语句,看看Vector数字是什么样的.如果它们到达5左右的某个地方而只是停留在那里,那么你可以使用一个函数将你的分数截断为4而不是回到5.如果它继续下降并最终停止,那么你可能需要提高你的摩擦系数.
如果你找不到一个简单的"舍入"函数,你可以使用(0.9*向量) - 1,从你现有的等式中减1应该做同样的事情.
当球在地面上滚动时,是的,检查速度是否低于某个最小值,如果是,则将其设置为零.如果你看一下这种理想化运动背后的物理特性,并与现实世界中的情况进行比较,你会发现单个方程不能用来解释真实球停止移动的事实.
顺便说一句,你正在做的事情被称为数值积分的欧拉方法.它是这样的:
从运动的运动方程开始:
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.如何模拟空气摩擦通常有两种选择.它可以与速度或速度的平方成比例.让我们使用square:
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值,这会导致计算机的准确性问题.要解决这个问题,有更复杂的技术.但是如果你只是想看到同时看起来像重力和摩擦的行为,那么欧拉的方法就可以了.