弹跳速度计算,与地面碰撞

时间:2013-07-04 13:13:40

标签: canvas collision-detection physics game-physics gravity

在画布上,我有一个球在重力作用下落地

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;

这是我陷入困境的地方。我到目前为止对吗?我现在需要计算新的速度。我确信这是可能的,但我无法理解它。有人可以帮忙吗?

3 个答案:

答案 0 :(得分:2)

这是一个演示重力和弹跳物理的完整演示:

Bouncing Ball (jsfiddle)


代码是自解释的,但进行模拟的主要部分是更新函数,其中包含以下代码:

// 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。