使用 C++ 设计游戏引擎框架
这篇文章描述了我使用 C++ 创建通用游戏引擎框架的工作。
引言
我承担了撰写一篇关于设计游戏引擎的白皮书的任务。为了完成这项工作,我开始使用 C++ 实现一个框架,其中涉及设计模式和 C++ 概念的基本实现,例如基于策略的设计。本文介绍了我的设计,并包含一个编译过的代码片段。
背景
这描述了一个使用著名的设计范式,例如基于策略的设计、装饰器和策略模式,并使用 C++ 实现的框架。
Using the Code
使用基于策略的设计进行设置
在深入研究足球游戏引擎设计的细节之前,任何游戏在启动时都必须允许用户选择难度级别。我假设默认有三个难度级别:低、中和高。由于必须在开始时选择级别,这给了我一个机会使用基于模板类的基于策略的设计(基于 Andrei Alexandrescu 的著作,Modern C++ Design)。因此,难度级别可以是一个策略。此外,我将为游戏模式添加另一个策略。游戏模式可以是自动(在这种情况下,您将与机器对战)或多(您可以与您的朋友一起玩)。
基于策略的设计被描述为策略模式的编译时变体
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;
};
接下来重要的是,一个团队应该能够在运行时更改游戏的逻辑和策略。例如,用户可以选择防御而不是攻击。这可以使用策略模式来实现。我们可以列出防御、攻击等的算法列表,以便用户能够在运行时选择团队策略。这可以通过使用策略模式来实现。在这里,我定义了三种策略,并且可以通过 GameLogic
类来设置这些策略。
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;
};
然后是每个实体可以执行的不同角色。每个团队都有一份球员、教练、理疗师、经理、裁判和团队首席执行官等的名单。团队中的每个人可以执行一个或多个角色,并且可以根据信誉和其他参数在运行时分配角色。此外,作为球员的每个人都可以有不同的职责,例如前锋、后卫、中场和守门员等。这也应该在运行时完成,而无需使用子类化。
角色和职责都使用装饰器模式在运行时分配。
以下是用于在运行时获取游戏实体(一个人)的角色和职责的辅助函数
//------------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));
}
以下是创建游戏实体并在运行时分配角色和职责并使用上述辅助函数检索这些对象的代码片段(有关完整的实现,请参阅附带的 CPP 文件)
// 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));
此外,不同的团队可以在不同的场地、不同的设置中进行不同的联赛比赛。例如,足球游戏中的每个实体都可以组合成一个团队,然后可以应用不同的演示设置。您可以将球员添加到特定的足球俱乐部(球衣颜色),并为联赛比赛(如英超联赛等)添加不同的球队。在这里,每个实体(团队/联赛比赛)的实际抽象可以与实现(联赛/足球场/演示设置等)解耦,使用桥接模式。因此,实现和抽象可以独立变化。或者,也可以使用构建器模式来完成相同的任务。
整个设计都在 Visual Studio 2005 (VC8.0) 编译器中实现/编译和测试。附上了已实现的 CPP 文件 FootballEngine.cpp 和 stdafx.h 。
关注点
未来新增内容
我曾想过使用观察者模式来通知玩家(游戏实体)关于策略、球的位置和其他对手的坐标。我将在后期添加它。
历史
N/A