I took this approach in one of my game jam entries (Ineptia), and it made adding new objects much easier. It also made it easier to add behaviour from one object to another. For example, exploding crates have a BodyComponent (which contains their HP), TargetableComponent (so the player can target them), and then have an Inventory which lets the game know what to spawn when they 'splode.
At the moment I've settled on something based on this approach, where entities are just an identifier, components are dumb blocks of data and all the processing happens in "systems" that register themselves as processors for one or more components.
This way of doing things made me pretty uneasy at first, especially as code that operates on components is stored in a system rather than the component itself. However, one hurdle I kept bumping into when code was in components was "which bit of behaviour goes where?". For example, when a crate is destroyed, which component controls spawning particles? What about making the item within fly out?
The components = data, systems = behaviour model does make sense after a while, as a single piece of data in a component may effect behaviour in several different places. What you end up with is more of a database (entities + components) and a bunch of stuff that operates on it.
What I've got right now is a template system for storing entity compositions, which can then be spawned by name. For example, here's the (slightly outdated) template for a wooden sword:
Code:
/**
* A simple wooden sword.
*/
[t:template,n:weapon_wooden_sword] {
doc = "Wooden Sword";
specializes = base_weapon;
equipment:attack = 3
[item]
{
display_name = "Wooden Sword";
icon = "demo_5.icons.weapons.wooden_sword";
description = "The most basic kind of sword.";
base_price = 75;
}
}
* A simple wooden sword.
*/
[t:template,n:weapon_wooden_sword] {
doc = "Wooden Sword";
specializes = base_weapon;
equipment:attack = 3
[item]
{
display_name = "Wooden Sword";
icon = "demo_5.icons.weapons.wooden_sword";
description = "The most basic kind of sword.";
base_price = 75;
}
}
The "specializes" keyword allows a template to inherit data from another template. So the wooden sword inherits from "base_weapon", which inherits from "base_item" - that way defaults can be moved further up the template tree. The sword has two components: item (which holds things like the price, icon and name) and equipment, which holds equipment type, ranges, stat modifiers etc.
This approach makes spawning a little slower, but most of the heavy lifting can be done when templates are loaded and the template tree is processed. It also makes adding new weapons a heck of a lot quicker, and because it's all data it can be reloaded on the fly.
Like I said, there's still plenty of things that I want to fix, but I'll keep this updated as I figure things out and eventually split it into another article.
Here's a bunch of things to read:
- Concepts of “object identity” in game programming…
- The Entity Systems Wiki
- Dungeon Siege Notes (definitely check out "A Data-Drive Game Object System")
- Ludum Dare Dry Run: Lessons Learned (Lots of details in the comments)
As always, questions & comments are much appreciated
