【LWJGL官方教程】Game loops 游戏循环
来源:互联网 发布:淘宝价格走势app 编辑:程序博客网 时间:2024/06/04 19:20
原文:https://github.com/SilverTiger/lwjgl3-tutorial/wiki/Game-loops
译注:翻译仅供参考,请以原文为准。代码请看原文中的链接。括号里的内容一般也是译注,供理解参考用。
PS:Markdown还挺好用的,给CSDN点个赞。
How does a game work? 一个游戏如何工作?
从编程角度来讲,一个游戏可以用一个简单的方法来描述:
public void startGame() { init(); gameLoop(); dispose();}
这就是游戏全部必须要做的事,init()包含初始化的全部内容,比如创建窗口、读取资源。dispose()包含释放游戏资源的全部代码。
A simple game loop 一个简单的游戏循环
游戏循环的关键部分是处理输入、更新游戏逻辑、渲染游戏。
public void gameLoop() { while (running) { input(); update(); render(); }}
这样的游戏循环会耗尽全部的CPU,因为它运行得太快了。通常你并不需要它这样,因此需要让循环适当停歇。
public void gameLoop() { long sleepTime = 1000L / 60L; while (running) { input(); update(); render(); sleep(sleepTime); }}
在这个例子里,加入了16毫秒的休眠时间,游戏每秒将只更新62.5次。
虽然这已经比毫无停歇要好一些了,但是其实你需要有一个可变的休眠时间以保证游戏有稳定的FPS。为此,我们应该看看时间运算教程。
根据时间计数器的选择,有两种形式的游戏循环:
- 可变时步游戏循环
- 固定时步游戏循环
Variable timestep 可变时步
用可变时步,你不需要关心你的游戏是不是跑得太快或太慢了,因为会使用时间增量(delta time)来更新游戏。(意思是,如果跑太慢了,时间增量超长,那么就一次更新许多内容,太快也是同理)
在这种循环,我们首先就得拿到时间增量
float delta = timer.getDelta();
时间增量用来更新游戏。所以给update()方法加上参数。
public void update(float delta) { /* do updates */}
之后也不需要再加什么了,最后是这样的:
public void gameLoop() { while (running) { float delta = timer.getDelta(); input(); update(delta); render(); window.update(); }}
你可能想问,为啥不休眠了。因为你可以让GLFW去激活垂直同步,这样你不需要再关心休眠的问题了。
但是如果我们在循环里加入休眠,也不需要改太多东西。
public void gameLoop() { long targetTime = 1000L / targetFPS; while (running) { /* Note that you have to multiply by 1000 to get milliseconds */long startTime = timer.getTime() * 1000; float delta = timer.getDelta(); input(); update(delta); render(); window.update(); /* Same as above, multiply time in seconds by 1000 */long endTime = timer.getTime() * 1000; sleep(startTime + targetTime - endTime); }}
但是这个循环有一个瑕疵:你的更新受限于你的帧率,在简单的游戏里这还OK,但是如果是想做一个逼真的物理情况模拟,这就不是你想要的了。(意思是,假如FPS很低的话,那更新次数也很低,时间增量也超长,如果模拟铁球下落,会看到铁球在空中是断断续续的,一次还落超长距离……效果很糟糕。大部分情况下虽然不会这么明显,但是也会让玩家有一种违和感。)
Fixed timestep 固定时步
固定时步的话,你的游戏循环不能再受限于帧率了,而是基于时间,比起可变时步,它更具有确定性。
固定时步除了时间增量以外还需要有更多的变量,需要一个累加器(accumulator)记录经过的时间,一个表示更新之间的时间应该是多少的间隔量(interval),还需要有一个aplha值来充当插值。
float delta;float accumulator = 0f;float interval = 1f / 30f;float alpha;
在这个例子里,我们希望每秒有30次更新,所以间隔量大概是33.33毫秒。
变量有了,开始循环。跟可变循环一样,先要拿到时间增量,但在此循环里,还要把增量加在累加器上。
delta = timer.getDelta();accumulator += delta;
接着处理输入。再之后要检查经过的时间是否应该做更新操作了。(累加器时间超过设定的间隔量,按间隔量来做更新)
while (accumulator >= interval) { update(interval); accumulator -= interval;}
之后我们其实还剩下一些多出来的时间,举个例子。
比如现在游戏已经开始了48毫秒,但是我们下一次更新应该是在66.67毫秒的时候,因为我们说好是每33.33毫秒更新一次的(之前设定的间隔量)。这时累加器的值应该是48-33.33=14.67毫秒。
所以我们当前的游戏状态其实是14.67毫秒以前的游戏状态,现在我们可以再渲染一次相同的屏幕内容,但是那样的话我们的游戏看起来就像是以30FPS来渲染而不是我们预期的那样,帧在做重复无意义的废渲染。为了能表现出两次更新间的某种状态,我们引入了alpha这个插值。(意思是,我们虽然希望一秒更新30次,但是却期望能更精确地连续渲染画面,而不是离散地去重复渲染这30次更新时的画面,那样就算一秒渲染了100次,其实也是在反复重复地渲染这30个更新瞬间的画面而已。)
alpha = accumulator / interval;
在上面说的例子里,alpha值应该是14.67/33.33=0.44,所以我们距下次更新大概行进到了44%的阶段。为了渲染,我们需要保持上一次和本次的状态插值,具体在另一篇教程里再说。
最后,固定时步循环应该是这样的:
public void gameLoop() { float delta; float accumulator = 0f; float interval = 1f / targetUPS; float alpha; while (running) { delta = timer.getDelta(); accumulator += delta; input(); while (accumulator >= interval) { update(); accumulator -= interval; } alpha = accumulator / interval; render(alpha); window.update(); }}
关于udpate方法,有一点:如果间隔量不打算改的话,其实没必要再把间隔量放在update方法里了。(因为它是固定的值)
本教程里,update()写作update(delta),render()写作render(alpha),所以你用哪种时步都可以。
public void update() { update(1f / targetUPS);}public void render() { render(1f);}
下一篇学习用shader来渲染。
- 【LWJGL官方教程】Game loops 游戏循环
- 【LWJGL官方教程】游戏逻辑
- 【LWJGL官方教程】渲染
- 【LWJGL官方教程】纹理
- 【LWJGL官方教程】文字
- 【LWJGL官方教程】Introduction 入门
- 【LWJGL官方教程】Timing 计时
- 【LWJGL官方教程】总目录
- 【LWJGL官方教程】批渲染
- 【LWJGL官方教程】输入处理
- j2me游戏开发之LWJGL(Lightweight Java Game Library)
- LWJGL教程--- 1.The Display
- 运行循环(Run Loops)
- 使用Ansible loops编写循环
- Run Loops官方文档翻译(一)
- 僵尸入侵游戏主体部分2--循环和game对象
- 用goto实现infinite loops(死循环)
- 嵌套循环连接(nested loops join)原理
- nginx+tomcat负载均衡、动静分离
- 动画(二)
- 转载:200多个js技巧代码
- money
- 人生三重界
- 【LWJGL官方教程】Game loops 游戏循环
- Linux 进阶 文件和文件系统的压缩和打包
- 动画(三)
- 托尔斯泰
- Node.js 内存泄露 定位
- 表达式转型注意事项,和final修饰的变量会被JVM优化
- Deep Learning及NLP(自然语言处理)杂谈--第一部分
- css中margin-left与left的区别
- Deep Learning及NLP(自然语言处理)杂谈--第二部分