The common form of a behaviour-based engine is to have behaviour classes to represent aspects of the entities in the game, so:
Code: Select all
class Behaviour {
public:
virtual Entity * parent() = 0;
};
class MoveBehaviour : public Behaviour {
public:
virtual void update() = 0;
};
class InteractBehaviour : public Behaviour {
public:
virtual void interactWith(Entity * other) = 0;
};
// ...
Code: Select all
class Entity {
public:
virtual MoveBehaviour * moveBehaviour() = 0;
virtual InteractBehaviour * interactBehaviour() = 0;
// And a pointer to each kind of behaviour an Entity might possibly have.
};
// ...
NPC * createNPC() {
auto_ptr<Entity> npc(new NPC);
npc->addMoveBehaviour(new RandomMoveBehaviour);
npc->addInteractBehaviour(new AttackOnSightBehaviour);
return npc.release();
}
void update(Entity * e) {
if(e->moveBehaviour())
e->moveBehaviour()->update();
}
The alternative I'm going to propose aims at providing a generalization to avoid these unneeded pointers, while still maintaining the full power of behaviours. First, we change Entity to store a list (more on this later) of Behaviours.
Code: Select all
class Entity {
public:
list<Behaviour *> behaviours() = 0;
void addBehaviour(Behaviour * behaviour) = 0;
};
NPC * createNPC() {
auto_ptr<Entity> npc(new NPC);
npc->addBehaviour(new RandomMoveBehaviour);
return npc.release();
}
Code: Select all
class Behaviour {
public:
virtual Entity * parent() = 0;
virtual void accept(BehaviourVisitor * visitor) = 0;
};
class BehaviourVisitor {
public:
visitMoveBehaviour(MoveBehaviour *) {}
visitInteractBehaviour(InteractBehaviour *) {}
// One visit* method for each of Behaviour subclasses.
};
Code: Select all
void MoveBehaviour::accept(BehaviourVisitor * visitor) {
visitor->visitMoveBehaviour(this);
}
// ...
void InteractBehaviour::accept(BehaviourVisitor * visitor) {
visitor->visitInteractbehaviour(this);
}
Code: Select all
class BehaviourFilter : public BehaviourVisitor {
public:
enum class Type {
MOVE, INTERACT
};
BehaviourFilter(Type type) : m_type(type) {}
list<Behaviour *> filter(list<Behaviour *> behaviours);
void acceptMoveBehaviour(MoveBehaviour * moveBehaviour);
void acceptInteractBehaviour(InteractBehaviour * interactBehaviour);
private:
Type m_type;
list<Behaviour *> m_result;
};
list<Behaviour *> BehaviourFilter::filter(list<Behaviour *> behaviours) {
result.clear();
for(Behaviour * b : behaviours)
b->accept(this);
return result;
}
void BehaviourFilter::visitMoveBehaviour(MoveBehaviour * moveBehaviour) {
if(m_type == Type::MOVE)
result.push_back(moveBehaviour);
}
void BehaviourFilter::visitInteractBehaviour(InteractBehaviour * interactBehaviour) {
if(m_type == Type::INTERACT)
result.push_back(interactBehaviour);
}
Code: Select all
void updateMoves(list<Entity *> entities) {
BehaviourFilter filter(BehaviourFilter::Type::MOVE);
for(Entity * e : entities) {
for(Behaviour * b : filter.filter(e->behaviours())) {
MoveBehaviour * moveBehaviour = dynamic_cast<MoveBehaviour *>(b);
moveBehaviour->update();
}
}
}
This is getting really long, so I think I'll split it into sessions. I don't suppose many (if any) will read this, but, if you did, I'd really appreciate if you shared your thoughts about it.