在画布上,我有一个球在重力作用下落地
vy += gravity
ball.y += vy
我希望球能够反弹,所以我已经实施了一个近似的解决方案,以检测球是否已经越过地面边界,如果是,则将其重置到地面,并在负面翻转它的速度方向。
if (ball.y + ball.radius > bottom) {
ball.y = bottom - ball.radius;
vy *= -1;
但是,我希望这是100%准确。为了做到这一点,它需要稍微高于地面,并且它的速度将略微低于-vy,因为重力已经开始减慢它。如何计算这些更准确的值?
地面以下的距离为ball.y + ball.radius - bottom
。所以必须有一个公式,将其转换为高于地面的距离,我认为是ball.y = (2 * bottom) - ball.y - 2*ball.radius;
这是我陷入困境的地方。我到目前为止对吗?我现在需要计算新的速度。我确信这是可能的,但我无法理解它。有人可以帮忙吗?
答案 0 :(得分:2)
这是一个演示重力和弹跳物理的完整演示:
代码是自解释的,但进行模拟的主要部分是更新函数,其中包含以下代码:
// calculate new position
ball.x += ball.vx;
ball.y += ball.vy;
// bounce Y (don't bounce on top)
if (ball.y >= bottom - ball.radius) {
ball.y = bottom - ball.radius; // (!) GROUND LIMIT
ball.vy = -(ball.vy * ball.elasticity);
}
// bounce X
if ((ball.x >= right - ball.radius) || (ball.x <= left + ball.radius)) {
ball.x = (ball.x < (left + ball.radius) ? (left + ball.radius) : (right - ball.radius));
ball.vx = -(ball.vx * ball.elasticity);
}
// compute gravity
ball.vy += gravity;
// compute frictions
ball.vx *= airDrag;
ball.vy *= airDrag;
if (ball.y >= (bottom - ball.radius)) {
ball.vx *= groundFriction;
}
上面的代码添加了三个不同的物理变量,可以创建更逼真的模拟:
ball.elasticity
:恢复系数; airDrag
:阻力系数; groundFriction
:摩擦力施加在地面上移动。 上述所有变量均由0到1的值表示。最接近1的值意味着弹性更大,空气阻力更小,摩擦力更小。最接近0的值意味着弹性更小,空气阻力更大,摩擦力更大。
如果您想要更逼真的模拟,可以使用drag equation动态更新airDrag
。
另一项改进是使用基于数值积分的更精确的算法来计算速度。这是一个比较游戏开发中最常用的集成方法的一个很好的例子:http://codeflow.org/entries/2010/aug/28/integration-by-example-euler-vs-verlet-vs-runge-kutta/#
如果您需要更复杂的模拟,也可以使用像box2d-web这样的物理引擎。
答案 1 :(得分:1)
有多个因素会影响掉落的物体
您可以通过添加“恢复原因”因素来调整反弹期间的能量损失。
基本上,恢复原状代表球的弹性,范围从0到1。
恢复原状== 0表示球根本没有反弹 - 它像保龄球一样停在地面上。
恢复== 1意味着球在弹跳过程中根本不会失去任何速度。
要实现恢复原状,你只需通过恢复原状来增加速度:
vy *= restitutionFactor;
如果您的球以一定角度下降,您可能还会考虑实施“摩擦力”,这是对弹跳过程中方向速度的调整。
当你的粗糙球在地板上“摩擦”时,摩擦力表示方向速度的损失。
摩擦力由0到1的值表示,并且像恢复原状一样实施:
vx *= frictionFactor;
分步说明
假设在第1帧,球没有反弹并高于地面。
假设在第2帧,球已反弹并重新回到空中。
你有这些计算来获得第2帧的球位置。
(1)保存球的初始位置和初始速度(我们稍后会需要它们):
startingY = ball.y;
startingVelocity = vy;
(2)球使用帧时间的一部分掉到地上:
ball.y = bottom;
(3)球击中地面并逆转速度:
vy* = -1;
(4)通过恢复原状和摩擦来调整新的向上速度:
vy *= restitution;
vx *= friction; // if moving at an angle
(5)球使用了这个帧时间的一部分向下移动,所以球的速度小于整帧时间值:
downtime = ( bottom - startingY ) / startingVelocity;
uptime = (1 - downtime);
(6)球以适当的帧数和新的速度向上弹跳:
ball.y += vy * uptime;
你可以介绍另一个因素 - 球“smush”。
Smush是指击球时球在底部暂时变平。
在脸红期间,速度暂停。所以smush是一个延迟时间。
Smush因球的弹性而变化...弹性更强= =更多的甩尾延迟。
您可以通过减少球可以用来向上移动的“正常运行时间”来调整傻瓜。
重新审视第5步:
uptime = (1 - downtime - smushtime);
这些是移动球的标准调整......享受!
答案 2 :(得分:0)
你已经使用了加速度:重力。
为什么不使用另一种加速度:反弹。
你唯一需要的是使“弹跳”指数,因此它不能穿透地面,因为你只有在球“足够接近地面”而不是低于或确切位置时才应用该加速度。 / p>
重力:无处不在。 弹跳:仅在距离地面一定距离后才存在(因为球有一个半径,并且在“反弹”下球会有一些缓冲距离让球加速)
然后让这种加速完成它的工作而不是摆弄许多if语句和特殊计算。事实上,由于保利的排斥原则+电磁学,它实际上是一种加速。
vy += gravity
if(closeEnough)
{
bounce=bConstant/pow(distance,3.0f)
vy-=bounce //this bounce will be calculated as F=bConstant/pow(distance,3.0f)
// bConstant is a small number that you try and choose
//That power does not have to be 3. You can try different values.
}
ball.y += vy
指数排斥力使球越来越接近接近地面的每个像素,然后你会看到那里的自然弹跳运动。
你不能准确%100,即使在现实世界中也是如此。将时间步长降低到尽可能低的值。不要直接为速度添加加速度。在您的示例中,您的时间步长为1(直接将加速度添加到速度)。
让时间步长较小(例如0.1f),以便更稳定,更准确地运动:
timeStep = 0.1f;
vy += gravity * timeStep;
if(closeEnough)
{
bounce=bConstant/pow(distance,3.0f)
vy-=bounce * timeStep
//this bounce will be calculated as F=bConstant/pow(distance,3.0f)
// bConstant is a small number that you try and choose
//That power does not have to be 3. You can try different values.
}
ball.y += vy * timeStep
同时使用时间步长可以根据游戏的FPS使用正确的加速度和位移。 (时间步长= 1 / FPS)
排水系统主要有三种类型。 Euler的集成,Verlet集成和Runge-Kutta集成。您使用的是Euler的非常简单的版本。最快但不稳定/不准确。
当你使用数千个相互弹跳的球时,你可以使用google Verlet和Runge-Kutta。