0

我正在寻找一些关于为游戏对象设计类层次结构的建议。对于这个讨论,我将主要讨论两个特定的类。一个ObjectSprite

是游戏中任何类型对象的Object基类。Sprite直接继承自Object,但某些Object公共方法不再有意义。这里有一些代码来解释:

class Object {
public:
    Object() : renderer(NULL), controller(NULL), collider(NULL) { }
    virtual ~Object();

    Renderer *getRenderer() const;
    Controller *getController() const;
    Collider *getCollider() const;

    void setRenderer(Renderer *renderer);
    void setController(Controller *controller);
    void setCollider(Collider *collider);
private:
    Renderer *renderer;
    Controller *controller;
    Collider *collider;
};

Object具有渲染器、碰撞器和控制器三个组件。 Sprite当然有这三个组件,但使用一个SpriteRenderer. 然后我想我不希望setRenderer为 sprite 类公开该方法。

所以我认为我应该编写精灵类如下:

class Sprite : protected Object {
public:
    Sprite(Texture *texture) : Object(), texture(texture) {
        setRenderer(new SpriteRenderer(this));
    }

    Renderer *getRenderer() const;
    Controller *getController() const;
    Collider *getCollider() const;

    void setController(Controller *controller);
    void setCollider(Collider *collider);
private:
    Texture *texture;
};

getter 和 setter 只调用相关的Object方法。但在这种情况下,我不能真正使用Spriteas an Object,例如将它传递给一个接受 s 的函数Object

我的另一个实现是:

class Sprite : public Object {
public:
    Sprite(Texture *texture) : Object(), texture(texture) {
        setRenderer(new SpriteRenderer(this));
    }
private:
    Texture *texture;
};

这暴露了setRenderer方法。

我的思考过程是,setRenderer方法不应该暴露,因为 aSpriteObject带有 a 的SpriteRenderer,所以如果它被改变了,那么它就不是 a Sprite。但是,我没有看到一种明显的方法来防止它发生变化。

一般来说,这是我不应该担心的事情,还是有另一种方法可以进行这种设计?

4

3 回答 3

1

与其使用 getter 和 setter 对对象进行建模,不如让对象真正做你想做的事——你不必担心在没有 setter 时暴露 setter。:)

例如,让对象自己渲染而不是返回渲染器。这样,精灵可以覆盖此行为:

class Object {
    virtual void render() = 0;
}

class Sprite : public Object {
    void render() {
      // use a SpriteRenderer
    }
}
于 2012-08-16T06:58:54.390 回答
1

您应该问自己:您是否需要在运行时更改渲染器(和其他组件)?我相信创建对象实例后不需要这样做。因此,我根本不会将方法 setRenderer(和其他 setXXX)放在公共 API 中。您是否需要公开这些参数?如果是,那么我可以提出以下建议:

.

class Object
{
public:
    // Constructor with all the necessary parameters. 
    // For simplicity I keep only Renderer
    Object(const Renderer & renderer) :
        renderer_(renderer)
    {
    }

    const Renderer & getRenderer() const { return renderer_; }

private:
    // I think reference is better if Renderer MUST always be not NULL
    const Renderer & renderer_;

};

这样你就有了很好的不可变类。

要构建 Sprite 实例,您可能需要 SpriteFactory 或 SpriteBuilder。

.

class Sprite : public Object
{
public:
    Sprite(const Renderer & renderer, const SpriteSpecificStuff & stuff) :
        Object(renderer),
        ...
    {
    }
};

class SpriteBuilder
{
public:
    void setRenderer(const Renderer * renderer);
    void setSpriteSpecificStuff();

    std::auto_ptr<Sprite> build();
};

同样,使用构建器,您将拥有不错的不可变类

于 2012-08-16T07:16:31.210 回答
1

结合我关于多重继承的评论,您可以将对象视为一些可渲染的,一些能够碰撞的,一些被“控制”(无论这在您的系统中意味着什么)。因此,您可以为每个功能拥有一个基类,而不是拥有一个包含所有这三个属性(并且可能更多)的单个基对象。

class Renderable
{
public:
    Renderable(Rendered *renderer) : remderer_(renderer) {}

    Renderer *getRenderer() const;
    void setRenderer(Renderer *renderer);

private:
    Renderer *renderer_;
}

class Controllable
{
public:
    Controllable(Constroller &constroller) : controller_(constroller) {}

    Controller *getController() const;

    void setController(Controller *controller);

private:
    Controller *controller_;
};

class Collidable {
public:
    Collidable(Collider *collider) : collider_(collider) {}

    Collider *getCollider() const;

    void setCollider(Collider *collider);

private:
    Collider *collider_;
};

然后Sprite具有所有三个功能的类,您继承所有三个:

class Sprite : public Renderable, public Controllable, public Collidable
{
public:
    Sprite()
        : Renderable(new SpriteRenderer)  // Supply special renderer
        {}

private:
    // Disallow users of the `Sprite` class to call this function;
    using Renderable::setRenderer;
};

这个具有能力类的系统在创建游戏世界中的“对象”时非常有效。以一个奇幻风格的游戏为例,你有一个Sword继承自和的类Weapon,这意味着剑可以用作武器,可以佩戴和携带。WearableCarryable

于 2012-08-17T05:01:15.213 回答