使用IrrLicht引擎开发2D复古风格游戏

游戏开发领域,一个普遍的共识是:游戏的核心在于玩法本身,而非其外观或声音效果。作为一名Introversion Software游戏的忠实粉丝,不禁思考,为何不将这种复古风格融入到即将开发的游戏之中呢?正如James Curran所言,Michael Birken的文章已经全面覆盖了游戏的各个方面,因此将更多地关注于构建一个非游戏机平台的2D游戏,并使其看起来像游戏机游戏的不同之处。

面临的第一个问题是,应该使用哪种技术来开发游戏:DirectX还是OpenGL?第二个问题更为重要:由于这是第一个图形项目,能否及时掌握所选技术并编写所需的所有代码?在网络搜索了一段时间后,偶然发现了IrrLicht这个网站。这是一个免费的图形引擎,支持DirectX和OpenGL,这也解决了第一个问题。这个引擎提供了2D和3D模式,非常完美。可以从2D开始,然后转向3D,而无需更换引擎。

非游戏机平台的输入处理

当不使用游戏机时,处理输入的方式是最大的不同之一。游戏机程序是一个输入驱动的程序。在分析输入之后,执行所需的逻辑,然后重新绘制屏幕。而Windows(非游戏机)程序是一个事件驱动的程序。这意味着需要分析事件并创建自己的输入来驱动游戏逻辑。而且,因为它是一个窗口,需要自己绘制屏幕,并在等待输入的同时不断更新屏幕。

代码使用前的准备

如果想要使用并编译此代码,还需要IrrLicht SDK,可以。它包含了开始所需的一切,包括已经构建好的DLL和lib文件。将SDK放置在磁盘上后,需要将包含文件和库文件的位置添加到Visual Studio中。打开“工具”菜单下的“选项”菜单。选择“项目和解决方案”选项以及子选项“VC++目录”。

代码使用

现在可以准备使用并编译此代码了。从控制台项目模板开始,这有一个优势,即控制台将被用作输出和跟踪窗口,这使得调试引擎更加容易。在主函数中,创建了一个Game对象。这个对象包含了设置、运行和结束游戏的功能。

C++ int _tmain( int argc, _TCHAR* argv[]) { Game* pTheGame = Game::getInstance(); if (pTheGame) { pTheGame-> setupGame(); pTheGame-> createData(); pTheGame-> runGame(); pTheGame-> endGame(); } return 0 ; }

setupGame()方法创建了游戏窗口和游戏行为。游戏行为将负责游戏逻辑。将游戏逻辑放在了一个Act对象中。这个Act对象被交给了一个Director对象。而这个Director对象将允许在行为之间切换。游戏中有三个行为:IntroAct、PlayAct和CreditsAct。Intro将绘制一个《星际迷航》的标志,并展示游戏的目标。Play将包含实际的游戏,而Credits将在游戏结束时被调用,以向胜利的船长致敬,或者为有史以来最伟大的星舰的毁灭而哀悼。最后需要的一个对象是InputManager。这个对象将IrrLicht引擎返回的事件转换为希望被通知的输入。在情况下,这些将是KeyPress事件。

为了将KeyPress事件发送到行为,本可以使用一个简单的回调函数,但喜欢C#的委托概念。在C++中,这可以通过Functor来实现。boost库提供了几个类来做到这一点,但不想使用boost(至少不是在这篇文章中)。因此,为此任务创建了硬编码Functor。

C++ struct FKeyPressed { virtual ~FKeyPressed() {}; virtual bool operator ()(EKEY_CODE) = 0 ; }; template class KeyPressed : public FKeyPressed { public: typedef bool (ACTOR::*FunctionType)(EKEY_CODE); public: KeyPressed(ACTOR* pActor, FunctionType pFunctor) { m_pActor = pActor; m_pFunctor = pFunctor; } virtual ~KeyPressed() {}; virtual bool operator ()(EKEY_CODE keyCode) { return (m_pActor-> *m_pFunctor)(keyCode); } protected: ACTOR* m_pActor; FunctionType m_pFunctor; };

想要接收KeyPress事件的Act对象只需要提供一个具有以下签名的函数:

C++ bool OnKeyPressed(EKEY_CODE keyCode);

在runGame函数中,屏幕被渲染。对来说,这意味着将调用活动Act的draw函数。要么在这里绘制一切,将为IntroAct和CreditsAct这样做,因为它们并不多,或者加载一些IDrawable对象到Act中。

使用了两种类型的IDrawable,那些相对静态的和那些更动态的。一个静态可绘制的例子是ShipDisplay。这个类显示了屏幕右侧的船只和游戏状态。这些信息总是需要被绘制的。一个动态可绘制的例子是Torpedo,它将在屏幕上绘制一段时间,然后被移除。称之为Animator的这种类型实现了IAnimator接口,它扩展了IDrawable接口。一个例子是Phaser类。Animator提供了所有绘制和控制生命周期的代码。因此,Phaser只需要添加不同之处。

C++ void Phaser::updateInfo(Info& rInfo) { if (rInfo.Alpha >= 0 ) { rInfo.Alpha += rInfo.Fade; } if (rInfo.Alpha >= 255 ) { rInfo.Alpha = 255 ; rInfo.Fade = - rInfo.Fade; } }

当Phaser的生命周期结束时,调用endAnimator。在Phaser的情况下,目标船只被击中。

C++ void Phaser::endAnimator() { if (m_pVessel) { m_pVessel-> hitPhaser(m_iEnergy); } }

实现控制台界面是最困难的部分之一。Console类处理外观和输入。每当按下Enter或Escape键时,就会触发CommandManager。这个管理器存储了游戏输入的状态。

C++ enum Mode { WaitForCommand, NavigateWaitForCourse, NavigateWaitForDistance, LaunchWaitForEnergy, LaunchWaitForHit, LaunchWaitForCourse, LaunchWaitForCoordinates, TransferWaitForEnergy, ComputerWaitForCommand, WaitForAnimation };

CommandManager验证输入并采取适当的行动,例如,让Enterprise对象将能量转移到护盾上。第三个类是Controller,这个对象实际上创建了Torpedo或Phaser动画器,并将它们添加到PlayAct中。runEnemyAI函数检查是否有运行的需要,以及是否有敌舰在该扇区。有三个船只:Enterprise、KlingonShip和StarBase,它们都实现了IVessel接口。

感兴趣的点

首先,想为使用像IrrLicht这样的伟大库来绘制一个简单的控制台外观而道歉。但是,将字符更改为Enterprise的2D图像应该相当容易。或者,可以走得更远,使其成为全3D。还应该很容易更改Controller和CommandManager类,使其变为实时而不是回合制。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485