弹跳球不符合能量守恒规则

时间:2009-05-11 17:59:45

标签: physics simulator bounce

我目前正在为Win32 API和c ++中的编程课程编写一个小球物理引擎。我已经完成了GDI backbuffer渲染器和整个GUI(需要调整的几件事),但我已接近完成。最后的唯一重大障碍是球与球之间的碰撞(但我可以自己解决这个问题),但最重要的问题就是球的弹跳。会发生什么事情,我扔了一个球,它真的下降,但一旦它反弹它会反弹高于我释放它的点?有趣的是,只有在低于一定高度时才会发生。这部分是物理代码: (如果您需要更多代码或解释,请询问,但如果您可以查看我的代码,我将非常感谢。)

#void RunPhysics(OPTIONS &o, vector<BALL*> &b)
{ 
    UINT simspeed = o.iSimSpeed;
    DOUBLE  DT; //Delta T
    BOOL bounce; //for playing sound

    DT= 1/o.REFRESH;

    for(UINT i=0; i<b.size(); i++)
    {
        for(UINT k=0; k<simspeed; k++)
        {           
            bounce=false;

            //handle the X bounce
            if( b.at(i)->rBall.left <= 0 && b.at(i)->dVelocityX < 0 ) //ball bounces against the left wall
            {
                b.at(i)->dVelocityX = b.at(i)->dVelocityX * -1 * b.at(i)->dBounceCof;
                bounce=true;
            }
            else if( b.at(i)->rBall.right >= SCREEN_WIDTH && b.at(i)->dVelocityX > 0) //ball bounces against the right wall
            {           
                b.at(i)->dVelocityX = b.at(i)->dVelocityX * -1 * b.at(i)->dBounceCof;
                bounce=true;
            }
            //handle the Y bounce
            if( b.at(i)->rBall.bottom >= SCREEN_HEIGHT && b.at(i)->dVelocityY > 0 ) //ball bounces against the left wall
            {
                //damping of the ball
                if(b.at(i)->dVelocityY < 2+o.dGravity/o.REFRESH)
                {
                    b.at(i)->dVelocityY = 0;
                }

                //decrease the Velocity of the ball according to the bouncecof
                b.at(i)->dVelocityY = b.at(i)->dVelocityY * -1*b.at(i)->dBounceCof;
                b.at(i)->dVelocityX = b.at(i)->dVelocityX * b.at(i)->dBounceCof;

                bounce=true;
            }


            //gravity
            b.at(i)->dVelocityY += (o.dGravity)/o.REFRESH;
            b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER; 
            //METER IS DEFINED GLOBALLY AS 100 which is the amount of pixels in a meter

            b.at(i)->pOrigin.x += b.at(i)->dVelocityX/o.REFRESH*METER; 

            b.at(i)->UpdateRect();
        }
    }
    return;
}

9 个答案:

答案 0 :(得分:4)

您正在使用Euler积分方法。您的时间步长(DT)可能太大。更新Y坐标的行似乎也有错误:

  b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER; 

您已经将重力添加到速度,因此您无需将其添加到位置,也不会将速度乘以DT。它应该是这样的:

  b.at(i)->pOrigin.y += b.at(i)->dVelocityY * DT; 

此外,对于单位(使用METER的方式)似乎存在一些混淆。

答案 1 :(得分:1)

好的,这里有一些事情。

对于左墙和右墙反弹,您有不同的代码路径,但代码是相同的。结合这些代码路径,因为代码是相同的。

关于你的基本问题:我怀疑你的问题源于你在施加任何阻尼力/反弹力后施加重力的事实。

答案 2 :(得分:1)

你什么时候打电话给RunPhysics?在计时器循环中?此代码只是一个近似值,没有精确计算。在delta t的短间隔内,球已经改变了他的位置和速度,这在你的算法中没有考虑并且几乎没有错误。你必须计算直到球击中地面并预测变化的时间。

并且重力已包含在速度中,因此请勿在此处添加两次:

b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER;

顺便说一句:将b.at(i)保存在一个临时变量中,这样就不必在每行中重新计算它。

Ball* CurrentBall = b.at(i);

答案 3 :(得分:1)

ANSWER !! ANSWER !! ANSWER !!但我忘记了我的其他帐户,所以我无法标记它: - (

感谢所有精彩的回复,它真的帮了我很多!你给出的答案确实是正确的,我的一些公式是错的,并且可以完成一些代码优化,但没有一个真正解决问题。所以我只是坐下来拿着一张纸,开始计算我手工编程得到的每一个价值,花了我两个小时:O但我确实找到了解决问题的方法: 问题是,当我更新我的速度(更正的代码)时,我得到一个十进制值,没有问题。后来我通过将速度乘以Delta T来增加Y中的位置,这是一个非常小的值。结果是需要添加的小值。现在的问题是,如果在Win32中绘制Elipse(),则点为LONG,因此所有小数值都将丢失。这意味着只有经过一段很长的时间后,当值的速度开始从小数值开始出现时会发生一些事情,并且随之而来的是,你丢球越高,结果越好(我的一个症状)解决方案这个问题非常简单,给我的Ball类增加了一个额外的DOUBLE值,其中包含了我球的真实位置(包括小数)。在RenderFrame()期间,您只需取双倍的底面或天花板值来绘制椭圆,但对于所有计算,您使用Double值。再次感谢所有回复,STACKOVERFLOW PEOPLE ROCK !!!

答案 4 :(得分:0)

如果你的dBounceCof是&gt; 1然后,是的,你的球将反弹更高。 我们没有能够回复您问题的所有值。

答案 5 :(得分:0)

我不认为你的立场方程是正确的:

 b.at(i)->dVelocityY += (o.dGravity)/o.REFRESH;

这是v=v0+gt - 虽然我写的是dGravity*DT而不是dGravity/REFRESH_FREQ,但这似乎很好。

 b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER; 

但这似乎是关闭的:它与p = p0+v + 1/2gt^2等效。

  • 你应该乘以速度*时间以使单位正确
  • 您正在按像素/米来缩放重力项,而不是速度项。因此,应该乘以METER
  • 您在更新速度时已经考虑了重力的影响,因此您无需再次添加重力项。

答案 6 :(得分:0)

感谢您的快速回复!对不起,我应该更清楚,RunPhysics正在PeekMessage之后运行。我还添加了一个帧限制器,确保每秒不再进行计算,而不是监视器的刷新率。因此,我的dleta因刷新率而下降1秒。也许我的DT实际上太小而无法计算,虽然这是一个双重值???我的恢复原状可以调整,但从0.9开始

答案 7 :(得分:0)

你需要在弹跳时重新计算你的位置,以确保你从墙上的正确位置反弹。

即。解决弹跳发生时的确切时间点,并根据方向变化计算新的速度/位置(部分计算到“框架”),以确保您的球不会“超出”墙壁,越来越多每次反弹。

W.r.t。时间步骤,您可能需要查看my answer here

答案 8 :(得分:0)

在刚体模拟中,您需要在碰撞瞬间运行积分,然后调整速度以避免在碰撞时穿透,然后恢复积分。这是一种瞬间的克服,可以涵盖刚体是近似的事实。 (真正的球在碰撞过程中会变形。这很难建模,而且对于大多数用途来说都是不必要的。)

您将这两个步骤结合起来(整合力量并解决碰撞)。对于你所展示的简单模拟,在你处理垂直反弹的任何迭代中跳过重力位可能就足够了。

在更高级的模拟中,您将在实际碰撞实例中拆分包含碰撞的任何间隔(dt)。积分到碰撞,然后解决碰撞(通过调整速度),然后积分剩余的间隔。但这对你的情况看起来有点过分了。