在C ++中为游戏对象设计类层次结构

时间:2012-08-16 05:59:34

标签: c++ oop

我正在寻找一些关于为游戏对象设计类层次结构的建议。对于这个讨论,我将主要讨论两个特定的类。 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方法。但在这种情况下,我无法真正将Sprite用作Object,例如将它传递给一个需要Object s。

的函数

我的其他实现是:

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

公开了setRenderer方法。

我的思维过程是setRenderer方法不应该公开,因为SpriteObject SpriteRenderer,所以如果它被更改,那么它是不是Sprite。但是,我没有看到一种明显的方法来阻止它改变。

总的来说,这是我不应该担心的事情,还是有其他方法可以解决这个问题?

3 个答案:

答案 0 :(得分:1)

不是使用getter和setter对对象进行建模,而是让对象实际执行您想要的操作 - 当没有setter时,您不必担心暴露setter。 :)

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

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

class Sprite : public Object {
    void render() {
      // use a SpriteRenderer
    }
}

答案 1 :(得分: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();
};

再次,使用构建器,您将获得不错的不可变类

答案 2 :(得分: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;
};

这个具有功能类的系统在游戏世界中创建“对象”时效果很好。举一个幻想风格的游戏,你有一个继承自SwordWeaponWearable的课程Carryable,这意味着剑可以用作武器,是穿着,可以随身携带。