我正在开发一个游戏环境,其中包含许多不同的实体。每个都有一些常见的功能(绘制,更新等),但有时游戏必须根据敌人的类型区别对待它们。到目前为止,我已经在他们的实例课程中编码了敌人的“类型”。所以,我们有这样的情况:
class MotionObject { ... };
class Entity : public MotionObject { ... };
class Coin : public Entity { ... };
class TextSign : public Entity { ... };
class ShapeEnemy : public Entity { ... };
class Attractor : public ShapeEnemy { ... };
class Bumper : public ShapeEnemy { ... };
所以类Coin,TextSign,Attractor和Bumper是游戏中实例化的实体类型。为不同类型的实体设置不同的类感觉是正确的,但是如果我只有一个实体类,其中包含一个基于其“实体类型”控制其行为的switch语句,我无法摆脱一些麻烦可能会被避免的感觉,存储在变量中的东西。玩家根据他们的类型以不同的方式与这些实体交互,每次我都使用dynamic_cast和null测试来确定应用了哪种行为。要清楚,这些是针对我无法在每个实体上调用简单Update()的行为;播放器将以特定方式响应,或者它们将是实体间交互,所有这些都基于实体类型。我的代码如下所示:
void Game::Update(float deltaT) {
for (int i =0; i < DrawChildren.size(); i++) {
//each entity has its Update called
DrawChildren[i].Update(deltaT);
//What follows is code that the Game class needs to run based on the entity type.
Coin * coin = dynamic_cast<Coin*>(DrawChildren[i]);
if (coin != nullptr) {
...
continue; //so no other type's code executes, in case I have inherited types.
}
TextSign * textSign = dynamic_cast<TextSign*>(DrawChildren[i]);
if (textSign != nullptr) {
...
continue; //so no other type's code executes, in case I have inherited types.
}
Attractor * attractor = dynamic_cast<Attractor*>(DrawChildren[i]);
if (attractor != nullptr) {
...
continue; //so no other type's code executes, in case I have inherited types.
}
Bumper * bumper = dynamic_cast<Bumper*>(DrawChildren[i]);
if (bumper != nullptr) {
...
continue; //so no other type's code executes, in case I have inherited types.
}
}
...
}
有没有那么麻烦的方法呢?
答案 0 :(得分:1)
另一种方法是使用“浅”类层次结构和独立的“行为”抽象类,您可以使用它们来装饰主层次结构。
所以你可以:
实体
- &GT;字符 - &gt; NPC - &gt; ....
- &GT;对象 - &gt;容器 - &gt; .... 渲染
可移动的
Killable
...可装备
您将使用多重继承来装饰类。
具有单个层次结构会强制您在不同的“子树”之间共享时将功能向上移动到树中,这意味着您可以向可能不使用它们的类添加功能,从而使整个设计复杂化;否则,你最终不得不重复代码(DRY原则?)。在这两种情况下,维护和扩展都变得更加困难。
答案 1 :(得分:1)
在我看来,这个问题通常最好使用接口来解决。如果对象是可绘制的,请实现IDrawable。如果它是可移动的,则实现IMovable。如果可以更新,请实现IUpdatable。
在创建对象时,他们可以添加它们(IOC),或者可以将它们添加到各种列表中,以确保在适当的时间允许它们移动/更新/绘制/等。
该策略简单,灵活,可以实现高度可读的代码。抵制使用switch语句或脆弱的类层次结构的冲动。
是的,我知道这是C ++,而不是C#,因此您需要在构建基础架构方面多做一些工作。您可以使用抽象类,没有数据成员的多重继承和dynamic_cast来获得类似的效果。旧学校的一些成员更喜欢使用位标志和静态强制转换来避免运行时开销。
答案 2 :(得分:0)
子类的选择或子类的选择是细微的。是否只有少量特定于实体的代码?考虑一个普通的旧通用实体类。这实际上取决于你在其他地方做了什么,以及哪种模式最适合。
在你的情况下,如果你需要让一个类识别另一个多态类,你可以去找
virtual EntityTypeEnum GetType();
返回枚举的方法,说明它是什么。这比试图多次进行类型转换更加清晰,switch
爱它enum
。但它基本上是相同的模式。
答案 3 :(得分:0)
(这个问题最近似乎得到了一些关注,所以我将提供我自己开始使用的解决方案。)
我切换到Entity-Component System,它一直很棒。最初很难弄清楚组件的粒度级别,但在做了几个月之后,我“得到”它并且无法想象回到类继承。
对于我的ECS,我正在使用entityx。它有点令人沮丧,因为它依赖于C ++ 11,我想最终建立在Windows上,但在我找到的所有ECS引擎中,它的语法对我来说似乎是最自然的。
我当前的组件类型是:
ParentComponent
Transformable
TranslateBounds
Drawable
Sprite
SpriteAnimation //gif-style
Velocity
Tags //a list of identifying strings
Animations