So I'm back to work on my game engine. One of the first things I wanted to do was to revamp the way entities are handled. For awhile now I _thought_ I had the component ideology down, but it turns out I was way off, and was just using a hierarchy-based system masquerading as a component-based system.
I've done a ton of reading, and want to be sure I have things straight.
When I first read about the component architecture a long, long time ago, the idea of data-driven objects completely flew over my head. I think that tonight, when I realized how critical it was to the architecture, something clicked and I understood everything a whole lot better than I did previously.
So here's my understanding of it all:
- There's a Behavior superclass, which all other behaviors inherit from:
Behavior.hBehavior.cppCode: Select all
enum Behaviors { PHYSICS, RENDER, INPUT, ... }; class Behavior { protected: const char *m_name; Entity *m_entity; public: Behavior(const char *_name, Entity *_entity); virtual ~Behavior(); virtual const char *GetName(); virtual void Update(float _delta); static const char *Names[]; } const char *Behavior::Names[] = { "physics", "render", "input", ... };
Code: Select all
Behavior::Behavior(const char *_name, Entity *_entity) : m_name(newStr(_name)), m_entity(_entity) { } Behavior::~Behavior() { m_entity = NULL; } const char *Behavior::GetName() { return m_name; } void Behavior::Update(float _delta) { }
- The actual Entity class should be relatively barebones, basically looking something like this:
Entity.hEntity.cppCode: Select all
class Entity { protected: const char *m_name; Data::HashTable<Behavior *> m_behaviors; public: Entity(const char* _name); virtual ~Entity(); virtual bool HasBehavior(const char *_name); virtual Behavior *GetBehavior(const char *_name); virtual bool AddBehavior(const char *_name, Behavior *_behavior); virtual bool RemoveBehavior(const char *_name); virtual void Update(float _delta); virtual void Render(float _delta); }
Code: Select all
Entity::Entity(const char *_name) : m_name(newStr(_name)) { } Entity::~Entity() { for (size_t i = 0; i < m_behaviors.used(); i++) { delete m_behaviors[i]; m_behaviors.erase(); } } bool Entity::HasBehavior(const char *_name) { return m_behaviors.exists(_name); } Behavior *Entity::GetBehavior(const char *_name) { return m_behaviors.find(_name); } bool Entity::AddBehavior(const char *_name, Behavior *_behavior) { return m_behaviors.insert(_name, _behavior) > -1; } bool Entity::RemoveBehavior(const char *_name) { if (m_behaviors.exists(_name)) { delete m_behaviors.find(_name); return m_behaviors.erase(_name); } else { return false; } } void Entity::Update(float _delta) { for (size_t i = 0; m_behaviors.used(); i++) { if (strcmp(m_behaviors[i]->GetName(), Behavior::Names[RENDER]) continue; m_behaviors[i]->Update(_delta); } } void Entity::Render(float _delta) { if (HasBehavior(Behavior::Names[RENDER])) { GetBehavior(Behavior::Names[RENDER])->Update(_delta); } }
-
Individual entities are described in text/binary data files written in a description language. So for example, I have a player entity in my game, defined in a lua file:
player.lua:Now, when I load the entity, I would have the engine parse the lua file and do something like this:Code: Select all
name = "player" behaviors = { { name = "physics", feels_gravity = true -- other physics behavior fields specific to the player }, { name = "render", texture = "player.png", -- ... }, { name = "input" triggers = { "keyup", "keydown" } }, ... }
Code: Select all
/* ... dofile player.lua ... */ Entity *e = new Entity(/* pass in name from lua file */); /* ... loop through behaviors table ... */ /* ... if/else tree deciding what behavior class to use based on behavior's name defined in the lua file, for example, RenderBehavior: ... */ RenderBehavior *b = new RenderBehavior(e); b->SetTexture(/* pass in texture filename from lua file */); e->AddBehavior("render", b); /* ... Add the rest of the behaviors ... */ // Add entity to the scene g_game->GetScene()->AddEntity(e);
So, how's my understanding? Is it still way off? Almost there? Spot on?