The Game Loop

来源:互联网 发布:天谕最萌萝莉捏脸数据 编辑:程序博客网 时间:2024/05/06 20:57

The Game Loop

Game loop是一种循环系统,不断调用刷新场景中的objects并绘制到显示器上。通常情况下,在程序启动后不久就初始化并执行game loop,而且一直运行到程序结束。有各种各样的game loop设计,图10.8描述了一种最基础的设计。

图10.8 A basic game loop.
从代码的角度看,这种game loop设计与列表10.2的相同
列表10.2 A Basic Game Loop in C++
void Game::Run(){Initialize();while (isRunning){mGameClock.UpdateGameTime(mGameTime);Update(mGameTime);Draw(mGameTime);}Shutdown();}void Game::Exit(){isRunning = false;}

其中,Game::Initialize()函数表示window和Direct3D的initialization(实际代码上分成两个函数),与具体game的初始化步骤一样执行。Game::Update()函数执行所有与渲染无关的操作。例如,Update()函数可能会处理键盘和鼠标的输入,旋转一个objcet或者更新object的坐标位置。相反,Draw()函数处理所有与图形渲染相关的指令。这两个方法用于场景中所有objects的updating和drawing。

Time

代码中调用了UpdateGameTime()函数,以及对应的成员变量mGameTime,该变量作为调用Update()和Draw()函数的参数。这些变量和函数都与time相关,在游戏和图形编程中是非常重要的。与时间相关的最值得关注的两个信息是:时钟开始时间和上一次game loop迭代持续的时间。后一个时间通常用于游戏的frame rate。Game loop迭代一次称为一帧(frame),frame rate表示一秒内执行的帧的数量。
在GameClock和GameTime类中封装了elapsed-time数据,以及对应的函数。图10.9显示了这两种类型的类结构图。列表10.3列出了类GameClock的头文件。

Figure 10.9 Class diagrams for the GameClock and GameTime classes.
Listing 10.3 The GameClock.h Header File
#pragma once#include <windows.h>#include <exception>namespace Library{    class GameTime;    class GameClock    {    public:        GameClock();        const LARGE_INTEGER& StartTime() const;        const LARGE_INTEGER& CurrentTime() const;        const LARGE_INTEGER& LastTime() const;        void Reset();        double GetFrequency() const;        void GetTime(LARGE_INTEGER& time) const;        void UpdateGameTime(GameTime& gameTime);    private:        GameClock(const GameClock& rhs);        GameClock& operator=(const GameClock& rhs);        LARGE_INTEGER mStartTime;        LARGE_INTEGER mCurrentTime;        LARGE_INTEGER mLastTime;        double mFrequency;    };}

GameClock类封装了调用获取高精度定时器的Windows API,该定时器是通过获取CPU执行频率计算得到的。通过调用QueryPerformanceFrequency()函数确定frequency,该函数通过修改一个LARGE_INTEGER(一个64位的整形数,或者long long)的参数值,返回获取的结果。把获取到的frequency值存储到GameClock类成员变量中,用于后续的elapsed-time计算。
通过调用QueryPerformanceCounter()函数,可以得到高精度定时器当前的状态。为了计算一次game loop迭代的持续时间(elapsed time),需要调用QueryPerformanceCounter()函数两次,一次是在循环的开始(在调用Update()函数之前),一次是在循环的结束(在调用Draw()函数之后)。使用下面的公式计算elapsed time(精确到秒):
timeelapsed = (stopTime − startTime) / frequency
要计算从程序启动到现在所经过的总的elapsed time,可以把每一帧的elapsed time相加或者保存最开始调用QueryPerformanceCounter()函数得到的结果,再使用上面的公式计算。列表10.4列出了GameClock类的实现代码。
列表10.4 The GameClock.cpp File
#include "GameClock.h"#include "GameTime.h"namespace Library{    GameClock::GameClock()        : mStartTime(), mCurrentTime(), mLastTime(), mFrequency()    {        mFrequency = GetFrequency();        Reset();    }    const LARGE_INTEGER& GameClock::StartTime() const    {        return mStartTime;    }    const LARGE_INTEGER& GameClock::CurrentTime() const    {        return mCurrentTime;    }    const LARGE_INTEGER& GameClock::LastTime() const    {        return mLastTime;    }    void GameClock::Reset()    {        GetTime(mStartTime);        mCurrentTime = mStartTime;        mLastTime = mCurrentTime;    }    double GameClock::GetFrequency() const    {        LARGE_INTEGER frequency;        if (QueryPerformanceFrequency(&frequency) == false)        {            throw std::exception("QueryPerformanceFrequency() failed.");        }        return (double)frequency.QuadPart;    }    void GameClock::GetTime(LARGE_INTEGER& time) const    {        QueryPerformanceCounter(&time);    }    void GameClock::UpdateGameTime(GameTime& gameTime)    {        GetTime(mCurrentTime);        gameTime.SetTotalGameTime((mCurrentTime.QuadPart - mStartTime.QuadPart) / mFrequency);        gameTime.SetElapsedGameTime((mCurrentTime.QuadPart - mLastTime.QuadPart) / mFrequency);        mLastTime = mCurrentTime;    }}


在GameClock类的实现代码中,成员变量mStartTime存储了用于计算总的elapsed time的计数值,该变量在Reset()函数中进行赋值。成员变量mCurrentTime和mLastTime用于存储再次调用QueryPerformanceCounter()的结果,并用于计算第一帧的elapsed time。UpdateGameTime()函数封装了elapsed-time的计算,而不是直接把这些计算代码放在Game::Run()函数中。Frequency是在GameClock初始化时获取的。GameTime类没有什么特别的函数;只是简单地提供了访问和修改成员变量mElapsedGameTime和mTotalElapsedGameTime的函数接口。所有源码都可以在本书的配套网站上找到。

Game Loop Initialization

Game类是渲染引擎的核心,并且封装了game loop。Game loop的初始化阶段,执行调用三个虚拟函数:InitializeWindow(), InitializeDirectX(), and Inititialize();这三个函数分别执行以下操作,为应用程序创建一个用于绘制的窗口,设置并初始化DirectX,执行通用组件的初始化(很快就会讨论到)。Game类设计成用于被其他类继承的基类(尽管正式地说,该类不是一个抽象基类,因为包含了一个完整的实现定义),因此所有的虚函数都可以由派生类自己实现。但是基类Game还是为每一个虚函数提供了通用的实现方法。

0 0