在Libgdx中修正物理引擎Box2d时间步长
2014-09-08 21:15
295 查看
文章翻译并修改自原文David Saltares。博主经验丰富,他的很多关于Libgdx的文章都值得一读,再此特作推荐。
Fix your Timestep是Glenn Fiedler所写的一篇关于处理物理模拟的文章。这篇文章是2006年的,但是其中大部分内容在今天仍然具有启发性。
这篇文章涉及了处理物理引擎中的时间增量的不同方法。这个问题很复杂,但是不可否认它对游戏行为和表现的影响。
我认为将这些内容迁移到使用Box2D物理引擎的Libgdx应用中是非常好的点子。这些也可以应用到Bullet中,它是另外一款优秀的物理引擎,而且迁移的改动非常小。
最合乎逻辑的方案是计算现在时点在最后一帧后经过的时间,并传递给物理引擎。这样做显然会让每一帧的表现不同,整个物理引擎会表现的很不稳定。不同设备之间的表现会由于设备自身的不同(比如内存、运算器)产生变化。很明显,这不是我们期待的方案。
我们需要一个累加器来保存时间,然后尽可能的保持物理引擎和渲染一致。当这一帧时间过多时,我们可以将多余时间保留给下一帧。
我们可以在这两个物理状态间追加一次实体的变化来减缓这种延迟。而追加的依据就是我们剩余的时间。
更详细的追加可以参考SionCore。
Download demo jar
Game Screen
sioncore
Fix your Timestep是Glenn Fiedler所写的一篇关于处理物理模拟的文章。这篇文章是2006年的,但是其中大部分内容在今天仍然具有启发性。
这篇文章涉及了处理物理引擎中的时间增量的不同方法。这个问题很复杂,但是不可否认它对游戏行为和表现的影响。
我认为将这些内容迁移到使用Box2D物理引擎的Libgdx应用中是非常好的点子。这些也可以应用到Bullet中,它是另外一款优秀的物理引擎,而且迁移的改动非常小。
简单却错误的方法
将时间步长设定为1/60每秒,这是非常常见而且简单的方法。它可以让物理模拟稳定运行,但是当游戏运行FPS降到60以下时,物理引擎的表现会非常糟。而这在移动设备上非常常见。最合乎逻辑的方案是计算现在时点在最后一帧后经过的时间,并传递给物理引擎。这样做显然会让每一帧的表现不同,整个物理引擎会表现的很不稳定。不同设备之间的表现会由于设备自身的不同(比如内存、运算器)产生变化。很明显,这不是我们期待的方案。
固定的时间步长
如果物理引擎以一个固定的步长,如1/60秒去运行,那么有些设备可能会运行的过快并达到120FPS。而同时有些设备可能运行过于缓慢,达到30FPS,那么物理引擎每两帧才前进一次。如果游戏以50FPS运行,会出现什么情况
我们需要一个累加器来保存时间,然后尽可能的保持物理引擎和渲染一致。当这一帧时间过多时,我们可以将多余时间保留给下一帧。
public class SionGame extends ApplicationListener { private World world; private double accumulator; private double currentTime; private float step = 1.0f / 60.0f; public void render() { double newTime = TimeUtils.millis() / 1000.0; double frameTime = Math.min(newTime - currentTime, 0.25); float deltaTime = (float)frameTime; currentTime = newTime; while (accumulator >= step) { world.step(step, velIterations, posIterations); accumulator -= step; } }其中step是固定的时间步长,而accumulator是剩余的时间。
追加处理
因为accumulator中保存了剩余的时间,但是它又不足驱动物理引擎一次。这样就会出现一种视觉上的延迟或者卡顿,因为你的渲染所经历的时间比物理时间经历的时间长。我们可以在这两个物理状态间追加一次实体的变化来减缓这种延迟。而追加的依据就是我们剩余的时间。
public class SionGame extends ApplicationListener { private World world; private double accumulator; private double currentTime; private float step = 1.0f / 60.0f; public void render() { double newTime = TimeUtils.millis() / 1000.0; double frameTime = Math.min(newTime - currentTime, 0.25); float deltaTime = (float)frameTime; currentTime = newTime; while (accumulator >= step) { world.step(step, velIterations, posIterations); accumulator -= step; entityManager.update(); } entityManager.interpolate(accumulator / step); }其中的Entityanager是一个专门的类。它承担为维持游戏实体集合的任务,并通过
update()来更新物理世界,通过
interpolate()方法来追加变化。
public void interpolate(float alpha) { for (Entity entity : entities) { Transform transform = entity.getBody().getTransform(); Vector2 bodyPosition = transform.getPosition(); Vector2 position = entity.getPosition(); float angle = entity.getAngle(); float bodyAngle = transform.getRotation(); position.x = bodyPosition.x * alpha + position.x * (1.0f - alpha); position.y = bodyPosition.y * alpha + position.x * (1.0f - alpha); entity.setAngle(bodyAngle * alpha + angle * (1.0f - alpha)); } }
更详细的追加可以参考SionCore。
实例
Libgdx社区的成员Just4phil提供了一个本文阐述的方法的实例。详见参考部分。参考
fix your timestepDownload demo jar
Game Screen
sioncore
相关文章推荐
- Libgdx专题系列:物理引擎篇 Box2D
- 6.在cocos2d里面如何使用物理引擎box2d:弹球
- cocos2d-x初探学习笔记(20)--物理引擎box2d(2)
- HTML5游戏开发开源库件lufylegend1.4.0发布,新增物理引擎Box2dWeb封装和缓动类TweenLite
- 基于HTML5的WebGL结合Box2DJS物理引擎应用
- Android 2d物理引擎Box2d介绍
- /LGC物理引擎/Ubuntu 下编译运行 Box2D
- (2011-12-11 旧博文搬运)[基于BOX2D引擎]物理切割游戏:猫狗照相馆
- Android游戏引擎libgdx使用教程9:libgdx中Box2d的用法
- cocos2dx-3.0 中的关于物理引擎Box2D与chipmunk
- box2d 初识 强大的物理碰撞引擎 结合canvas
- Android 游戏引擎libgdx之Box2D 案例实践——弹球
- /LGC物理引擎/Box2D:一个 2D rigid body 物理引擎
- cocos2d学习笔记(九)物理引擎box2d之三
- 基于HTML5的WebGL结合Box2DJS物理引擎应用
- (译)在cocos2d里面如何使用物理引擎box2d:弹球
- coco2dx 3.0以后最box2d和chipmunk这两个物理引擎进行了封装,使用起来非常的便利。
- 物理引擎Box2D 入门
- Cocos2d-x教程Box2D 物理引擎
- Box2D引擎实现割绳子物理部分的方法