2

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

vy += gravity
ball.y += vy

我想让球反弹,所以我实现了一个近似的解决方案来检测球是否已经越过地面的边界,如果是的话,将它的位置重置到地面,并将它的速度翻转到负方向。

if (ball.y + ball.radius > bottom) {
ball.y = bottom - ball.radius;
vy *= -1;

但是,我希望这是 100% 准确的。要做到这一点,ball.y 需要稍微高于地面,它的速度将略低于 -vy,因为重力已经开始减慢它。如何计算这些更准确的值?

地下的距离将是ball.y + ball.radius - bottom。所以必须有一个公式可以将其转换为地面以上的距离,我认为是ball.y = (2 * bottom) - ball.y - 2*ball.radius;

这就是我卡住的地方。到目前为止我是对的吗?我现在需要计算新的速度。我确信这是可能的,但我就是无法理解它。任何人都可以帮忙吗?

4

3 回答 3

2

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

弹跳球(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 的值意味着弹性更小、空气阻力更大和摩擦更大。


高级物理模拟 - 如果您受到鼓励并且对物理和数学有基本的了解:

如果您想要更真实的模拟,您可以airDrag使用阻力方程动态更新。

另一个改进是使用基于数值积分的更精确算法来计算速度。这是比较游戏开发中最常用的集成方法的一个很好的示例:http: //codeflow.org/entries/2010/aug/28/integration-by-example-euler-vs-verlet-vs-runge-kutta/#

如果您需要更复杂的模拟,还可以使用box2d-web等物理引擎。

于 2013-07-04T19:54:25.890 回答
1

There are several factors that affect a falling object as it hits the ground

You can adjust for the loss of energy during a bounce by adding a "restitution" factor.

Essentially, restitution represents the bounciness of the ball and ranges from 0-1.

Restitution==0 means the ball doesn't bounce at all--it stops on the ground like a bowling ball.

Restitution==1 means the ball doesn't lose any velocity at all during a bounce.

To implement restitution, you just multiply velocity by restitution:

vy *= restitutionFactor;

If your ball is dropping at an angle, you might also consider implementing "friction" which is an adjustment to directional velocity during a bounce.

Friction represents the loss of directional velocity when your rough ball "rubs" on the floor.

Friction is represented by values from 0 to 1 and is implemented like restitution:

vx *= frictionFactor;

A step-by-step illustration

Assume on Frame#1 the ball has not bounced and is above the ground.

Assume on Frame#2 the ball has bounced and is back in the air.

You have these calculations to make to get Frame#2 ball position.

(1) Save the ball's initial position and initial velocity (we'll need them later):

startingY = ball.y;
startingVelocity = vy;

(2) The ball uses part of the Frame-time to drop to the ground:

ball.y = bottom;

(3) The ball hits the ground and reverses velocity:

vy* = -1;

(4) The new upward velocity is adjusted by restitution and friction:

vy *= restitution;
vx *= friction;     // if moving at an angle

(5) The ball used part of this frame-time moving downward, so the ball gets less than a full frame-time worth of upward velocity:

downtime = ( bottom - startingY ) / startingVelocity;
uptime = (1 - downtime);

(6) The ball bounces upward for the appropriate fraction of a frame and at the new velocity:

ball.y += vy * uptime;

There's another factor you can introduce--ball "smush".

Smush means the ball becomes temporarily flat on its bottom when hitting the ground.

During smush, velocity is suspended. So smush is a delay-time.

Smush varies with the resilience of your ball...more resilience == more smush delay.

You adjust for smush by reducing the "uptime" that the ball can use to move upward.

Revisiting step#5:

uptime = (1 - downtime - smushtime);

These are the standard adjustments to a moving ball...enjoy!

于 2013-07-04T17:34:37.020 回答
0

您已经使用了加速度:重力。

为什么不使用另一种加速度:反弹。

您唯一需要的是使“反弹”指数化,因此它不能穿透地面,因为您仅在球“足够接近地面” 而不是低于或确切位置之后应用该加速度。

重力:无处不在。弹跳:仅在离地一定距离后才存在(因为球有半径,并且会有一些缓冲距离让球在“弹跳”下加速)

然后让这种加速发挥作用,而不是摆弄许多 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 积分。您使用的是非常简单的欧拉版本。最快但不稳定/不准确。

当你使用数千个相互弹跳的球时,你可以用谷歌搜索 Verlet 和 Runge-Kutta。

于 2013-07-05T15:27:53.917 回答