【发布时间】:2019-06-19 05:45:20
【问题描述】:
我正在尝试将我用 C++ 编写的游戏构建为实体/组件系统(基本想法是我将拥有一组代表不同类型数据的“实体”类和一组“组件”表示给定实体可能具有的各种行为的类)。我想使用组件的虚拟基类来实现它。例如,我可以有一个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),但我需要分别处理战士和法师(例如,循环遍历所有Characters 以及他们是否可以施放他们这样做的咒语,否则他们会用近战武器攻击)。在实际的游戏逻辑中,如何区分同样实现了Mage 的Character 实例与同样实现Fighter 的Character 实例?
有没有一种简单的方法来做我想做的事情,或者我应该完全重新考虑我的设计?
(注意:这不是实际代码;在现实世界中,我实际上会使用工厂或其他东西来创建精灵或其他东西,而不是尝试将所有信息放入构造函数中,并且我可能会将逻辑不同,但它说明了我遇到的问题)。
【问题讨论】:
-
ECS 是字面上没有继承,如果这是您的目标,请再次阅读 ECS。游戏引擎通常需要检查对象的类型,因此您将在当前设计中拥有类似
dynamic_cast<Mage*>(&someCharachter)的内容,以及使Character具有多态性。 -
@PasserBy 我知道它应该避免复杂的继承层次结构,但“所有
Characters 可以Mages 可以cast”的概念基本上与java 的interface或c++ 的概念相同虚拟类范式。另外我知道我必须进行动态转换,我的问题是我怎么知道刚刚给定一个Character实例它实现了哪些组件才能知道要转换什么 -
@Raghav Malik 在这种特定情况下,它们非常相似。问题不在于对一个对象调用一种方法,而在于如何处理数千个对象,每个对象可能包含数百个组件。在这种情况下,它们是非常不同的。实现组件系统的最简单方法是创建一个
GameObject类,并为它提供一个指向每个可能的组件类型的指针。如果您想要所有法师,只需遍历所有对象并获取那些具有指向MageComponent的非空指针的对象
标签: c++ oop design-patterns entity