我正在尝试将使用C ++编写的游戏设计为实体/组件系统(基本思想是,我将具有一组代表不同类型数据的“ Entity”类和一组“ Component”表示给定实体可能具有的各种行为的类)。我想使用组件的虚拟基类来实现它。例如,我可以有一个Character
实体:
class Character {
public:
int armor;
int health;
std::string name;
};
以及代表可以使用近战武器的东西的Fighter
组件:
class Fighter {
public:
int damage;
virtual Direction attack() = 0;
}
和一个Mage
组件,代表可以施放法术的东西
class Mage {
public:
std::vector<Spell> spellList;
virtual Spell cast() = 0;
}
使用多重继承来使用这些继承
class ElvenMage : Character, Mage {
public:
ElvenMage() {
this->armor = 0;
this->health = 10;
this->name = "elven mage";
this->spellList = ...;
}
virtual Spell cast() {
// some AI logic to figure out which spell to cast
}
};
和
class Player : Character, Fighter, Mage {
public:
Player() {
this->armor = 5;
this->health = 20;
this->name = "Player";
}
virtual Direction attack() {
// some logic to handle player melee input
}
virtual Spell cast() {
// some logic to handle player spell casting input
}
};
要跟踪地图上的所有当前字符,我可以做类似的事情
class Engine {
public:
std::vector<std::unique_ptr<Character>> characters;
我必须将它们直接存储为Characters
(而不是Fighters
或Mages
),但是我需要分别处理战士和法师(例如,遍历所有{{ 1}},如果他们可以施放咒语,则必须这么做,否则他们将使用近战武器进行攻击。在实际的游戏逻辑中,如何区分也实现Characters
的{{1}}实例和也实现Character
的{{1}}实例?
是否有一种简单的方法可以做我想做的事情,还是应该完全重新考虑我的设计?
(注意:这不是实际的代码;在现实世界中,我实际上是使用工厂或某种东西来创建精灵或其他东西,而不是尝试将所有信息都放入构造函数中,并且我可能会将逻辑不同,但这说明了我遇到的问题)。
答案 0 :(得分:0)
您可以在引擎中使用visitor pattern:
每个字符都将覆盖Character
类的纯虚拟accept方法。
class Engine;
class Spell {};
class Direction {};
class Character {
public:
virtual void accept(Engine& engine) = 0;
};
class Fighter {
public:
virtual Direction attack() = 0;
};
class Mage {
public:
virtual Spell cast() = 0;
};
在Character
和Mage
中继承Fighter
将包括更少的样板代码。如果要坚持继承形式,则需要在每个字符中提供accept
方法的重载,该方法仅调用visit
的{{1}}函数:
Engine
神奇的地方是class ElvenMage : public Character, Mage {
public:
ElvenMage() {}
virtual Spell cast() {
std::cout << "Casting from ElvenMage"
<< "\n";
return Spell{};
}
void accept(Engine& visitor) override { visitor.visit(*this); }
};
class Player : public Character, Fighter, Mage {
public:
Player() {}
virtual Direction attack() {
std::cout << "Attacking from Player"
<< "\n";
return Direction{};
}
virtual Spell cast() {
std::cout << "Casting from Player"
<< "\n";
return Spell{};
}
void accept(Engine& visitor) override {
// cast to Player if it should attack instead of casting
visitor.visit(static_cast<Mage&>(*this));
}
};
方法:
visit
请注意,我在上面的代码中有一个循环依赖关系,在拆分声明和定义时应该解决该依赖关系。