C++游戏编程8步云
<英语原文:http://www.cplusplus.com/articles/1w6AC542/>
第一步 选择游戏库
要想编写一款游戏你必须选择一个游戏库,除非你要编写自己的游戏库。下面列出了一些游戏库的名称和网站,它们都提供了相同的底层功能。
● Simple Fast Multi-Media Library (SFML): http://www.sfml-dev.org/● Simple DirectMedia Layer (SDL): http://www.libsdl.org/
● Allegro: http://www.allegro.cc/
● OpenGL (GFX only, however, there are wrapper libs like AllegroGL): http://www.opengl.org/
● DirectX (Windows only): http://msdn.microsoft.com/en-us/directx/
● rrlicht (3d lib): http://irrlicht.sourceforge.net/
还有,一款好的游戏库一般具备以下特征:
● 一些专门加载和播放声音文件的系统。● 一些专门加载和显示图像的系统。
● 至少要有一些为基本图像提供的操作(如图像的旋转,图像的缩放等)
● 最基本的绘画功能(如画圆函数, 绘制点、线的函数, 绘制矩形的函数等)
● 文本显示功能
● 支持多线程
● 计时器功能
第二步 定义游戏规则
所有的游戏都从规则开始。首先,提出你对该游戏所持有的想法(规则)。一旦你有了一个想法, 你就可以扩展出更多的基于它而产生的想法。假如,你编的是一款棋类游戏,你就会想怎么才算赢局?又有什么玩法?等等。假如,你的游戏是一款讲述故事情节的游戏,只需建立一个一个故事情节即可。一定要确保你有一个好的可玩性高的游戏规则,只有这样你的游戏才算得上是一款真正的游戏。越复杂的游戏,你就越该好好的周祥一番,从打算一开始编写它时就定义好你的游戏规则,这样你在编写代码时就不必担心游戏本身所存在的问题了。在你编写游戏时,游戏的演变步骤一定要了然于胸。
第三步 划分好你的游戏引擎
你有必要划分出你的游戏引擎的各个功能组件,以及知道如何将各个功能组件组装在一起。由于各大小游戏的复杂性程度的不同,你也许不必关心这个步骤。但是,通过把引擎分成各个部分,可以很好的对各个部分进行测试研究,你只需要在它们被组装起来之前,确保它们能够完成它们相应的功能即可。 相反,如果不分成各个组成部分,而逐个小功能的不断补充,情况将变得很糟糕,因为你很难把握各个功能的耦合。因此,你应该开始设计类结构图了(就是所谓的面向对象程序设计(OOP))。虽然,已有各种各样的已编好的游戏引擎供你在工程中使用,但是你还是必须记着这重要的一步。
第四步 开始编写游戏引擎(如果你是自己写引擎的话)
现在,该是开始编写引擎的时候了。编写引擎并不意味着就是编写游戏本身,但是在一定程度上,渲染内核,物理模型,文件处理,还有各种函数和类都是构建游戏世界的组成部分。然而,由于游戏的复杂度,引擎和游戏代码也许是同一个概念。对一个更复杂的游戏,将很可能会调用一个叫资源管理员(resource manager)的类对象。资源管理员不需要你知道什么是声音文件,它只是管理你的资源文件(如,图像文件,音频文件等)。定义资源管理员类来管理所有资源不仅能使你的代码更加整洁,还能使你避免内存泄露。
来看以下两段优秀的代码。该代码能使整个引擎达到某种程度上的紧密性,以及它提供了易于使用的接口。以此来编写你的游戏,你就不用通过资源文件来找函数名,也不用知道它是什么。一种实现这种思想的简单方法就是用OOP思想实现的。
两个示例:
//碰撞检测管理,在游戏中碰撞检测算法频繁出现,可采用该类进行管理
class collisions {
bool check_col(obj1*, obj2*); //检测两个对象是否碰撞,obj通常都由一个类派生而来
void handle_col(obj1*, obj2*); //碰撞发生后的处理函数
public:
void handle_all(); //处理所有碰撞后的对象
}Collision;
//渲染事件管理,在游戏中要绘制各种游戏对象,可采用该类进行管理//
//http://blog.csdn.net//zhanxinhang
class rendering {
void bots(); //游戏对象bots
void bullets(); //游戏对象bullets
void players(); //游戏对象players
public:
void draw_all(); //绘制所有对象
}Renderer;
//所有对象的碰撞检测处理和对象的绘制都可以通过以下两个接口并放到游戏循环中
Renderer.draw_all();
Collision.handle_all();
定义资源管理员类(Resource Manager):
#include <map>
#include <string>
#include <exception>
typedef const std::string URI;
// 异常处理
namespace Exceptions {
// 如果资源路径不存在抛出此异常
class URINotFound : public std::runtime_error
{
public:
URINotFound(const std::string& Message = "The specified URI was not found in the resource index.")
: runtime_error(Message) { }
};
// 如果资源内存空间分配失败抛出此异常
class BadResourceAllocation : public std::runtime_error {
public:
BadResourceAllocation(const std::string& Message = "Failed to allocate memory for resource.")
: runtime_error(Message) {}
};
}// namespace Exceptions
//=http://blog.csdn.net//zhanxinhang
//资源管理类
template <class Resource> class ResourceManagerB {
typedef std::pair<URI, Resource*> ResourcePair;
typedef std::map<URI, Resource*> ResourceList;
// 资源‘菜单’
ResourceList Resources;
public:
~ResourceManagerB() { UnloadAll(); }
// 通过指定路径载入资源
URI& Load(URI& Uri);
// 通过指定路径卸载资源
void Unload(URI& Uri);
// 卸载所有资源
void UnloadAll();
// 得到资源的指针
Resource* GetPtr(URI& Uri);
// 得到资源的引用
Resource& Get(URI& Uri);
};
//=====================================
//** 以下为资源管理类的接口实现代码 //
//=====================================
template <class Resource>
URI& ResourceManagerB<Resource>::Load(URI& Uri)
{
// 检测资源是否已载入,否则运行里面的判断体
if (Resources.find(Uri) == Resources.end())
{
//分配内存
Resource* temp = new (std::nothrow) Resource(Uri);
//如果分配失败抛出异常
if (!temp)
throw Exceptions::BadResourceAllocation();
//插入该资源到菜单
Resources.insert(ResourcePair(Uri, temp));
}
return Uri;
}
template <class Resource>
void ResourceManagerB<Resource>::Unload(URI& Uri)
{
//找到该资源在菜单中的位置
ResourceList::const_iterator itr = Resources.find(Uri);
// 如果找到
if (itr != Resources.end())
{
//释放资源
delete itr->second;
// 从菜单中删除
Resources.erase(Uri);
}
}
template <class Resource>
void ResourceManagerB<Resource>::UnloadAll()
{
// 遍历每个资源
ResourceList::iterator itr;
for (itr = Resources.begin(); itr != Resources.end(); itr++)
// 释放资源
delete itr->second;
// 清除菜单
Resources.clear();
}
template <class Resource>
Resource* ResourceManagerB<Resource>::GetPtr(URI& Uri)
{
// 从菜单中找到指定元素
ResourceList::const_iterator itr;
// 如果找到
if ((itr = Resources.find(Uri)) != Resources.end())
// 返回该元素
return itr->second;
// 否则返回0
return 0;
}
template <class Resource>
Resource& ResourceManagerB<Resource>::Get(URI& Uri)
{
// 得到该资源
Resource* temp = GetPtr(Uri);
// 如果已得到该资源
if (temp)
//返回该资源
return *temp;
else
//否则抛出异常
throw Exceptions::URINotFound();
}
第五步 图像和声音
在游戏规则的基础之上,开始绘制各种图像以及游戏音效用来制造游戏氛围。如果你要更进一步的发展,你就需要创建更多的图像效果(GFX)和特效(SFX),并且很可能不会使用它们中的某些。而且,随着整个进程的发展,这一步可能还会继续下去。
第六步 编写你的游戏
一旦你有你有了你的游戏引擎,你就可以开始编写你的实际代码了。这步将关系到一切与游戏规则,故事情节等相关代码工作。还有,这步也关系到了游戏循环。游戏循环反复地更新一切游戏所需要的数据。可看如下代码示例。如果你的引擎能够正确运行,这将会变得比编写游戏引擎更加容易,而且更有趣! 这里也将很可能是你添加声音文件的地方。一旦这个步骤完成了,你该进行一项工作区完成游戏的一份复制。越想得到,你就越能得到!
Game Loop:
//你的游戏循环会因为各种不同的游戏而不同,尤其是对一些棋类游戏,以下只是一个简单的示例
while (!Game.lost()) //如果游戏失败,则退出循环
{
Game.handle_input(); //获得输入数据
AI.update_bots(); //关于bot的人工智能
Collision.handle_col(); //碰撞检测
Game.check_win(); //检查游戏输赢
Renderer.draw_all(); //绘制游戏画面
Game.sleep(); //为了不让游戏走得太快,延迟时间
}
第七步 代码优化
并不是因为你成功地实现了你的游戏,就意味着你就完成整个工作。除了在你的代码中添加详细的记录说明外,还很可能需要对你的代码进行一些优化处理。这步关系到了内存的使用(尽量不要使用全局变量,检查内存泄露等),还有程序执行效率(确保你的代码执行效率不要太慢,否则cpu很容易发生超载)。常规的程序调试也可以在这有效地组织起来。
第八步 打包发售
既然你的游戏已经制作完成,你就需要将它打包起来并依照你的意愿进行发售。至于如何打包,应该尽量使你的游戏文件有组织地进行存放并且将所有文件放在一个文件夹下以便打包,这会使你的游戏发售变得更加easy.^_^
………………………………………………………………………………………………………………………………………………………………………………
C++游戏编程8步云 完。 欢迎email:zhanxinhang@gmail.com ^_^
<转载不忘注明出处:http://blog.csdn.net/zhanxinhang,及作者:花心龟的扮演者ZhanHang>