Jump to content

  • Log In with Google      Sign In   
  • Create Account

- - - - -

Refactoring with components

Posted by , 02 February 2011 · 721 views

I've been refactoring my engine with the component-entity model, and things are turning out quite nicely. The one problem I always had with components was the way inter-component dependencies were handled. In the article I linked (one of the classics on the subject), the author says his components originally referenced each other through a component manager, but eventually they allowed components to store pointers to each other for performance reasons. In my mind, that's way too many concrete dependencies.

So I'm rolling my own data binding system. Instead of directly accessing a component's data from another component, I'm having both components store a copy of the properties they need. So if the physics component wants to overwrite the position and orientation of the model component, I don't have to do this inside the physics component:

this.ModelComponentReference.WorldTransform = blah;
Instead, the physics component and model component have separate transforms. These act like input/output ports for each component. When constructing an entity, I bind the two "ports" together. I can also use multiple inputs for one output by combining them together with a transformation function. I can also bind actions (which is basically like event handling).

Here's a good example of the power of this system. Let's say I have a working player entity, and I want to have it play footstep sounds when he's walking. I already have a PositionComponent and a PlayerStateComponent (with an enum of states like Idle, Running, Jumping...).

Let's create a TimerComponent that has a few properties like Enabled, Interval, and Repeats, and an action (or event) that fires when the timer expires. First we would set a proper interval and enable repeating. Then we could create a binding for the Enabled state, which would set Enabled to true whenever the player's state is "Running". In my implementation, this is accomplished with a closure.

Then we can create a SoundComponent that's set up with our footstep sounds, and whose 3D position is bound to our player's position. We could then bind the TimerComponent's firing event to the SoundComponent's "Play" method. Mission accomplished.

Bear in mind that this system is still untested and I'm still in the middle of refactoring, but so far it's looking good. Here's a screenshot of my player factory code to give you an idea of how I'm doing this:

Posted Image

So there you go. It's not perfect, but I think it's going to work. The idea, at least, is sound. I'll post later about the details of my binding system, which is still a matter of some contention. I'm trying to keep things clean while avoiding reflection at all costs.

Mirrored on my blog

This is interesting. I imagine it results in a pretty decoupled system. How do the components get updated each frame, are you holding them in some array or list?

It'd be interesting to see a comparison of performance between the older engine and the refactored one.
Yeah, I just have a list of entities, each of which has a list of components. So all the components in entity one or updated, then entity two, etc. I think the data binding will slow the engine down a bit, but luckily I'm not doing too much processor-intensive work.

But I do think this pattern lends itself pretty well to multi-threading. You could pretty easily split it up so one thread updated all the components of a certain type or something. I'll have to give it a shot some time.

January 2017 »

15161718 19 2021