component-based game object framework

Recommended Posts

Hello guys, I have a fairly simple question for you guys, but I have not managed to find the answer on the internet anywhere. Has anyone if you succesfully implemented and used a component-based framework in a of your reasonable sized game project? Please bear with me, I'll explain myself. I am talking about a component-based system as described by some of the following links: http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/ http://www.gamearchitect.net/Articles/GameObjects1.html http://www.drizzle.com/~scottb/gdc/game-objects_files/frame.htm When you read the pages, it all makes perfect sense. It's also not so difficult to implement a good framework for creating objects, creating components, attaching them to objects and have them communicate with each other. I have opted, as have most people who use this approach it seems, for a message-system to communicate between components. So one component sends (or broadcasts) a message, and other components who are listening pick up the message and respond to it. So far so good... everything still seems to make sense. But when I start to actually USE this system in a game, I get completely stuck. I completely and utterly fail to convert my seasoned inheritance-based thinking to the new paradigm. To make this more concrete, here are some examples I am struggling with: 1. In my game, I am using a quad tree to manage game objects, and to increase performance in expensive operations such as drawing and collision detection. Implementing this in an inheritance system is very straight forward, but I can't find a way to translate this to a component-based system for the life of me. 2. Performing actions on objects in a particular order. For example, I want to draw sprites on screen starting from the one with the lowest z-value up to the sprite with the highest z-value, so that sprites "closer" to the player are drawn on top of previous ones. How to do this neatly and efficiently in a message-based, component-based system, I don't know. I'm sure that anyone who has ever used a component-based system with success must have tackled these problems, since both are pretty much essential tasks in every 2D game. So if you can give me some insight in how to solve these problems, and how to actually USE such a component-based framework in general, it would be greatly appreciated. Thanks!

Share on other sites
I have not, and tend to think that current languages make such a thing very unwieldy. That said, this comes up every so often and there are people who have used such things in commercial games here.

1. Have a class that defines a leaf in the tree. It can then talk with a component that holds position (either by reference or messages or whatever) in order to reposition itself in the tree (on events or messages triggered by the position component) or otherwise provide the tree access to the object.

2. Same way you'd do it in a 'normal' system. Sorted container of sprites, for each -> draw.

Just because everything is an entity doesn't mean you can't still refer to parts of the entity directly. That is part of the reason behind designing things that way; make interfaces smaller so that the different parts of code use what they need and no more.

Share on other sites
I'm sorry, I'm not with you. I think you're being too fast for me.

First, this leaf in the tree, is it a component itself? Or is it something outside of the component system? Is the tree a component, or an object containing components? If the leaf is a component, then how does it re-organize itself in the tree? By sending messages to other leaf components? Or the tree component/object?

Your answer to point 2 is even more puzzling. How do you even sort something if there's no communication but through messages/events/...?

I think I'm interpreting the entire idea totally wrong.

Share on other sites
I've done so, in various iterations. I just finished refactoring the system I've been using for a while now, actually, and I planned to start writing a blog post or two on it -- particularly aiming at covering the kinds of problems you're running into. There's a lot of (obvious-in-hindsight) sort of articles out there on how component-oriented object systems should be conceptualized... objects as an implicit or explicit aggregation of components, components that provide isolated behavior and/or data, et cetera.

But most such articles (while they are quite good) stop at those high level issues and don't get into the practical nitty-gritty of various sort of implementation problems, such as how you initialize a newly-formed aggregate object with runtime-determined properties, how you deal with inter- and intra-component communication and dependencies without invalidating the benefits of the system, and along what axes you break your former rigid types down into components.

It's not that these problems are hard or that you've never tackled them before, it's than in a component-based system you're kind of approaching them... sideways... at least relative to what you might be used to.

The trick, also, is to remember that you don't need -- or want -- to convert everything to components! Some of your components can be little more than lightweight stand-ins, proxies for the aggregate object within an external subsystem. Rendering is a good example, to address one of your specific questions.

The 'visual component' in my system is little more than a stand-in. The component holds a 'render instance' (which is basically the primitive drawable entity my renderer consumes) and understands how to modify its properties in response to certain messages sent from elsewhere the component system.

But the renderer also holds a reference to the render instance and it is responsible for all those heavy-duty tasks like sorting based on material and all that.

Share on other sites
Thank you, I appreciate your information greatly. I also felt that the high-level concepts are quite easy and well-explained on the internet, but the details are left unexplained too much.

So if I understand you correctly, your rendering system is something like this. You have a class Renderer (could be a singleton), which works with Render Instance components, which are part of every object that wants to be rendered. These Render Instances contain access to information such as current location, bounding box information, sprite/texture details, etc. These components are part of the object, but they are also "sent" (via messages?) to the Renderer when they are initialized. The Renderer does not bother with the fancy component/object architecture for the rest, and just works with the Render Instance component instead of the object directly.

is this a correct interpretation?

Share on other sites
I'm using to great effect in an XNA project a la C#.

It has been a while since I last touched this code but I'll give a summary.
The engine as a whole operates on the MVC principle. The scenegraph is the model. The graphics rendering, sound, input, timer are views. Animation, physics, menu logic, game logic are all controllers.

The scenegraph and scenenodes implement the visitor pattern. Visitors can visit all the nodes in the scenegraph or a node and all of its children.

Nodes components - here named NodeProperties - stored in Dictionary<NodePropertyType, NodeProperty>. A node can have several or no components attached to it. A NodeProperty stored with the key Animatable will always implement the IAnimatable interface. Same goes for rendering and physics, et. all.

Here is a sample of code in the animation controller.
        public void Update()        {            //visit each scenenode and see if it has any animation properties. Apply them            AnimationNodeVisitor animationnodevisitor=new AnimationNodeVisitor(gameTime);            sceneGraph.Visit(animationnodevisitor.AnimateNodeDelegate);        }        private class AnimationNodeVisitor        {            private GameTime gameTime;            public AnimationNodeVisitor(GameTime gameTime)            {                this.gameTime = gameTime;            }            public void AnimateNodeDelegate(ISceneNode node)            {                if (node.Properties.ContainsKey(AnimatableProperty.PropertyType))                {                    ((AnimatableProperty)node.Properties[AnimatableProperty.PropertyType]).Animate(gameTime, ref node);                }            }        }

The AnimatableProperty wraps the IAnimatable and serves as a component to be attached to a scenenode:
    public class AnimatableProperty: NodeProperty, IAnimatable    {        private IAnimatable animatable;        public AnimatableProperty(IAnimatable animatable)        {            this.animatable = animatable;        }        public void Animate(GameTime gameTime, ref ISceneNode sceneNode)        {            animatable.Animate(gameTime, ref sceneNode);        }    }

Finally, we can make things like an AnimatedSkinnedMesh that implemented interfaces IRenderable and IAnimatable. It works with both the RenderableProperty and the AnimatableProperty to be attached to scenenodes.
    public class AnimatedSkinnedMesh : IRenderable, IAnimatable    {        //we can get which animation clip is playing and start a new animation clip using this property.        public string Animation        {            get {                 return animationPlayer.CurrentClip.ToString();            }            set            {                SkinningData skinningData = model.Tag as SkinningData;                if (skinningData.AnimationClips.ContainsKey(value))                {                    AnimationClip clip = skinningData.AnimationClips[value];                    animationPlayer.StartClip(clip);                }                else                {                    animationPlayer.StartClip(skinningData.AnimationClips.Values.GetEnumerator().Current);                }            }        }        protected Model model;        protected Effect effect;        protected Texture texture;        protected string technique;        protected AnimationPlayer animationPlayer;        public AnimatedSkinnedMesh(Model model, Texture texture, Effect effect)        {            this.model = model;            this.texture = texture;            this.effect = effect;            this.technique = "SkinnedModelTechnique";            // Look up our custom skinning information.            SkinningData skinningData = model.Tag as SkinningData;            if (skinningData == null)                throw new InvalidOperationException                    ("This model does not contain a SkinningData tag.");            // Create an animation player, and start decoding an animation clip.            animationPlayer = new AnimationPlayer(skinningData);            AnimationClip clip = skinningData.AnimationClips["Take 001"];            animationPlayer.StartClip(clip);        }        public void Animate(GameTime gameTime, ref ISceneNode node)        {            animationPlayer.Update(gameTime.ElapsedGameTime, true, Matrix.Identity);        }        public void Render(Camera camera, GraphicsDeviceManager graphics)        {            if (model != null && texture!=null && effect!=null)            {                //render the model using the texture and effect from the camera's perspective using the graphicsdevicemanager            }        }    }

Views like the Render view, take advantage of the visitor pattern as well and traverse the scenegraph when the model is changed and needs to be re-rendered.

Share on other sites
Quote:
 Original post by RavelerSo if I understand you correctly, your rendering system is something like this. You have a class Renderer (could be a singleton)

I'll pretend you didn't say that. :(

Quote:
 which works with Render Instance components, which are part of every object that wants to be rendered. These Render Instances contain access to information such as current location, bounding box information, sprite/texture details, etc. These components are part of the object, but they are also "sent" (via messages?) to the Renderer when they are initialized. The Renderer does not bother with the fancy component/object architecture for the rest, and just works with the Render Instance component instead of the object directly.is this a correct interpretation?

More or less, although the render instances are not themselves components, but rather there are VisualComponent objects that contain RenderInstance objects and a handful of other relevant data.

Share on other sites
For me it helps to divide my components into Model-, View- and Controller-Components, that each hold a list of components.

I like to think of the MVC in game programming as following:
- The Model holds world rules that knows nothing about View or Controller.
- The View can render stuff based on the Model.
- The Controller knows about the Model (so that a controller can manipulate a rule) and the View (so that a controller can query it when using a mouse to manipulating on-screen objects).
- Using events, I also allow inter-model, inter-view and inter-controller events.

Check out deWitter's article on MVC for further reading:
http://dewitters.koonsolo.com/gamemvc.html

Using this approach, it has been easier for me to divide my code into components with the rules applied by the MVC.

An example (from deWitters, for the lazy, ported to component-based with event handling):
- We have an entity named Racecar.
- The Racecar holds following components:
- ModelComponents:
- - CarHandlingModel
- - CarPhysicsModel
- - CarStateModel
- ViewComponents:
- - Car3DView
- ControllerComponents:
- - CarHandlingController
- - CarPhysicsController

The CarHandlingController is a component that could be plugged into any car entity to allow the user to control the car (one could probably branch this component to allow a user or an AI to control the CarHandlingController also).

The user presses the left button, which causes the controller to fire the event Event_SteerLeft. The CarHandlingModel subscribes to this event, catches it and handles the rules.

The CarPhysicsController also subscribes to the Event_SteerLeft, so that it may control the physical behavior of such an event. It issues the event Event_WheelRotate that the CarPhysicsModel subscribes to. The model then handles the rules, like friction, forces, velocity and acceleration vectors, etc for the car. The CarPhysicsModel migh change the state of the model through the CarStateModel, if the PhysicsModel notice that the friction might damage the wheels, or that the car crashes or whatever, this also happens through events.

Every time the engine finds it well to do another frame event update, the Car3DView Viewer, which subscribes to the event, will extract the latest information from the modelcomponents and use that to render the car. An AudioView Viewer would've done the same thing to render audio.

That's how I like to divide up my components.

Share on other sites
For the better part of last year I took part in converting a large codebase over to a component-based object system. The conceptualization is relatively easy, but like jpetrie said the real challenge is in the implementation.

How do you cleanly make the entire thing data-driven? How do you automate a lot of the tedious steps involved in authoring new components so that programmers can concentrate on writing the component logic itself and not on writing and maintaining easy-to-screw-up-and-forget boilerplate glue code? How do you efficiently reference and communicate with other components (and handle dependencies) in an order-independent way? How do you make it easy for designers to create slight variations on object definitions (essentially create "base" definitions and types, mimicking inheritance) without forcing them to write redundant data? How do you design your component system to be compatible with C++ inheritance? How do you design a generic interface that allows programmers to easily expose per-instance editing functionality to tools, such as a level editor? How do you design the system to work across multiple project and library boundaries with all their interwoven dependencies? How do you design the system so that you can leverage the same data across multiple creation contexts (i.e. on the server versus the client versus the level editor)? How do you properly handle static (per object definition) and dynamic (per object instance) and pseudo-static (anything not in the other two categories) data? How do you design components to be as independent as possible? How do you design components to be event-based as opposed to service- or update-based?

These are just a few of the major issues we had to address. Unfortunately I can't really discuss any implementation details, and even if I could there's simply too much information to divulge all at once; after all it does represent over a year of design and implementation by a core team of programmers. My suggestion is to start slow and implement features one at a time prioritized by need, as per usual. Start with getting your framework to a place where you can define an object in data and instantiate it in code. Next I would work on getting it integrated into your level editor. That should cover the most important features, which is establishing a basic data-driven pipeline even if it doesn't have a lot of features or requires a lot of manual labor for programmers to get new components implemented. The good news is that a lot of the topics above would probably be considered "advanced" features that a basic functional framework won't need, and when you put the thing together one small piece at a time, you'd be surprised at how quickly you have a cool system up and running.

Share on other sites
Hello all,
My two cents on the subject. I think Zipster nailed some of the major issues, and they couldn't have been worded better.

Another issue to consider is object persistence. I apologise if it has been already mentioned, but it's something really worth taking in account as early as possible in the design.

I think the T=Machine blog mentions the fact that entity systems and relational databases share many similarities. And in my humble opinion, this is a fundamental aspect.

As a final note, I for one found enlightening trying to remodel the way buffs and debuffs work in World of Warcraft. If you're interested in some quick considerations about it, I have a blog with some entries at arainsong.blogspot.com.

Share on other sites
I too find this a very interesting approach to system design - conceptually it seems the most 'complete' in the sense that it is both modular and non-redundant. I'd really like to see a small example - perhaps a simple entity of two components - realised in code.

Share on other sites
We have used this system in both our MMOs here (one in dev and the other has shipped). I'm the lead client programmer so I'm heavily involved in architecture. Both (very different) projects share core components. I was an OO inheritance guy, but I have now seen the light. ;)

Share on other sites
A quick question for those who have/will be implementing this sort of system.

I'm curious about the "messages" which are passed between the components and subsystems. Are these messages serialized chunks or data? Which can be buffered, and passed around in a conventional manner?

Or are we talking about a signal-slot, or a callback/functor type of system?

I imagine that both methods would be possible, but the choice would impact the performance and extensibility of the system.

It would be great to know how people have implemented this, and how it worked.

Share on other sites
Hi guys! Here's a quick follow-up on my progress.

First and foremost, I have to thank you guys, because you finally put me in the right direction. After three iterations of trying to program a object/component manager and communication system that I can wrap my head around, I have finally come up with a very lightweight solution that really does the job.

Basically, the main method of communication between components in my system is the request of other components. Each component can register a "component request" for a particular type of component, and whenever such a component is created or destroyed (either locally, in the same object, or in any object), the interested component is warned about it.

This way, I can still let components interact with other components in a traditional (read: non-message) way, while still maintaining a high level of modularity, since components only need to know about components they work with directly. This is a huge improvement over my previous system, which had boiled down to a big blob object class which contained tons of logic.

I'm only about 1/3 in converting my code base to the new system, but everything seems to port rather nicely. However, there is one particular issue that I can't find a good solution for.

In my system, a game object can have several components, and game objects are assembled on the fly from an XML file. Some of these components are:
- light source (if an object emits light)
- collision box (if an object can collide with other objects)
- sprite (if an object is drawn on the screen)
- movement controller (if an object can move around)

There are quite a few more, but they all share one basic property: they all rely on the same small set of basic object properties that every object has. I am talking about, for example, the location or size of the object (objects can be scaled dynamically in my engine). I have put these in a separate component, but the problem is that I have to include an interface to this component in EVERY other component, which is kind of annoying. But it even gets more annoying if I, for example, have two collision boxes resolve a collision. Because collision box 1 needs the location and size of collision box 2, which it can only access through the base component. So I have to provide functions in the collision box component that just forwards the location and size from the interface to the base component it has.

So I have many functions looking like this:
<code>
Vector2 GOLocalComponent::getBoundingBoxSize() const {
return fParent->getBoundingBoxSize();
}
</code>

This annoys me a great deal, but I see no better solution at the moment. How do you guys go about solving this problem?

Share on other sites
One approach is to just compromise that one part of your framework and store a plain old position vector in your entity/game object. There was a thread made a few years back about component-based architectures (I think the title may have included the word "off-board") where this issue was raised a few times.

Ninja: aha

(it was "outboard" ;) )

Share on other sites
Quote:
 Original post by NohupA quick question for those who have/will be implementing this sort of system.I'm curious about the "messages" which are passed between the components and subsystems. Are these messages serialized chunks or data? Which can be buffered, and passed around in a conventional manner?Or are we talking about a signal-slot, or a callback/functor type of system?I imagine that both methods would be possible, but the choice would impact the performance and extensibility of the system.It would be great to know how people have implemented this, and how it worked.

I've been iterating a ton on the messaging / event-handling aspect of my system. I use a std::map<KeyType, BaseSignal*>. An event is sent by it's KeyType (template-defined), and the accompanying arguments are bound to corresponding signal functor with boost::bind. The resultant boost::function<void ()> objects are inserted in the event queue. This affords me the advantage of being able to pass whatever I want to event handling routines. There's some overhead with all the functors flying around, but I prefer it to the overhead of having a bunch of event classes derived from a BaseEvent.

It was a bit of a hack to make it type-safe, but it works. =P

I can post the code if there's interest, but gotta run for now.

Share on other sites
Quote:
 Original post by m0nkfishOne approach is to just compromise that one part of your framework and store a plain old position vector in your entity/game object. There was a thread made a few years back about component-based architectures (I think the title may have included the word "off-board") where this issue was raised a few times.Ninja: aha(it was "outboard" ;) )

Wow, that's an incredibly interesting post! It answered most of the remaining questions I still had and gave me some really neat ideas on how to improve what I already have. Thank you very much!

Someone should really write a good, lengthy article which discusses these implementation/practical issues in detail. It seems this component-based architecture is used my many developers, but none have bothered to share the details so far. It's a shame because it seems like a really powerful paradigm.

Share on other sites
Quote:
 Original post by RavelerSomeone should really write a good, lengthy article which discusses these implementation/practical issues in detail. It seems this component-based architecture is used my many developers, but none have bothered to share the details so far. It's a shame because it seems like a really powerful paradigm.

To be fair, that's probably due to the fact that many of the implementation details are so tailored to a particular project, proprietary code base, set of requirements, etc., that divulging any of that information either comes dangerously close to breaking the NDA, or impossible to fit into anything less than a full book. I'm on that fence at the moment -- I'd love to go into detail on how we solved a lot of issues in our component-based system, but so much of it is tied to our particular projects and other proprietary software that the risk of stepping on a mine is too high, and there's simply too much that would have to be explained :/

Share on other sites
I agree - there aren't that many [online] resources on this framework approach even though it's been around for ages. In particular, a gdnet article is way overdue! When I've played around with the idea a bit more I may volunteer to write one, but obviously there are far more qualified people here that could do it better :)

Edit:

If you're looking for an example of an implementation (as I was), here you go.

[Edited by - m0nkfish on January 22, 2009 8:52:41 AM]

Share on other sites
Quote:
 Original post by RavelerIt seems this component-based architecture is used my many developers, but none have bothered to share the details so far. It's a shame because it seems like a really powerful paradigm.

GP Gems 5 and 6 both have articles on it. If you only read one, I'd recommend 6.

Share on other sites
I have by now succesfully ported my engine to a component-based architecture. I am pleasantly surprised that it actually made a lot of my code much simpler, and I am very pleased with the result. However, there are still two issues I'm having problems with. I have ideas about solutions for both, but they seem far from ideal.

1. I am worried about the performance of the message-based method. Especially the many string comparisons which have to be made bother me. String comparison is notoriously slow. However, I see no other flexible method to send messages without hard-coding them (which I want to avoid for obvious reasons). A small speed-up can be achieved my performing some kind of "pre-compile", in which messages are translated to integers id's, and then further processed using the id instead of the message name. This saves some string comparisons along the way. How do you guys go about tackling this?

2. My object loading system became one terribly ugly bastard. In my game, I have so-called "object classes", which contain components shared by all objects of that class. Creating a new object of the class was as easy as copying the entire object using something like:

newObject = new GameObject();
*newObject = *objectClass;

Not anymore with the new system. Now I have to call new on each component separately, copy them over, and add them to a new object through the object manager. This results in very ugly code with lots of repeated parts (one for each component type!) and it is rather slow. Worst of it all, I have to provide a copy constructor, which I have to keep up to date so that any new members are also copied.

An alternative might be to do some kind of low-level memory allocation, and make sure all the components are stored in memory in one block. Then you can copy the entire block at once using something like memcpy. A lot faster, but also very, very ugly.

What's your thoughts on these subjects?

[Edited by - Raveler on January 23, 2009 11:31:09 AM]

Share on other sites
It sounds like my component system is implemented similarly to yours. As far as I can tell, creation of new objects hasn't been a bottleneck (I'm using pooled allocation to speed things up a bit). Are you sure that that part of your code is slow?

I do have one question:
Quote:
 Worst of it all, I have to provide a copy constructor, which I have to keep up to date so that any new members are also copied.
Is there a reason that the compiler-generated copy constructor won't suffice?

Share on other sites
1. A simple improvement (if you haven't already) is to use a hash map for the lookups. String comparison is slow, hash comparison is fast. But replacing them with IDs later if needs be (if you're just using constant strings your compiler should do it for you) can be done as you expect.

2. This seems convoluted at first glance. How do you have things setup? Naively, cloning an entity should be as simple as for each component: newbie.add(entry.clone()); Or are you talking about something semantically different from cloning?

Share on other sites
Quote:
 Original post by Ravelerin which messages are translated to integers id's, and then further processed using the id instead of the message name. This saves some string comparisons along the way. How do you guys go about tackling this?

Strings can be slow (practical experience has shown it is rarely a bottle-neck), and caching of component references once resolved commonly solves the performance issues.

Quote:
 This results in very ugly code with lots of repeated parts (one for each component type!) and it is rather slow. Worst of it all, I have to provide a copy constructor, which I have to keep up to date so that any new members are also copied.An alternative might be to do some kind of low-level memory allocation, and make sure all the components are stored in memory in one block. Then you can copy the entire block at once using something like memcpy. A lot faster, but also very, very ugly.

Think about this for a second. There is exactly one class - the container. This container has a list of all components.

To create a new instance that looks exactly like some other type, iterate over other's components and copy construct them. A simple for loop.

By consequence, you customize "classes" by creating instances with certain components and certain values. If you need to "derive" a class (small_orc from basic_orc), you copy basic_orc, and reduce its size. If you then need an army of small_orcs, you copy from small_orc instance.

Share on other sites
Thanks for the advice on object initialization, but it's not exactly what I'm looking for. I was looking for a method to automate the cloning in such a way that I only had to allocate one big memory block, and put all the components in the same block, so that I can copy the entire block at once instead of doing separate new's for each component in each new object.

I envision all these new's becoming a problem later, because in my game the entire map is loaded seamlessly (and HUGE!) and a lot of objects (AI agents who "live their live" somewhere on the map while you play) are loaded and unloaded all the time. However, this is not a big issue at the moment so solving this can be postponed to a later date.

What IS a serious issue, though, is the messaging. I have developped a dynamic lighting system, which uses 2D ray tracing to calculate the lighting on the scene. 2D ray tracing means a LOT of calls to collision boxes for hit tests (I am talking about hundreds of rays with hit tests against every object on screen - making for many thousands of hit tests EVERY update!) Right now, these hit tests are issued as messages, but this apparently is way too slow. Even one single light brings my engine to a halt, while it used to run smoothly before the transition.

This is basically the setup in pseudocode:
for each light   objectsInRange = nodeGraph.getInRange(light)   for all objects in range      sendMessage("TraceRay", objectId, &ray);

I already cache the references to components in hash maps and the like; sendMessage is basically an index in a string -> object id -> component hash map, but this is apparently already too much. I can't use a broadcast message, because I only want to get the objects in range of the light (through the quad tree). I could hack around the component-based design here and just ask for the collision box components of the object directly - it's definitely an option. It would be great if there was a better solution within the framework though.

Create an account

Register a new account

• Forum Statistics

• Total Topics
628303
• Total Posts
2981923

• 10
• 11
• 11
• 10
• 10