iPhone 游戏开发教程 游戏引擎是本文要介绍的内容,先来看内容详解。为了解决“如何在IPHONE上创建一个游戏”这个大问题,我们需要首先解决诸如“如何显示图像”与“如何播放声音”等一系列小问题。这些问题关系到创建部分游戏引擎。就像人类的身体一样,游戏引擎的每个部分虽然不同,但是却都不可或缺。因此,首先从游戏引擎剖析开始本章。我们将会讨论一个游戏引擎的所有主要部分,包括应用程序框架、状态机、图像引擎、物理引擎、声音引擎、玩家输入和游戏逻辑。
写一个好玩的游戏是一项牵扯到很多代码的大任务。非常有必要从一开始就对项目进行良好的,有组织的设计,而不是随着进度的进行而到处杂乱添加代码。就像建造房屋一样,建筑师为整幢房屋勾画蓝图,建筑工人以此来建造。但是,许多对游戏编程不熟悉的编程人员会从根据导读建造出房屋的一部分,并随着学习的进行为其添加房间,这无疑将会导致不好的结果。
图2-1 游戏引擎的功能结构
图2-1显示了一个适用于大部分游戏的游戏引擎结构。为了理解一个游戏引擎的所有部分和它们是如何工作在一起的,我们可以先为整个游戏做设计,然后再创建我们的应用程序。在以下的几个小节中,我们的讲解内容将会涵盖图2-1的每个部分。
应用程序框架
游戏状态管理器
图像引擎
应用程序框架
应用程序框架包含使应用程序工作的必须代码,包括创建一个应用程序实例和初期化其他子系统。当应用程序运行时,会首先创建一个框架类,并接管创建和销毁状态机、图像引擎和声音引擎。如果我们的游戏足够复杂以至于它需要一个物理引擎,框架也会管理它。
框架必须适应于我们所选择的平台的独特性,包括相应任何的系统事件(如关机与睡眠),以及管理载入与载出资源以使其他的代码只需要集中与游戏。
主循环
框架会提供主循环,它是一切互动程序后的驱动力量。在循环中的每一次迭代过程中,程序会检查和处理接受到的事件,运行游戏逻辑中的更新并在必要时将内容描画到屏幕上。(参见图2-2)
图2-2 主循环序列
主循环如何实现依赖于你使用的系统。对于一个基本的控制台程序,它可能是一个简单的while循环中调用各个函数:
- while( !finished ) {
- handle_events();
- update();
- render();
- sleep(20);
- }
注意到这里的sleep函数。它使得代码休眠一小段时间不致于占用全部的CPU。
有些系统完全不想让用户代码那些写,它们使用了回调系统以强制程序员常规的释放CPU。这样,当应用程序执行后,程序员注册一些函数给系统在每次循环中回调:
- void main(void) {
- OS_register_event_handler( myEventHandler );
- OS_register_update_function( myUpdate );
- OS_register_render_function( myRender );
- }
一旦程序执行后,根据必要情况,那些函数会间隔性的被调用。IPHONE是最接近后面这个例子。你可以在下一章和IPHONE SDK中看到它。
游戏状态管理器
一个好的视频游戏不仅有一组动作来维持游戏:它会提供一个主菜单允许玩家来设定选项和开始一个新游戏或者继续上次的游戏;制作群屏将会显示所有辛勤制作这款游戏的人员的名字;而且如果你的游戏没有用户指南,应该一个帮助区域会给用户一些提示告诉他们应该做什么。
以上任何一种场合都是一种游戏状态,并且代表中一段独立的应用程序代码片段。例如,用户在主菜单调用的函数与导航与用户在制作群屏调用的是完全不同的,所以程序逻辑也是不同的。特别的是,在主菜单,你可能会放一张图片和一些菜单,并且等待用户选择哪个选项,而在制作群屏,你将会把游戏制作人员的名字描绘在屏幕上,并且等待用户输入,将游戏状态从制作群屏改为主菜单。最后,在游戏中状态,将会渲染实际的游戏并等待用户的输入以与游戏逻辑进行交互。
以上的所有游戏状态都负责相应用户输入、将内容渲染到屏幕、并为该游戏状态提供相对应的应用程序逻辑的任务。你可能注意到了这些任务都来自于之前讨论的主循环中,这是因为它们就是同样的任务。但是,每个状态都会以它们自己的方式来实现这些任务,这也就是为什么要保持他们独立。你不必在主菜单代码中寻找处理游戏中的事件的代码。
状态机
状态管理器是一个状态机,这意味着它跟踪着现在的游戏状态。当应用程序执行后,状态机会创建基本的状态信息。它接着创建各种状态需要的信息,并在离开每种状态时销毁暂时存储的信息。
状态机维护着大量不同对象的状态。一个明显的状态是用户所在屏幕的状态(主菜单、游戏中等)。但是如果你有一个有着人工智能的对象在屏幕上时,状态机也可以用来管理它的“睡眠”、“攻击”、“死亡”状态。
什么是正确的游戏状态管理器结构?让我们看看一些状态机并决定哪种最适合我们。
有许多实现状态机的方式,最基本的是一个简单的switch语句:
- class StateManager {
- void main_loop() {
- switch(myState) {
- case STATE_01:
- state01_handle_event();
- state01_update();
- state01_render;
- break;
- case STATE_02:
- state02_handle_event();
- state02_update();
- state02_render;
- break;
- case STATE_03:
- state03_handle_event();
- state03_update();
- state03_render;
- break;
- }
- }
- };
改变状态时所有需要做的事情就是改变myState变量的值并返回到循环的开始处。但是,正如你看到的,当我们加入越来越多的状态时,代码块会变得越来越大。而且更糟的是,为了使程序按我们预期的执行,我们需要在程序进入或离开某个状态时执行整个任务块,初始化该状态特定的变量,载入新的资源(比如图片)和释放前一个状态载入的资源。在这个简单的switch语句中,我们需要加入更多的程序块并保证不会漏掉任何一个。
以上是一些简单重复的劳动,但是我们的状态管理器需要更好的解决方案。下面一种更好的实现方式是使用函数指针:
- class StateManager {
- //the function pointer:
- void (*m_stateHandleEventFPTR) (void);
- void (*m_stateUpdateFPTR)(void);
- void (*m_stateRenderFPTR)(void);
- void main_loop() {
- stateHandleEventFPTR();
- m_stateUpdateFPTR();
- m_stateRenderFPTR();
- }
- void change_state( void (*newHandleEventFPTR)(void),
- void (*newUpdateFPTR)(void),
- void (*newRenderFPTR)(void)
- ) {
- m_stateHandleEventFPTR = newHandleEventFPTR;
- m_stateUpdateFPTR = newUpdateFPTR;
- m_stateRenderFPTR = newRenderFPTR
- }
- };
现在,即使我们处理再多状态,主循环也足够小而且简单。但是,这种解决方案依然不能帮助我们很好的解决初始化与释放状态。因为每种游戏状态不仅包含代码,还有各自的资源,所以更恰当的做法是将游戏状态作为对象的属性来考虑。因此,接下来,我们将会看看面向对象(OOP)的实现。
我们首先创建一个表示游戏状态的类:
- class GameState
- {
- GameState(); //constructor
- virtual ~GameState(); //destructor
- virtual void Handle_Event();
- virtual void Update();
- virtual void Render();
- };
接着,我们改变我们的状态管理器以使用这个类:
- class StateManager {
- GameState* m_state;
- void main_loop() {
- m_state->Handle_Event();
- m_state->Update();
- m_state->Render();
- }
- void change_state( GameState* newState ) {
- delete m_state;
- m_state = newState;
- }
- };
最后,我们创建一个指定具体游戏状态的类:
- class State_MainMenu : public GameState
- {
- int m_currMenuOption;
- State_MainMenu();
- ~State_MainMenu();
- void Handle_Event();
- void Update();
- void Render();
- };
当游戏状态以类来表示时,每个游戏状态都可以存储它特有的变量在该类中。该类也可以它的构造函数中载入任何资源并在析构函数中释放这些资源。
而且,这个系统保持着我们的代码有很好的组织结构,因为我们需要将游戏状态代码分别放在各个文件中。如果你在查找主菜单代码,你只需要打开State_MainMenu类。而且OOP解决方案使得代码更容易重用。这个看起来是最适合我们需要的,所以我们决定使用它来作为我们的状态管理器。
小结:iPhone 游戏开发教程 游戏引擎的内容介绍完了,希望本文对你有所帮助。想要深入了解游戏引擎的更多内容,请参考以下几篇文章: