如何实现游戏主循环的详细分析,游戏开发者
作为一个游戏程序员,我见过很多手机游戏的代码。这些代码向我展示了丰富多彩的方法来实现游戏的主循环。你可能会问,“这么简单的小工具怎么能做出奇怪的事情呢?”是这样的话,我会在本文中讨论一些主流实现的优缺点,给大家介绍一下我认为的输送养分的最佳方案。
游戏主循环
每个游戏都包括获取用户输入、更新游戏状态、处理AI、播放音乐和音效以及显示图片。游戏的主循环用来处理这个行为序列。我在简介里说过,游戏的主循环是每一场游戏的心跳。在本文中,我不会深入解释上面提到的任何一种行为,只详细介绍游戏的主循环。所以我把这些行为简化为两个功能:
update _ game();//更新游戏状态(后面的文字可能会翻译成逻辑帧)
显示_游戏();//更新显示(显示帧)
以下是游戏最简单的主循环:
bool game _ is _ running = true
当(game_is_running ) {
update _ game();
显示_游戏();
}
这个简单循环的主要问题是它忽略了时间,游戏会尽可能快地运行。在恶霸机器上运行会让玩家感到极度沮丧,而在牛逼机器上运行则需要玩家有超人的判断力和APM。%26#8230;)。在古代,硬件的速度是已知的,这不算什么,但目前硬件平台如此之多,我们不得不处理时间这个重要因素。处理时间的方法有很多种,我一一呈现。
首先,我将解释贯穿整篇文章的两个术语:
每秒帧数(以下简称FPS)
FPS是每秒帧数的缩写。在本文的上下文中,它表示每秒调用display_game()的次数。
游戏速度
游戏速度就是每秒更新游戏状态的速度,换句话说就是每秒调用update_game()的次数。
2.FPS依赖于恒定的游戏速度。
实现
保持游戏以每秒25帧运行的解决方案如下:
const int FRAMES _ PER _ SECOND = 25
const int SKIP _ TICKS = 1000/FRAMES _ PER _ sec;
DWORD next _ game _ tick = GetTickCount();
// GetTickCount()返回当前毫秒数
//自系统启动以来已经过去的时间
int sleep _ time = 0;
bool game _ is _ running = true
当(game_is_running ) {
update _ game();
显示_游戏();
next _ game _ tick+= SKIP _ TICKS;
sleep _ time = next _ game _ tick-GetTickCount();
if(sleep _ time & gt;= 0 ) {
睡眠(sleep _ time);
}
否则{
//妈的,我们落后了!
}
}
这个方案有一个很大的优点:简单!因为你知道update_game()每秒被调用25次,所以你的游戏的逻辑部分会非常直观。比如在这个游戏中用主循环实现一个回放功能会非常简单(译者注:因为每一帧的间隔时间是已知的,所以只需要记录游戏每一帧的状态,回放时匀速播放即可。就像电影胶片一样)。如果在游戏中不受随机值影响,只需要记录玩家的输入就可以实现回放。
你可以在实现这个循环的硬件上把FRAMES_PER_SECOND调整到一个理想值,但是游戏的主循环在各种硬件上会有怎样的表现呢?
小霸王机器
如果硬件可以处理指定的FPS,什么都不会发生。但是恶霸一般都应付不了,游戏会卡壳。极端情况下会卡顿,或者一次十卡,一次十步(原意是游戏速度有些情况下慢,有些情况下正常)。这样的问题会毁了你的游戏,让玩家沮丧。
超棒的机器
在牛逼的机器上看起来没什么问题,但是这个游戏的主循环浪费了很多时钟周期!牛逼机可以轻松运行游戏到300帧,但是每秒只运行25、30帧~那么这个主循环的实现会让硬件牛逼的玩家无法充分发挥自己的硬件效果,会造成很大的挫败感(本意是这个实现会影响你的视觉效果,尤其是高速移动的物体)。
另一方面,这可能是移动设备上的优势。游戏的持续高速运行会很快消耗电池。%26#8230;
结论
基于恒定游戏速度的FPS主循环实现方案简单易学。但是,也有一些问题。比如定义的FPS太高,经典电脑会不堪重负,定义的FPS太低,高端硬件会失去太多视觉效果。
3.基于可变FPS的游戏速度
实现
另一个游戏实现可以让游戏尽可能快地运行,让游戏速度根据FPS来决定。游戏状态将根据每个显示帧所消耗的时间进行更新。
DWORD prev _ frame _ tick
DWORD curr _ frame _ tick = GetTickCount();
bool game _ is _ running = true
当(game_is_running ) {
prev _ frame _ tick = curr _帧_ tick;
curr _ frame _ tick = GetTickCount();
update _ game(curr _ frame _ tick-prev _ frame _ tick);
显示_游戏();
}
这个游戏主循环的代码比之前复杂了一点,因为我们要考虑两次update_game()调用的时间差。然而幸运的是,这并不复杂。
乍一看,这个实现的代码似乎是一个理想的实现方案。我见过很多聪明的游戏程序员都是这样写游戏主循环的。但是我会在小霸王和牛逼机上给你看这个实现方案的严重问题!是的,包括非常专业,非常熟练,非常牛逼的玩家的机器。