【问题标题】:Component-based game entities and type checking基于组件的游戏实体和类型检查
【发布时间】:2011-09-16 08:09:12
【问题描述】:

我正在编写一个游戏,它将使用基于组件的实体系统。我正在实现的前两个组件是 EntityRepresentation 和 EntityState。表示包含动画,状态决定实体应如何对游戏事件做出反应(EntityStates 的示例:站立、跳跃、攻击、死亡、坠落)。

EntityRepresnetation 根据实体的 EntityState 决定在屏幕上绘制什么动画。如果实体处于“跳跃”状态,则会播放相应的动画。 (参见 EntityRepresentation::changeAnimation() 函数。)

这是我编写课程的大致方式...

class EntityRepresentation
{
  public:
    void draw(float x, float y, long currentTime, DrawingContext& dc) {
        // ...
    }

    void changeAnimation(const EntityState::Id& stateId) {
        currentAnimation = animationMap_[stateId];
    }

  private:
    map<EntityState::Id, const Animation*> animationMap_;
    const Animation* currentAnimation_;
};

目前的方法有一些我真的不喜欢的东西... >:( EntityState::Id 部分。目前,EntityRepresentation 将它持有的每个动画映射到特定的 EntityState::Id。ID 是唯一的到从 EntityState 派生的每个类(对于类,而不是实例是唯一的)。这些 ID 基本上是必须在类的构造函数中手动编写的字符串,这意味着它们很容易发生名称冲突 - 更是如此,因为我计划让游戏可编写脚本(Python)。

谁能给我一些建议,告诉我如何缓解我在使用 ID 时遇到的这个问题。我在同样使用 ID 的游戏中看到了很多基于组件的系统的实现,但我仍然不喜欢它们。它只是让我感觉不对。

也许您可以建议进行设计更改,这样 EntityRepresentation 就不必知道 EntityState 的类型,同时仍然保留封装。

【问题讨论】:

  • “取决于实体的状态”——这是否与 EntityState 相关,或者例如到更广泛意义上的状态?
  • @Luc 在 EntityState 类上。 EntityState 是所有状态的基类。 EntityState 有一个 EntityState::Id 类型的字段(它只是一个字符串),它(应该)对每种类型的 EntityState 都是唯一的。

标签: c++ python components entity-system


【解决方案1】:

我建议使用State pattern实现设计

class EntityState
{
public:
    EntityState(const Animation* animation);
    static const EntityState* getStandingState();
    static const EntityState* getJumpingState();
    static const EntityState* getAttackingState();

    const Animation& getAnimation() const;

protected:
    const Animation* pAnymation_;
};

class EntityRepresentation
{
public:
    void draw(float x, float y, long currentTime, DrawingContext& dc) {
        // ...
        const Animation& animation(state_->getAnimation());
    }

    void changeState(const EntityState* newState) {
        state_ = newState;
    }

private:
    const EntityState* state_;
};

void EventHandler(EntityRepresentation& entity)
{
    entity.changeState(EntityState::getJumpingState());
}

【讨论】:

  • 感谢您的回答! :) 当我发表关于“保留封装”的评论时,这就是我所说的。我不希望状态保存那些应该只对表示感兴趣的数据。
【解决方案2】:

假设在编译时不需要与每个 EntityState 关联的 ID,我使用的一个技巧是使用指针作为 ID。如果 EntityState 是单例,这特别有效,在这种情况下,您可以简单地使用单例实例的地址作为 ID。否则为每个 ID 静态分配一小块内存就可以了。

class EntityState {
public:
    EntityState(void *id) : mId(id) { }
private:
    void *mId;
};

class EntityStateJump : public EntityState {
public:
    EntityStateJump() : EntityState(getId()) { }
private:
    void *getId() {
        static void *id = new int;
        return id;
    }
};

编辑:为避免静态初始化顺序问题进行了细微更改。

【讨论】:

  • 我已经考虑了这两种解决方案。在这两者中,我会选择单例,它开始看起来越来越成为一个可行的选择,特别是因为它们很容易在 Python(我将使用的脚本语言)中实现。不过,我真的不明白 EntityStateJump 类如何为每种类类型生成唯一的 ID。
  • “类类型”是什么意思?我假设从 EntityState 派生的每个类都有一个且只有一个 ID,其中该类的每个实例都将具有相同的 ID。
  • @zennnehoy 是的,我就是这个意思。
  • 关于单例:如果您的各种 EntityState 类不包含任何实体特定的信息(成员),我强烈建议将它们设为单例。通过使用实例指针作为 ID,这也使得检索给定 ID 的 EntityState 变得很简单(因为 ID 是实例)。此外,您无需为 ID 分配额外的内存。
【解决方案3】:

使用 typeid(),这就是它的用途。您可以使用 const type_info* 作为键类型。

或者,你知道,你可以只使用实际继承,比如调用一个虚函数来获取当前的动画,这样会更聪明。

【讨论】:

  • 使用typeid 是我一直在想的一个选项,但我尽量避免,因为它通常用于调试目的。不过,我不会丢弃它。 | 我真的不明白继承在我的情况下是如何工作的。你能补充一些细节吗?
猜你喜欢
  • 2012-05-21
  • 1970-01-01
  • 1970-01-01
  • 2010-12-26
  • 1970-01-01
  • 1970-01-01
  • 2011-04-26
  • 1970-01-01
  • 2017-07-27
相关资源
最近更新 更多