平台独立开发示例






1.90/5 (13投票s)
一个平台无关的游戏模型。
引言
在过去的一个月里,我读了很多关于编写平台无关应用程序的讨论。这些讨论让很多编程初学者不确定该使用哪个库,或者哪个语言才是正确的选择。我甚至读到关于C++重要性下降的争论,因为对平台无关开发的需求越来越大。在本文中,我想通过一个小的C++游戏示例来展示,你不需要拥有在特定库方面的丰富知识来移植你的应用程序。更重要的是开发对平台相关任务的良好抽象。
背景
你应该做的第一步是包装,包装,再包装。因此,你应该首先确定代码中需要使用平台相关功能的部分。在我的游戏应用程序中,我非常惊讶地发现这种功能并不多。首先,让我们来看看应用程序的主要部分
- 用户交互
- 绘图
- 游戏逻辑
唯一真正依赖于平台的部分是第2点。第1点和第3点可以很容易地使用C++和STL来完成。真的就这些吗?不,你还需要找到一种在某种消息循环中运行游戏循环的方法,该消息循环位于你的应用程序窗口内。所以主要的任务是创建一个包装器,它能够进行绘制并执行应用程序的主循环。我决定编写一个抽象类作为所有所需功能的接口。让我们看看它的定义
class GdiWrapper { public: GdiWrapper(){}; //making the game known to the Wraper for callback void SetGameNotifier( GameNotifier* pTheGame ){ m_pTheGame = pTheGame; } virtual ~GdiWrapper(){};//all deinitialisation //asking the user for Input virtual bool GetUserInput( char* pstrStringOne, char* pstrStringTwo ) = 0; //outputs a Mesage on the Screen virtual void MessageOutput( char* pstrMessage ) = 0; virtual void OutputGameInfo( char* pstrText ) = 0;//outputs an Info Text virtual int DrawLine( int x1, int y1, int x2, int y2 ) = 0;//Draws a Line //Draws a Scale on the Playfield virtual int DrawScale( int x1, int y1, int x2, int y2 ) = 0; //Draws the Playstone Circles virtual void DrawCircle( int x, int y, int Radiant, bool bFilled, unsigned long dwColor ) = 0; //Draws something at the End of a Playfield - Row virtual void DrawEndLineText( int x, int y, int iCoordinate ) = 0; //Shows the Visualisation Window and inits the Main - Loop virtual void RunMainLoop(void) = 0; //returns if the Apps Message-loop is still running virtual bool LoopRunning(void) = 0; protected: GameNotifier* m_pTheGame; };
这些函数是你可视化游戏所需要的一切。这样你就不需要包装整个GUI功能,你只需要一些处理应用程序中所需任务的函数。为了移植应用程序,你所要做的就是继承自GdiWrapper
并实现所需的功能。如果你看看DrawEndLineText(...)
函数及其在不同GdiWrapper
移植中的使用,你会注意到,只有控制台实现才真正执行了在行末绘制坐标的功能。
使用代码
所有版本的代码都以非常相似的方式初始化
#include "Game.h" int main(int argc, char* argv[]) { GdiWrapperKonsole theGdi;//instance of the choosen wrapper Game theGame( PlaySize( 5,5 ), &theGdi );//initializing the game theGame.Run();//running the game return 0; }
在MFC版本中,初始化是在应用程序的InitInstance()
中完成的。你看到,为了移植所要做的唯一工作就是编写一个新的GdiWrapper
并使用它初始化游戏实例。
结论
编写平台无关代码并不总是需要用到哪些库的问题。开发一个可移植的应用程序可能需要多花一点时间,但之后移植应用程序应该会非常容易。你并不总是需要像QT这样大型工具包的完整功能,所以通常只要使用你真正需要的应用程序的一些功能就足够了。在开发过程中测试你的代码在不同的编译器上是永远不会错的(你会惊讶地看到我使用了多少次BOOL
, DWORD
和其他MS特定的定义)。这些是你需要注意的一些主要方面
- 尽量避免平台特定的定义(例如,MS特定的
DWORD
,BOOL
...)。 - 将你的应用程序数据与你的可视化分开(MVC模式,DOC/View...)。
- 使用不同的编译器,并在不同的系统上进行测试。
- 当你编写包装器时,总是将包装的任务分解为简单易懂的函数。
但是,我的代码肯定不是最优的 - 我认为OpenGL实现与SDL就说明了这一点,但这仅仅是因为在开发游戏类时,我从未计划编写这样的实现。对于编译QT、SDL和wxWindows示例时可能出现的问题,我深感抱歉,但我只在我的Linux上使用KDevelop 3.0进行了测试。MFC和控制台实现是使用VS6完成的。