如何让精灵在不同的帧率下运动速度不变--Frame Rate Independent Movement
2006-05-22 00:45
323 查看
本文性质:翻译加原创,转载请保留原作者和译者信息!
行文安排:前不份是翻译,后部分是自己的原创。
FrameRateIndependentMovement
byBenDilts
翻译KevinLynx2006-5-21
第一部分:
Ihaveseencountlesspostsonthisandothermessageboards,andevenpersonalemailhasbeensenttome,allaskingthesamequestion:HowdoImakeitsomystupidobjectsmoveatthesamespeediftheframeraterisesordrops?
在各种论坛以及我收到的个人邮件里,很多人都问我相同的一个问题:我是怎样让我游戏里那些物体以相同速度运动,而无论帧率是下降还是上升?
Afterafewmonthsprogramming,IdevisedwhatIbelievetobethemostefficientandusefulwaytohandlethisproblem.Forthepurposesofthisarticle,Iwillencapsulatethisfunctionalityinitsownclass.
在几个月的编程以后,我设计了一个我认为最有效果且最有用的方法来解决这个问题。为了在这篇文章里达到这个目的,我把那些有作用的东西封装成一个类:
Ileaveallthememberspublicbecausetheyworkindependentlyofeachotherandareallusefulinsomewaytotheoutsideprogram.。
我把每一个成员都设置成public的,是因为他们都是独立工作并且对于外部程序,他们都是有用的。(和外部环境联系比较大,所以就直接设置成public的)
Hereistheimplementation,followedbysomeexplanation:
以下是执行代码和一些解释:
Someexplanation:
targetfpsispassedintotheInitfunction,butcanbesetatanytime.Itisthetargetframeratefortheprogram.ThisisusedintheSetSpeedFactorfunctiontodeterminewhatthespeedfactorwillbe.Lookdownatmyexplanationofspeedfactor.
一些解释:
targetfps是由Init函数来设置的,但是它也可以在任何时候被设置。它用来表示程序中物体的帧率。这个被用在SetSpeedFactor函数里去决定speedfactor会怎么样。看下面对于speedfactor的解释。
fpsistheactualframerateoftheprogram.Itisnotreallyneededtomakeframerateindependentmovement,butsinceitissocloselylinked,Iincludeitanyway.
fps用来表示程序真实的帧率。它没有必要去让帧率成为独立的运动,但是很接近。(不明白这句话的意思)
tickspersecondissetintheInitfunctiontobethenumberofticksthehighperformancetimerhaspersecond.
currentticksissetintheSetSpeedFactorfunctiontobethecurrenthighperformancetimerticks.
framedelayisthepreviousframecurrentticks.
tickspersecond在Init函数里被设置成每秒的tick数。
currentticks在SetSpeedFactor被设置成当前时钟ticks.
framedelay是前一帧的ticks。
speedfactoristheheartofthisclass.WhenitissetisSetSpeedFactor,itbecomesanumberthatyoumultiplyallyourmotionby.Forinstance,ifthetargetfpsis100,andtheactualfpsis85,thespeedfactorwillbesetto100/85,orabout1.2.Youthenmultiplyallyourmotionibthegame,atitslowestlevel,bythisnumber.
speedfactor是这个类的核心。当它在SetSpeedFactor中被设置时,它成为一个****的数。举个例子,如果物体的FPS是100,而真实的FPS是85,speedfactor就会被设置为100/85,或者1.2。在游戏里你把所有的动作与这个数相乘,。。。。
Forinstance,ratherthansimplycodingspaceship->MoveForward(5),Iwouldcodespaceship->MoveForward(5*framerate.speedfactor).
例如,比起简单地spaceship->MoveForeward(5),我会这样写spaceship->MoveForeward(5*frame.speedfactor);(在张勇的超级马力里有类似的方法)
Inconclusion:
Thissimpleroutinesavesawholeheapoftrouble.Justplugitintoalmostanygameorotherreal-timeprogram.CallInitatthebeginningoftheprogram,andtheneachframecallSetSpeedFactor.Thenmultiplyallyourmovementbythespeedfactor.Itisthatsimple.Nowstopfloodingthosemessageboards:)
这个简单的事例解决了很多问题,你可以在游戏里或者任何实时程序里。在程序开始时调用Init,然后在程序的每一帧里调用SetSpeedFactor。然后把所有物体的移动都乘上speedfactor。这确实很简单,以后就别来发消息给我了:)(。)
第二部分:
译者的理解:
其实以上讲的东西,在我以前看的一分超级马力的游戏里用的技术是一样的。我们不限制图象渲染的帧率,只限制精灵逻辑更新的帧率,或者说,在这里不是限制其更新间隔,而是限制其更新的属性大小------坐标改变的大小。
现在开始推导:
假设我们想把我们的游戏的FPS限制在85,85即是我们想要的帧率,这里我以want_fps表示;但是游戏真正的FPS呢?真正的FPS会随着机器性能的高低而明显不同,我用actual_fps表示;精灵sprite每次坐标增加量用coor_add表示。
那么,当actual_fps==want_fps时,sprite_coor+=coor_add;这是很理想的状态;当actual_fps<want_fps时,这个时候的表现就是,精灵比我要求动作的速度慢了----因为逻辑更新的间隔变大了,这个时候要想精灵的速度不变(或者变化很小),那么,我们就要加大coor_add,这里,coor_add显然是和actual_fps和want_fps有关系的。
于是我们可以这样做:sprite_coor+=coor_add*(want_fps/actual_fps);因为现在actual_fps<want_fps,所以其比值就大于1,这样就可以增大这个坐标增加量。
同理,当actual_fps>want_fps时,want_fps./actual_fps就小于1,这样就缩小了坐标增加量。
于是,这样就达到了在不同真实帧率下,依然可以让精灵的移动恒定。
我们所要做的,也就是得到want_fps/actual_fps的比值,也就是上文中的speedfactor,
要计算这个值,我们有两种方法:
1.按照我们以往计算FPS的方式算出实际的FPS,然后再根据希望的FPS来得到
2.这也是更为通用的方法,也是上文的方法,即通过时间间隔直接计算,反过来根据这个值来计算实际的FPS。
第一种方法很简单,而用第二种方法时,如何去直接计算呢?我们可以反向推出其计算方法----当然上文已经直接给出了计算方法
设欲求比值为speedfactor,要求达到的FPS为want_fps,实际的FPS为actual_fps,游戏主循环上次得到的时间TICK为last_tick,本次的TICK为this_tick-(其实这里可以理解为毫秒时间)。
于是:
因为每循环一次,就更新一帧,于是更新一帧用的毫秒数为:(this_tick–last_tick)=è
更新一帧用时:(this_tick–last_tick)===è于是:
actual_fps=1/[(this_tick–last_tick)/1000]
=1000/(this_tick–last_tick)====è
因为want_fps已知,根据:
speedfactor=want_fps/actual_fps===è
speedfactor=want_fps/actual_fps
=want_fps/[1000/(this_tick–last_tick)]
=want_fps*(this_tick–last_tick)/1000
=want_fps*每一帧用的秒数
最终公式也就是:
speedfactor=want_fps*(this_tick–last_tick)/1000
与上文提供的公式对比,完全一致。
OK,本文到此结束。
行文安排:前不份是翻译,后部分是自己的原创。
FrameRateIndependentMovement
by
翻译KevinLynx2006-5-21
第一部分:
Ihaveseencountlesspostsonthisandothermessageboards,andevenpersonal
在各种论坛以及我收到的个人邮件里,很多人都问我相同的一个问题:我是怎样让我游戏里那些物体以相同速度运动,而无论帧率是下降还是上升?
Afterafewmonths
在几个月的编程以后,我设计了一个我认为最有效果且最有用的方法来解决这个问题。为了在这篇文章里达到这个目的,我把那些有作用的东西封装成一个类:
classframerate
{
public:
floattargetfps;
floatfps;
LARGE_INTEGERtickspersecond;
LARGE_INTEGERcurrentticks;
LARGE_INTEGERframedelay;
floatspeedfactor;
voidInit(floattfps);
voidSetSpeedFactor();
};
Ileaveallthememberspublicbecausetheyworkindependentlyofeachotherandareallusefulinsomewaytotheoutside
我把每一个成员都设置成public的,是因为他们都是独立工作并且对于外部程序,他们都是有用的。(和外部环境联系比较大,所以就直接设置成public的)
Hereistheimplementation,followedbysomeexplanation:
以下是执行代码和一些解释:
voidframerate::Init(floattfps)
{
targetfps=tfps;
QueryPerformanceCounter(&framedelay);
QueryPerformanceFrequency(&tickspersecond);
}
voidframerate::SetSpeedFactor()
{
QueryPerformanceCounter(¤tticks);
//Thisframe'slengthoutofdesiredlength
speedfactor=(float)(currentticks.QuadPart-framedelay.QuadPart)/((float)tickspersecond.QuadPart/targetfps);
fps=targetfps/speedfactor;
if(speedfactor<=0)
speedfactor=1;
framedelay=currentticks;
}
Someexplanation:
targetfpsispassedintotheInitfunction,butcanbesetatanytime.Itisthetargetframeratefortheprogram.ThisisusedintheSetSpeedFactorfunctiontodeterminewhatthespeedfactorwillbe.Lookdownatmyexplanationofspeedfactor.
一些解释:
targetfps是由Init函数来设置的,但是它也可以在任何时候被设置。它用来表示程序中物体的帧率。这个被用在SetSpeedFactor函数里去决定speedfactor会怎么样。看下面对于speedfactor的解释。
fpsistheactualframerateoftheprogram.Itisnotreallyneededtomakeframerateindependentmovement,butsinceitissocloselylinked,Iincludeitanyway.
fps用来表示程序真实的帧率。它没有必要去让帧率成为独立的运动,但是很接近。(不明白这句话的意思)
tickspersecondissetintheInitfunctiontobethenumberofticksthehighperformancetimerhaspersecond.
currentticksissetintheSetSpeedFactorfunctiontobethecurrenthighperformancetimerticks.
framedelayisthepreviousframecurrentticks.
tickspersecond在Init函数里被设置成每秒的tick数。
currentticks在SetSpeedFactor被设置成当前时钟ticks.
framedelay是前一帧的ticks。
speedfactoristheheartofthisclass.WhenitissetisSetSpeedFactor,itbecomesanumberthatyoumultiplyallyourmotionby.Forinstance,ifthetargetfpsis100,andtheactualfpsis85,thespeedfactorwillbesetto100/85,orabout1.2.Youthenmultiplyallyourmotionibthegame,atitslowestlevel,bythisnumber.
speedfactor是这个类的核心。当它在SetSpeedFactor中被设置时,它成为一个****的数。举个例子,如果物体的FPS是100,而真实的FPS是85,speedfactor就会被设置为100/85,或者1.2。在游戏里你把所有的动作与这个数相乘,。。。。
Forinstance,ratherthansimplycodingspaceship->MoveForward(5),Iwouldcodespaceship->MoveForward(5*framerate.speedfactor).
例如,比起简单地spaceship->MoveForeward(5),我会这样写spaceship->MoveForeward(5*frame.speedfactor);(在张勇的超级马力里有类似的方法)
Inconclusion:
Thissimpleroutinesavesawholeheapoftrouble.Justplugitintoalmostanygameorotherreal-timeprogram.CallInitatthebeginningoftheprogram,andtheneachframecallSetSpeedFactor.Thenmultiplyallyourmovementbythespeedfactor.Itisthatsimple.Nowstopfloodingthosemessageboards:)
这个简单的事例解决了很多问题,你可以在游戏里或者任何实时程序里。在程序开始时调用Init,然后在程序的每一帧里调用SetSpeedFactor。然后把所有物体的移动都乘上speedfactor。这确实很简单,以后就别来发消息给我了:)(。)
第二部分:
译者的理解:
其实以上讲的东西,在我以前看的一分超级马力的游戏里用的技术是一样的。我们不限制图象渲染的帧率,只限制精灵逻辑更新的帧率,或者说,在这里不是限制其更新间隔,而是限制其更新的属性大小------坐标改变的大小。
现在开始推导:
假设我们想把我们的游戏的FPS限制在85,85即是我们想要的帧率,这里我以want_fps表示;但是游戏真正的FPS呢?真正的FPS会随着机器性能的高低而明显不同,我用actual_fps表示;精灵sprite每次坐标增加量用coor_add表示。
那么,当actual_fps==want_fps时,sprite_coor+=coor_add;这是很理想的状态;当actual_fps<want_fps时,这个时候的表现就是,精灵比我要求动作的速度慢了----因为逻辑更新的间隔变大了,这个时候要想精灵的速度不变(或者变化很小),那么,我们就要加大coor_add,这里,coor_add显然是和actual_fps和want_fps有关系的。
于是我们可以这样做:sprite_coor+=coor_add*(want_fps/actual_fps);因为现在actual_fps<want_fps,所以其比值就大于1,这样就可以增大这个坐标增加量。
同理,当actual_fps>want_fps时,want_fps./actual_fps就小于1,这样就缩小了坐标增加量。
于是,这样就达到了在不同真实帧率下,依然可以让精灵的移动恒定。
我们所要做的,也就是得到want_fps/actual_fps的比值,也就是上文中的speedfactor,
要计算这个值,我们有两种方法:
1.按照我们以往计算FPS的方式算出实际的FPS,然后再根据希望的FPS来得到
2.这也是更为通用的方法,也是上文的方法,即通过时间间隔直接计算,反过来根据这个值来计算实际的FPS。
第一种方法很简单,而用第二种方法时,如何去直接计算呢?我们可以反向推出其计算方法----当然上文已经直接给出了计算方法
设欲求比值为speedfactor,要求达到的FPS为want_fps,实际的FPS为actual_fps,游戏主循环上次得到的时间TICK为last_tick,本次的TICK为this_tick-(其实这里可以理解为毫秒时间)。
于是:
因为每循环一次,就更新一帧,于是更新一帧用的毫秒数为:(this_tick–last_tick)=è
更新一帧用时:(this_tick–last_tick)===è于是:
actual_fps=1/[(this_tick–last_tick)/1000]
=1000/(this_tick–last_tick)====è
因为want_fps已知,根据:
speedfactor=want_fps/actual_fps===è
speedfactor=want_fps/actual_fps
=want_fps/[1000/(this_tick–last_tick)]
=want_fps*(this_tick–last_tick)/1000
=want_fps*每一帧用的秒数
最终公式也就是:
speedfactor=want_fps*(this_tick–last_tick)/1000
与上文提供的公式对比,完全一致。
OK,本文到此结束。
相关文章推荐
- 贝塞尔曲线:如何让一个正在按照贝塞尔曲线运动的精灵减速或加速(动画结束回调)
- 理论:如何调整四轴4个电机的转速,使飞行器朝不同方向运动
- CSplitterWnd分割Frame,左边是CTreeView,如何右边是对应选中的不同Dialog??
- 速度版运动框架(获得属性+requestAnimationFrame请求动画帧)
- 不同机器下,游戏编程如何保证物体移动具有相同的速度
- ROBODK仿真如何设置运动速度
- 控制精灵以相同的速度在不同设备上移动
- 小球沿不同轨迹运动时水平方向速度的比较
- 速度优化:只加载一个CSS满足所有要求、针对不同浏览器加载不同CSS、正式站改版测试如何不影响用户体验
- Pyspark系列笔记--如何成功join不同的pyspark dataframe
- [Tips] [Windows Phone 7] 如何開啟和關閉畫面更新率(Frame Rate Counters)的顯示
- 面向Web服务的游戏设计6:不同客户端的精灵运动同步
- 探讨:小球沿不同路线运动时的水平速度
- 报表制作过程中出现输入不同参数查询数据不变的情况
- Java-如何为frame窗口设置背景图片
- sqlserver 两个数据库中表的结构不同,如何把一个表中的数据导入到另一个表中
- JS-多物体运动2(不同物体做不同的运动)
- [VB.NET]如何来优化sqldatareader的读取速度
- 两个不同数据库之间的两个表如何关联起来查询数据