我目前正在为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;
}
答案 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)
感谢所有精彩的回复,它真的帮了我很多!你给出的答案确实是正确的,我的一些公式是错的,并且可以完成一些代码优化,但没有一个真正解决问题。所以我只是坐下来拿着一张纸,开始计算我手工编程得到的每一个价值,花了我两个小时: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
等效。
答案 6 :(得分:0)
感谢您的快速回复!对不起,我应该更清楚,RunPhysics正在PeekMessage之后运行。我还添加了一个帧限制器,确保每秒不再进行计算,而不是监视器的刷新率。因此,我的dleta因刷新率而下降1秒。也许我的DT实际上太小而无法计算,虽然这是一个双重值???我的恢复原状可以调整,但从0.9开始
答案 7 :(得分:0)
你需要在弹跳时重新计算你的位置,以确保你从墙上的正确位置反弹。
即。解决弹跳发生时的确切时间点,并根据方向变化计算新的速度/位置(部分计算到“框架”),以确保您的球不会“超出”墙壁,越来越多每次反弹。
W.r.t。时间步骤,您可能需要查看my answer here。
答案 8 :(得分:0)
在刚体模拟中,您需要在碰撞瞬间运行积分,然后调整速度以避免在碰撞时穿透,然后恢复积分。这是一种瞬间的克服,可以涵盖刚体是近似的事实。 (真正的球在碰撞过程中会变形。这很难建模,而且对于大多数用途来说都是不必要的。)
您将这两个步骤结合起来(整合力量并解决碰撞)。对于你所展示的简单模拟,在你处理垂直反弹的任何迭代中跳过重力位可能就足够了。
在更高级的模拟中,您将在实际碰撞实例中拆分包含碰撞的任何间隔(dt)。积分到碰撞,然后解决碰撞(通过调整速度),然后积分剩余的间隔。但这对你的情况看起来有点过分了。