Chapter 2. Game Engine Anatomy游戏引擎剖析(揭秘)

翻译自 iPhone Game Development Developing 2D & 3D games in Objective-C

To solve the large problem of how to create a game on the iPhone, we first need to solvea series of smaller problems such as how to display graphics and play sounds. Theseare problems associated with building parts of a game engine. And just like the humanbody, each part of a game engine is different but vital. Thus begins our chapter on gameengine anatomy. We will discuss each major part of a game engine, including the ap-plication framework, state machine, graphics engine, physics engine, audio engine,player input, and game logic.
为了解决“如何在IPHONE上创建一个游戏”这个大问题,我们需要首先解决诸如“如何显示图像”与“如何播放声音”等一系列小问题。这些问题关系到创建部分游戏引擎。就像人类的身体一样,游戏引擎的每个部分虽然不同,但是却都不可或缺。因此,首先从游戏引擎剖析开始本章。我们将会讨论一个游戏引擎的所有主要部分,包括应用程序框架、状态机、图像引擎、物理引擎、声音引擎、玩家输入和游戏逻辑。*
Writing a serious game is a big task that involves a lot of code. It is important to designthat code in an organized fashion from the start, rather than adding bits and pieces overtime. When building a house, an architect creates blueprints for the whole house, whichthe contractors then build. However, many programmers who are new to game pro-gramming build one part of the “house” from a tutorial, and add each “room” pieceby piece as they learn. It’s no surprise when the end result is bad.
写一个好玩的游戏是一项牵扯到很多代码的大任务。非常有必要从一开始就对项目进行良好的,有组织的设计,而不是随着进度的进行而到处杂乱添加代码。就像建造房屋一样,建筑师为整幢房屋勾画蓝图,建筑工人以此来建造。但是,许多对游戏编程不熟悉的编程人员会从根据导读建造出房屋的一部分,并随着学习的进行为其添加房间,这无疑将会导致不好的结果。*
Figure 2-1 shows the structure of a game engine that works well for most games. Byunderstanding all of the parts of a game engine and how they work together, we candesign the whole game at the start, and build our application the “Right Way.” In thefollowing sections, we’ll cover each part of Figure 2-1:

  • Application framework
  • Game state manager
  • Graphics engine

图2-1显示了一个适用于大部分游戏的游戏引擎结构。为了理解一个游戏引擎的所有部分和它们是如何工作在一起的,我们可以先为整个游戏做设计,然后再创建我们的应用程序。在以下的几个小节中,我们的讲解内容将会涵盖图2-1的每个部分。

  • 应用程序框架
  • 游戏状态管理器
  • 图像引擎

2.1 Application Framework 应用框架

The application framework consists of code necessary to get an application started,including creating an instance of the application and initializing the rest of the subsys-tems. Our code first creates a Framework class when our application starts and, in turn,will be in charge of creating and destroying the state machine, graphics engine, and audio engine. If our game is complex enough to require a physics engine, the frameworkwill manage that as well.
应用程序框架包含使应用程序工作的必须代码,包括创建一个应用程序实例和初期化其他子系统。当应用程序运行时,会首先创建一个框架类,并接管创建和销毁状态机、图像引擎和声音引擎。如果我们的游戏足够复杂以至于它需要一个物理引擎,框架也会管理它。
The framework should take care of the peculiarities of the platform we are working on,including handling any system events (such as Shut Down or Sleep), and managing theloading and unloading of resources so that the rest of the code can focus only on the game.
框架必须适应于我们所选择的平台的独特性,包括相应任何的系统事件(如关机与睡眠),以及管理载入与载出资源以使其他的代码只需要集中与游戏。

Main Loop 主循环

The framework will provide the main loop that is the driving force behind any inter-active program; on each iteration of the loop, the program will check for and handle incoming events, run updates on the game logic, and draw to the screen as necessary (see Figure 2-2).
框架会提供主循环,它是一切互动程序后的驱动力量。在循环中的每一次迭代过程中,程序会检查和处理接受到的事件,运行游戏逻辑中的更新并在必要时将内容描画到屏幕上。(参见图2-2)

Figure 2-2. Main loop sequence图2-2 主循环序列
Exactly how the main loop is implemented depends on the system you are using. For a basic console application, it could be as simple as a while loop that calls each function:
主循环如何实现依赖于你使用的系统。对于一个基本的控制台程序,它可能是一个简单的while循环中调用各个函数:

while( !finished ) {
    handle_events();
    update();
    render();
    sleep(20);
}

Notice the sleep function here. It puts the code to sleep for some small period each loop so that it does not form a solid loop that would constantly suck up all of the time on the CPU.
Some systems don’t want user code to write loops like that at all; instead, they use a callback system that forces the programmer to release the CPU regularly. This way, when the application starts, the programmer registers some functions to be called back during each loop:
注意到这里的sleep函数。它使得代码休眠一小段时间不致于占用全部的CPU。
有些系统完全不想让用户代码那些写,它们使用了回调系统以强制程序员常规的释放CPU。这样,当应用程序执行后,程序员注册一些函数给系统在每次循环中回调:

void main(void) {
    OS_register_event_handler( myEventHandler );
    OS_register_update_function( myUpdate );
    OS_register_render_function( myRender );
}

Once the program starts, those functions are called periodically, as needed. The iPhone is closest to this last example, which you will see in the next chapter and in the examples provided with the iPhone SDK.
一旦程序执行后,根据必要情况,那些函数会间隔性的被调用。IPHONE是最接近后面这个例子。你可以在下一章和IPHONE SDK中看到它。

Legacy C and C++ Code
If you have a C/C++ code base you want to port to the iPhone, or want to keep the option of reusable code from an iPhone application on other platforms such as Win-dows Mobile or Android, Objective-C presents a challenge to porting. The good news is that you can write C++ code that runs directly on the iPhone. However, you will have to write Objective-C to use certain portions of the iPhone SDK. The framework is the perfect place to wrap those interfaces in C++ classes so that the rest of your code can be written in pure C++.
If you or your company is dedicated to launching cross-platform titles, we strongly suggest developing engines with similar features and interfaces on each of the platforms.
After you isolate your game-specific code this way, it is an easy task to copy code from one project to another and make minor adjustments to get it up and running. With a little care, this technique even works when switching between C++ and Java!

2.2 Game State Manager游戏状态管理器

A serious video game offers more than just a theater of action holding the game: it has a Main menu that allows the player to set options and start a new game or continue a previous one; a Credits screen that shows the names of all the hardworking people who helped make the game; and if your game doesn’t come with a user manual, perhaps a Help section that gives the player a clue about what he’s supposed to be doing.
一个好的视频游戏不仅有一组动作来维持游戏:它会提供一个主菜单允许玩家来设定选项和开始一个新游戏或者继续上次的游戏;制作群屏将会显示所有辛勤制作这款游戏的人员的名字;而且如果你的游戏没有用户指南,应该一个帮助区域会给用户一些提示告诉他们应该做什么。
Each of these is a game state and represents a separate part of the application code. For instance, the functions and navigation invoked by a player in the Main menu are quite different from those invoked by a player in the Credits screen, so the program logic is a lot different, too. Specifically, in the Main menu, you will likely be drawing a title image and some kind of menu, and listening for player input to select one of the menu options. When you are in the Credits screen, you will be drawing the names of all the people who worked on the game, while listening for player input that will cause your current game state to change from the Credits screen back to the Main menu. And finally, in the Game Play state, you will be rendering the actual game and listening for the player’s input to interact with the game logic.
以上任何一种场合都是一种游戏状态,并且代表中一段独立的应用程序代码片段。例如,用户在主菜单调用的函数与导航与用户在制作群屏调用的是完全不同的,所以程序逻辑也是不同的。特别的是,在主菜单,你可能会放一张图片和一些菜单,并且等待用户选择哪个选项,而在制作群屏,你将会把游戏制作人员的名字描绘在屏幕上,并且等待用户输入,将游戏状态从制作群屏改为主菜单。最后,在游戏中状态,将会渲染实际的游戏并等待用户的输入以与游戏逻辑进行交互。
Each of these game states is responsible for handling player input, rendering to the screen, and providing any application logic that is specific to that state. You might recognize these tasks from our earlier discussion about the main loop, and that’s because they are exactly the same tasks. However, each state implements them in its own way, which is why we have to keep them separate. You don’t want to have to search through the Main menu code to make a change to the Game Play event handler.
以上的所有游戏状态都负责相应用户输入、将内容渲染到屏幕、并为该游戏状态提供相对应的应用程序逻辑的任务。你可能注意到了这些任务都来自于之前讨论的主循环中,这是因为它们就是同样的任务。但是,每个状态都会以它们自己的方式来实现这些任务,这也就是为什么要保持他们独立。你不必在主菜单代码中寻找处理游戏中的事件的代码。

State Machine状态机

The Game State Manager is a state machine, which means it keeps track of the current game state. When the application starts, the state machine creates the basic state information. It goes on to create information required by each state and to destroy temporary information when the application leaves a state.
状态管理器是一个状态机,这意味着它跟踪着现在的游戏状态。当应用程序执行后,状态机会创建基本的状态信息。它接着创建各种状态需要的信息,并在离开每种状态时销毁暂时存储的信息。
A large number of different objects have state that is maintained by the state machine. One obvious state is the screen the player is on (Main menu, Game Theater, etc.). But if you have an enemy artificial intelligence (AI) agent on the screen that can be in a “sleeping,” “attacking,” or “dead” state, a state machine can be used to keep track of those states as well.
状态机维护着大量不同对象的状态。一个明显的状态是用户所在屏幕的状态(主菜单、游戏中等)。但是如果你有一个有着人工智能的对象在屏幕上时,状态机也可以用来管理它的“睡眠”、“攻击”、“死亡”状态。
What is the right architecture for a Game State Manager? Let’s take a look at some state machines and decide which design pattern best fits our needs.
什么是正确的游戏状态管理器结构?让我们看看一些状态机并决定哪种最适合我们。
There are many ways to implement a state machine, the most basic of which is a simple switch statement:
有许多实现状态机的方式,最基本的是一个简单的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;
        }
    }
};

All that is necessary to switch states is to change the value of the myState variable and return to the start of the loop. However, as you can see, the more states we add, the larger that code block gets. Furthermore, we typically have entire blocks of tasks we need to perform predictably when entering or leaving a state: initialize state-specific variables, load new resources (such as images), and deallocate resources used by the previous state. In a simple switch statement, we’d have to add that block to each case and make sure not to forget a step.
改变状态时所有需要做的事情就是改变myState变量的值并返回到循环的开始处。但是,正如你看到的,当我们加入越来越多的状态时,代码块会变得越来越大。而且更糟的是,为了使程序按我们预期的执行,我们需要在程序进入或离开某个状态时执行整个任务块,初始化该状态特定的变量,载入新的资源(比如图片)和释放前一个状态载入的资源。在这个简单的switch语句中,我们需要加入更多的程序块并保证不会漏掉任何一个。
This is fine for simple tasks, but something as complex as our Game State Manager needs a better solution. The next best way to implement a state machine is to use function pointers:
以上是一些简单重复的劳动,但是我们的状态管理器需要更好的解决方案。下面一种更好的实现方式是使用函数指针:

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
    }
};

Now the main loop is very small and simple, even if we handle many game states. However, this solution still does not help us initialize and deallocate states. Because each game state has not only code but also unique resources, it is appropriate to think of game states as attributes of an object. So, next we will look at an object-oriented programming (OOP) approach.
现在,即使我们处理再多状态,主循环也足够小而且简单。但是,这种解决方案依然不能帮助我们很好的解决初始化与释放状态。因为每种游戏状态不仅包含代码,还有各自的资源,所以更恰当的做法是将游戏状态作为对象的属性来考虑。因此,接下来,我们将会看看面向对象(OOP)的实现。
We start by creating a class to represent our game states:
我们首先创建一个表示游戏状态的类:

class GameState
{
    GameState();        //constructor
    virtual ~GameState();    //destructor
    virtual void Handle_Event();
    virtual void Update();
    virtual void Render();
};

Next, we change our state manager to use that class:
接着,我们改变我们的状态管理器以使用这个类:

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;
    }
};

Finally, we create a specific instance of our game state:
最后,我们创建一个指定具体游戏状态的类:

class State_MainMenu : public GameState
{
    int m_currMenuOption;
    State_MainMenu();
    ~State_MainMenu();
    void Handle_Event();
    void Update();
    void Render();
};

When it is represented by a class, each game state can store its unique variables inside that class. It can also allocate any resources it needs in its constructor and deallocate them in its destructor.
当游戏状态以类来表示时,每个游戏状态都可以存储它特有的变量在该类中。该类也可以它的构造函数中载入任何资源并在析构函数中释放这些资源。
Furthermore, this system keeps our code nicely organized because we have to put the code for each state in separate files. If you are looking for the Main menu code, all you have to do is open the State_MainMenu class and there it is. And the OOP solution makes this code easy to reuse.
而且,这个系统保持着我们的代码有很好的组织结构,因为我们需要将游戏状态代码分别放在各个文件中。如果你在查找主菜单代码,你只需要打开State_MainMenu类。而且OOP解决方案使得代码更容易重用。
This seems to best fit our needs, so we will use the OOP solution for our Game State Manager.
这个看起来是最适合我们需要的,所以我们决定使用它来作为我们的状态管理器。

The Next Level: Concurrent Access
Another, more complicated, approach to the Game State Manager would be a kernel or scheduler. Very complex game engines, such as the ones found in current-generation PC and console platforms, use these to organize multiple tasks that run simultaneously, such as disk access, physics, and graphics routines.
Concurrent processes take advantage of the delays in completing tasks on each CPU,so while one portion of the game is waiting on something such as hard drive access and another part is waiting on the graphics card, you can still use the CPU to calculate physics simulations. This idea is also well suited for hardware with multiple CPU cores.
However, concurrent access isn’t very useful for games on the iPhone. First of all, most game designs are limited by the graphics and input provided by the platform, and therefore they demand far fewer resources and calculations than console games. Fur-thermore, there is less to be gained from multithreaded processes on the iPhone because filesystem access is much faster due to the hard drive being much smaller in capacity and responding to only one application at a time.
But the iPhone is still a new platform, and developers have yet to tap into its capabilities to the fullest. Just keep in mind that game code should solve the problem put forth by game design. Don’t get carried away if it doesn’t help you implement the game.

创建于2014-04-01深圳,更新于2016-07-02杭州

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值