By nalla
翻译:Lymons
本文使用C++来描述我的作品---给范型游戏引擎创建一个框架
· Download design strategy - 122 KB
· Download sample game engine - 129 KB
简介
我拿到了一个任务,就是写一篇关于游戏引擎设计的报告。为此,我开始用C++来实现一个框架,它包含了一些设计模式(Design Patterns)的基本实现以及类似于基于原则设计(Policy based design)的一些C++概念。而本文就是谈论我的设计,并且里面也包括一些可编译的代码片断。
背景
在本文描述的框架中使用了一些著名的设计范式(design paradigms),如:基于原则的设计(Policy based design),装饰者(Decorator)和策略(Strategy)模式,以及相应的C++的代码实现。
代码的功能说明
基于原则的设计是用于游戏的设置
在进入到足球游戏引擎设计的细节之前,先讨论一下游戏中的设置。在任何游戏中都允许用户在游戏开始期间来选择游戏的难度。我假设这里有三种难度级别,即:低级,中级,高级。因为这些级别允许在开始的时候被选择,这就给了我们一个机会可以利用模板类来使用基于原则的设计(基于Andrei Alexandrescu的书《Modern C++ Design》)。因此,这些难度级别在这儿可以被当做一个原则(Policy)。而且我为设置游戏模式也新添加一个原则,这些游戏模式可以是自动模式(你可以和机器对战)或者多人模式(你可以和朋友一起玩)。
基于原则的设计可以被当作策略模式在编译期间的一个变体。
Collapse
template <typename T>
struct DifficultyLevel_High
{
static void setDifficultyLevel(T&) { cout << "Setting to High difficulty level" <<endl; }
};
template <typename T>
struct DifficultyLevel_Med
{
static void setDifficultyLevel(T&)
{ cout << "Setting to Medium difficulty level" <<endl; }
};
template <typename T>
struct DifficultyLevel_Low
{
static void setDifficultyLevel(T&)
{cout << "Setting to Low difficulty level" <<endl; }
};
template <typename T>
struct Playmode_policy_auto
{
static void setPlaymodepolicy(T&)
{ cout << "Setting to auto Playmode" <<endl; }
};
template <typename T>
struct Playmode_policy_multi
{
static void setPlaymodepolicy(T&)
{ cout << "Setting to multi Playmode" <<endl; }
};
class FootballEngineType
{
public:
FootballEngineType()
{ cout << "Engine set as Football " << endl;
}
};
//---------------------Usage of Policy based design----------------//
template< typename T,
template <typename> class DifficultyLevel_policy,
template <typename> class Playmode_policy >
class GamingEngine
{
public:
void Run()
{
DifficultyLevel_policy<T>::setDifficultyLevel(engineType);
Playmode_policy<T> ::setPlaymodepolicy(engineType);
start();
}
private:
T engineType;
};
接下来要实现的事情是让球队在游戏的运行期内可以改变它的逻辑和策略。例如,用户能够选择防守策略而不是进攻, 使用策略模式使之成为可能。我们能够为防守或者进攻等的策略定制一套算法,以便用户能在运行期选择球队的策略。那我们在这里就使用策略模式 Strategy pattern,在这儿我定义了三种策略,以及能够通过 GameLogic 类来设置这些策略。
Collapse
class Strategy
{
public:
Strategy() {}
virtual void Apply()=0;
virtual ~Strategy() {}
};
class DefendStrategy : public Strategy{
public:
DefendStrategy():Strategy() { cout << "Defend strategy set" << endl; }
void Apply() { cout << "Defend strategy applied" << endl; }
virtual ~DefendStrategy() {}
};
class AttackStrategy: public Strategy
{
public:
AttackStrategy():Strategy() { cout << "Attack strategy set" << endl; }
void Apply() { cout << "Attack strategy applied" << endl; }
virtual ~AttackStrategy() {}
};
class MediumStrategy: public Strategy
{
public:
MediumStrategy() :Strategy(){ cout << "Medium strategy set" << endl; }
void Apply() { cout << "Medium strategy applied" << endl; }
virtual ~MediumStrategy() {}
};
class GameLogic
{
public:
StratType StrategyType;
GameLogic()
{
m_Strategy = NULL;
}
void SetStrategy(StratType type)
{
if (m_Strategy) delete m_Strategy;
if (type == Med)
m_Strategy = new MediumStrategy();
else if (type == Defend)
m_Strategy = new DefendStrategy();
else if (type == Attack)
m_Strategy = new AttackStrategy();
}
void Exec() { m_Strategy->Apply(); }
~GameLogic() { if (m_Strategy) delete m_Strategy; }
private:
Strategy *m_Strategy;
};
然后考虑的是每个实体能够执行的不同的角色。 每个球队都有很多的球员,教练,体能教练,经理,还有裁判和球队的CEO等。在球队中的每个人都能执行一个或者多个角色,并且这些角色能够通过一些参数在运行期间被分配出来,另外,作为一个球员本身也有不同的职责,像前锋,后卫,中场,和守门员等。不通过子类化(sub classing)这些都应该能够被做出来。
我们使用装饰者模式(Decorator pattern)在运行期来分配角色和职责。
下面是辅助函数(helper functions)的代码,被用来获取游戏实体在运行期内的角色和职责:
Collapse
//------------templated helper functions for getting roles-------//
template <class T>
T* getresponsibility_entity(GameEntity *pEnt)
{
return dynamic_cast<T*>(pEnt->GetResponsibility(T::RESP_CLSID));
}
template <class T>
T* getroles_entitiy(GameEntity *pEnt)
{
return dynamic_cast<T*>(pEnt->GetRole(T::ROL_CLSID));
}
下面的代码片断是在运行期创建一个游戏实体并给它分配角色和职责,以及使用上面做成的helper functions接收这些对象 (关于完全的实现,请参考附件中的CPP文件):
Collapse
// Add a single player
GameEntity* play1 = new GameEntity("Beckham", "David");
//Adding role as Player
play1->AddRole(new Player(play1));
Player *playRole = getroles_entitiy<Player>(play1);
//Adding Responsibilities to play and manage
play1->AddResponsibilities(new ToPlay(play1));
play1->AddResponsibilities(new ToManage(play1));
还有,不同的队伍能够使用不同的设置在不同的球场上踢不同联赛的比赛。举例子来讲,在足球游戏中的每一个实体能够凑在一起形成一个球队,并且还能够应用不同的外观设置。你能够添加一些球员到一家指定的俱乐部中以及添加一些不同的球队到不同的联赛中(如,英超联赛)。这里,使用桥接模式(Bridge pattern)就可以把每个实体的抽象(球队/联赛的比赛)能够从具体的实现(联赛/足球场/外观设置等)中解耦出来。因此,实现和抽象之间能够非常独立的相互存在。或者说,使用建造者模式(Builder Pattern)也用来完成同样的任务。
整个设计已经被实现了并使用Visual Studio 2005 (VC8.0)编译器编译通过了,并且测试过了。一些具体的实现请参考附件中的FootballEngine.cpp 。
兴趣点
附加特征
近期我也正在思考如何使用观察者模式,当策略和足球的位置以及对手的坐标被改变时来及时通知球员(游戏实体)。