3

我目前正忙于为我的 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;
}
4

9 回答 9

4

您正在使用欧拉积分方法。您的时间步长 (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 的方式)似乎有些混淆。

于 2009-05-11T18:20:58.900 回答
1

好的,这里有几件事。

你有不同的代码路径来反弹左墙和右墙,但代码是相同的。组合这些代码路径,因为代码是相同的。

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

于 2009-05-11T18:09:58.870 回答
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);
于 2009-05-11T18:26:52.587 回答
1

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

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

于 2009-05-12T21:56:26.263 回答
0

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

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

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

于 2009-05-11T20:05:25.560 回答
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
  • 您在更新速度时已经考虑了重力的影响,因此您无需再次添加重力项。
于 2009-05-11T18:42:56.837 回答
0

感谢您的快速回复!!!抱歉,我应该更清楚一点,RunPhysics 是在 PeekMessage 之后运行的。我还添加了一个帧限制器,以确保每秒执行的计算不会超过显示器的刷新率。因此,我的 dleta t 是 1 秒除以刷新率。也许我的DT实际上太小无法计算,虽然它是一个double值???我的补偿系数是可调的,但从 0.9 开始

于 2009-05-11T19:14:52.430 回答
0

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

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

Wrt time step,您可能想在这里查看我的答案

于 2009-05-11T19:39:14.357 回答
0

如果您的 dBounceCof > 1,那么是的,您的球会弹得更高。我们没有能够回答您的问题的所有价值观。

于 2009-05-11T18:10:15.157 回答