Entity system

Published December 05, 2009
Advertisement
Like a lot of people who "grew up" with C++, I read a lot of books that taught you to program along the lines: "Circle is-a shape, which is-a Renderable which is-a BaseObject", and the whole idea of object-composition never really clicked on me. I've worked on a lot of games. A lot, my friends. Haven't released many to the public, because most have been pretty ugly or built for experimentation.

However, in all my development there has been a common theme: that sense of sickening dread (and I'm being literal here) when it comes to hashing out the game object hierarchy. Starting with the base GameObject, and working down the ever-increasingly-complex tree of inheritance (and multiple inheritance; yay!) down to the final culmination: the ever-so-spaghetti-ish PlayerObject. Since the Player is the first object I really usually get working in the game, that means a LOT of base-code to design, write, test, debug, scrap, re-write, curse at, delete in fury, re-write in chastened humility, and ultimately hate to the depths of my being.

Then I read Evolve Your Hierarchy. At first, it took a couple days for it to percolate into my head. I'd of course heard of plenty of rationalizations for object composition systems, but I'd never given them much shrift. In my mind, I was convinced that struggling with a messy object inheritance hierarchy was The Right WayTM. However, this article got me thinking, and over the next couple days as I worked, toiling over wheeled cauldrons of freshly mixed concrete, I pondered it.

And started to get kind of excited.

My programming style is not exactly what you could call top-down. Although I was taught in college to always, always, ALWAYS have your design on paper, rock-solid and thorough, that system never worked for me. My own mental deficiencies naturally prohibit it. I simply can not visualize the complex systems to completion well enough to get the design down. It's not until I physically begin writing code that I really see all the issues, potential pit-falls, and requirements. I'm a hands-on developer. And so naturally, the "is-a" school of class hierarchy thought simply sucks for me. I spend so much time tweaking the hierarchy, agonizing over where in the tree to put such-and-such functionality, etc... that I commonly abandon the project in frustration or lack of interest.

After reading that article, and doing a bit more reading of different sources, I've started experimenting with component-based systems, entity systems, and the like, and I have really started to understand: this stuff is pretty awesome. And it suits my slap-dash design methodology to a T. No more munging around in the hierarchy, trying to find the hack or kludge or work-around that will make the system function. No more hierarchy, in fact.

I am currently playing with an entity system where all game objects are long integer GUIDs, the central nervous system is a global event pump that prioritizes events and dispatches them to sub-systems, and where object components that define the behavior of objects are managed by event-handling subsystems. And the system is neat. Really neat.

The hardest thing I had to wrap my head around, I think, is the fact that rather than an object (say, a player) being a discrete chunk of data and methods localized in one place, it is now a widely distributed set of sub-objects living in disparate parts of the system, and tied together only by that one piece of shared data: the GUID. No direct communication at all between components of an entity. All linkages are done through the event system.

The part of an object that Takes Damage now can no longer directly affect the part of the object that maintains Health. Instead, it must dispatch a message (TAKE_DAMAGE) and rely on the Health component to listen for that message and modify the health accordingly, in it's turn sending out additional messages as needed (HEALTH_IS_LOW, HEALTH_IS_0, etc...) While the system has introduced some convolution, it is of the standardized sort of convolution, and has greatly simplified the way in which object interactions are handled. It has helped me to finally decouple all of my objects, and has given me an approach that suits my design-as-you-go method.

It has changed my life. It has changed the way I look at coding and development. It has added 16 pounds of muscle mass, whitened my teeth, and enabled me to pick up lots of chicks.

Component/Entity systems rawk.
Previous Entry Ogre
Next Entry Black Triangle
0 likes 5 comments

Comments

Jotaf
My hair grew longer and I started riding horses shirtless in Scotland after I implemented my first components system.

For the record, my latest system is lighter than all the others. It's in python. It goes like this.

An object has a MAP of components. They're mapped by type. I ask an object for a component like this:

if Health in object.components: object.components[Health].hp += 5.

Type validation comes for free, and I didn't write a single function to handle it.

Component base class (the only base class ever) defines default functions that some components might not need (for ease). One default property is a pointer to the owner object, so components can directly communicate with other components in the same object.

Event dispatching is as simple as a list, like drop_notify. Add a listener: drop_notify.append(obj.listener_func) (bound methods are fun). Call all listeners: for func in drop_notify: func(). Again, I didn't write a single function for the system.

That's it. I practically didn't write any code at all for it to start working!
December 05, 2009 09:34 AM
evolutional
I'd be interested to see your implementation of it. I went to message-based Entity as an experiment a while ago and loved it.

I started out putting data into components, so the "position" component had the X/Y/Z of the entity, the physics had the size and mass, etc. The main issue I found was the need for a "shared" data - position, size, etc all need to be used by others.

I tried to then move entities being "data", so a guid and a load of properties that could be read/written by components. The components themselves provided the actions on this data. Data was driven through a property system and could be modified by C++ or by script. Not sure how successful it was, because I never did anything with it.

I'm not sure if there's a right way to do it, but am interested in seeing people's interpretations.
December 06, 2009 10:19 AM
Jotaf
Well, I'd love to show an example but it happens to be highly coupled with my game. In my other component designs I could show you the "add_component" function, and all that; but this system doesn't have any functions at all. What I described is really all there is to it. It's not something you code, it's more something... you just use. Like it was there all along :)

Hm, it seems to me like you went a bit overboard with the design (I did it too in the first version). It's easy to define components for everything, but you should always think long and hard if breaking them down so much is really needed. Realistically, when are you going to need an object with position but no mass, or the other way around? If it's immovable, just assign a special value to the mass, or have a flag for that. Actually, I decided against having a position component. Since it's used so often, all objects have it. Yes, when an item is in the inventory its position is unused; but that shouldn't bother anyone. Don't be afraid to make decisions like that, or you'll over-complicate. For instance, there can be a component that means an object can be carried by the player, it can be empty or have values like weight and size; or a component that makes an object behave like a monster, with tons of AI functions. Would it be wise to break down the big AI component into different components? Maybe. But until I need to, I won't do that, or risk devolving my game into a huge, perfect, inter-connected, non-working system. (Until I need to.)
December 06, 2009 09:40 PM
JTippetts
I go with the objects-as-GUIDs system. Sure, there are a handful of components who all need positional/orientation data, but it really is not a burden to just have them all listen for events of the SET_OBJECT_POSITION variety, and keep their own local copies of the relevant data. There is a small amount of redundancy, but nothing extravagant. If it ever gets to the point that I need more components tracking position, I may move it into a base Object class and use object ptrs rather than GUIDs, but until then I'm good.

My systems are divided roughly into 2 varieties: those that handle components, and those that don't. Some systems don't work on objects, and have no associated component, but need to be part of the event system nonetheless. The portion of the framework that takes in raw input and filters it into events such as cast spell, make melee attack, etc... for example.

The systems that handle components are in charge of keeping track of the components, by way of a hash_map keyed on the object's GUID.

Once the game starts, all systems are initialized and registered as listeners for various events. The main loop consists of a basic fixed-logic update loop. The loop simply injects events into the system (UPDATE_LOGIC, UPDATE_VISUALS, RENDER), and the rest just merrily ticks along.
December 07, 2009 06:52 AM
blewisjr
I was planning initially to use a address based system as well works almost like the mail. But when I really think about it a system based off subscription would be better. So your player entity contains components. The components are capable of subscribing to each other using say a boost shared pointer to the component. This will allow them to interact and get the data they need quickly rather then having to deal with the synchronization of a event pump. Should perform faster with more reliability and still remain flexible.
December 13, 2009 03:37 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement