我正在寻找一些关于为游戏对象设计类层次结构的建议。对于这个讨论,我将主要讨论两个特定的类。 Object
和Sprite
。
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
方法不应该公开,因为Sprite
是Object
SpriteRenderer
,所以如果它被更改,那么它是不是Sprite
。但是,我没有看到一种明显的方法来阻止它改变。
总的来说,这是我不应该担心的事情,还是有其他方法可以解决这个问题?
答案 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;
};
这个具有功能类的系统在游戏世界中创建“对象”时效果很好。举一个幻想风格的游戏,你有一个继承自Sword
,Weapon
和Wearable
的课程Carryable
,这意味着剑可以用作武器,是穿着,可以随身携带。