Cowboy Coder

  • Content count

  • Joined

  • Last visited

Community Reputation

365 Neutral

About Cowboy Coder

  • Rank
  1. Introduction Up until fairly recent years, game programmers have consistently used a deep class hierarchy to represent game entities. The tide is beginning to shift from this use of deep hierarchies to a variety of methods that compose a game entity object as an aggregation of components. This article explains what this means, and explores some of the benefits and practical considerations of such an approach. I will describe my personal experience in implementing this system on a large code base, including how to sell the idea to other programmers and management. Game Entities Different games have different requirements as to what is needed in a game entity, but in most games the concept of a game entity is quite similar. A game entity is some object that exists in the game world, usually the object is visible to the player, and usually it can move around. Some example entities: Missile Car Tank Grenade Gun Hero Pedestrian Alien Jetpack Med-kit Rock Entities can usually do various things. Here are some of the things you might want the entities to do Run a script Move React as a rigid body Emit Particles Play located audio Be packed up by the player Be worn by the player Explode React to magnets Be targeted by the player Follow a path Animate Traditional Deep Hierarchies The traditional way of representing a set of game entities like this is to perform an object-oriented decomposition of the set of entities we want to represent. This usually starts out with good intentions, but is frequently modified as the game development progresses - particularly if a game engine is re-used for a different game. We usually end up with something like figure 1, but with a far greater number of nodes in the class hierarchy. As development progresses, we usually need to add various points of functionality to the entities. The objects must either encapsulate the functionality themselves, or be derived from an object that includes that functionality. Often, the functionality is added to the class hierarchy at some level near the root, such as the CEntity class. This has the benefit of the functionality being available to all derived classes, but has the downside of the associated overhead also being carried by those classes. Even fairly simple objects such as rocks or grenades can end up with a large amount of additional functionality (and associated member variables, and possibly unnecessary execution of member functions). Often, the traditional game object hierarchy ends up creating the type of object known as "the blob". The blob is a classic "anti-pattern" which manifests as a huge single class (or a specific branch of a class hierarchy) with a large amount of complex interwoven functionality. While the blob anti-pattern often shows up near the root of the object hierarchy, it will also show up in leaf nodes. The most likely candidate for this is the class representing the player character. Since the game is usually programmed around a single character, then the object representing that character often has a very large amount of functionality. Frequently this is implemented as a large number of member functions in a class such as CPlayer. The result of implementing functionality near the root of the hierarchy is an overburdening of the leaf objects with unneeded functionality. However, the opposite method of implementing the functionality in the leaf nodes can also have unfortunate consequence. Functionality now becomes compartmentalized, so that only the objects specifically programmed for that particular functionality can use it. Programmers often duplicate code to mirror functionality already implemented in a different object. Eventually messy re-factoring is required by re-structuring the class hierarchy to move and combine functionality. Take for example the functionality of having an object react under physics as a rigid body. Not every object needs to be able to do this. As you can see in figure 1, we just have the CRock and the CGrenade classes derived from CRigid. What happens when we want to apply this functionality to the vehicles? You have to move the CRigid class further up the hierarchy, making it more and more like the root-heavy blob pattern we saw before, with all the functionality bunched in a narrow chain of classes from which most other entity classes are derived. An Aggregation of Components The component approach, which is gaining more acceptance in current game development, is one of separating the functionality into individual components that are mostly independent of one another. The traditional object hierarchy is dispensed with, and an object is now created as an aggregation (a collection) of independent components. Each object now only has the functionality that it needs. Any distinct new functionality is implemented by adding a component. A system of forming an object from aggregating components can be implemented in one of three ways, which may be viewed as separate stages in moving from a blob object hierarchy to a composite object. Object as Organized Blob A common way of re-factoring a blob object is to break out the functionality of that object into sub-objects, which are then referenced by the first object. Eventually the parent blob object can mostly be replaced by a series of pointers to other objects, and the blob object's member functions become interface functions for the functions of those sub-objects. This may actually be a reasonable solution if the amount of functionality in your game objects is reasonably small, or if time is limited. You can implement arbitrary object aggregation simply by allowing some of the sub-objects to be absent (by having a NULL pointer to them). Assuming there are not too many sub-objects, then this still allows you the advantage of having lightweight pseudo-composite objects without having to implement a framework for managing the components of that object. The downside is that this is still essentially a blob. All the functionality is still encapsulated within one large object. It is unlikely you will fully factor the blob into purely sub-objects, so you will still be left with some significant overhead, which will weight down your lightweight objects. You still have the overhead of constantly checking all the NULL pointers to see if they need updating. Object as Component Container The next stage is to factor out each of the components (the "sub-objects" in the previous example) into objects that share a common base class, so we can store a list of components inside of an object. This is an intermediate solution, as we still have the root "object" that represents the game entity. However, it may be a reasonable solution, or indeed the only practical solution, if a large part of the code base requires this notion of a game object as a concrete object. Your game object then becomes an interface object that acts as a bridge between the legacy code in your game, and the new system of composite objects. As time permits, you will eventually remove the notion of game entity as being a monolithic object, and instead address the object more and more directly via its components. Eventually you may be able to transition to a pure aggregation. Object as a Pure Aggregation In this final arrangement, an object is simply the sum of its parts. Figure 2 shows a scheme where each game entity is comprised of a collection of components. There is no "game entity object" as such. Each column in the diagram represents a list of identical components, each row can be though of as representing an objects. The components themselves can be treated as being independent of the objects they make up. Practical Experience I first implemented a system of object composition from components when working at Neversoft, on the Tony Hawk series of games. Our game object system had developed over the course of three successive games until we had a game object hierarchy that resembled the blob anti-pattern I described earlier. It suffered from all the same problems: the objects tended to be heavyweight. Objects had unnecessary data and functionality. Sometimes the unnecessary functionality slowed down the game. Functionality was sometimes duplicated in different branches of the tree. I had heard about this new-fangled "component based objects" system on the sweng-gamedev mailing list, and decided it sounded like a very good idea. I set to re-organizing the code-base and two years later, it was done. Why so long? Well, firstly we were churning out Tony Hawk games at the rate of one per year, so there was little time between games to devote to re-factoring. Secondly, I miscalculated the scale of the problem. A three-year old code-base contains a lot of code. A lot of that code became somewhat inflexible over the years. Since the code relied on the game objects being game objects, and very particular game objects at that, it proved to be a lot of work to make everything work as components. Expect Resistance The first problem I encountered was in trying to explain the system to other programmers. If you are not particularly familiar with the idea of object composition and aggregation, then it can strike you as pointless, needlessly complex, and unnecessary extra work. Programmers who have worked with the traditional system of object hierarchies for many years become very used to working that way. They even become very good at working that way, and manage to work around the problems as they arise. Selling the idea to management is also a difficult. You need to be able to explain in plain words exactly how this is going to help get the game done faster. Something along the lines of: "Whenever we add new stuff to the game now, it takes a long time to do, and there are lots of bugs. If we do this new component object thing, it will let us add new stuff a lot quicker, and have fewer bugs." My approach was to introduce it in a stealth manner. I first discussed the idea with a couple of programmers individually, and eventually convinced them it was a good idea. I then implemented the basic framework for generic components, and implemented one small aspect of game object functionality as a component. I then presented this to the rest of the programmers. There was some confusion and resistance, but since it was implemented and working there was not much argument. Slow Progress Once the framework was established, the conversion from static hierarchy to object composition happened slowly. It is thankless work, since you spend hours and days re-factoring code into something that seems functionally no different to the code it replaces. In addition, we were doing this while still implementing new features for the next iteration of the game. At an early point, we hit the problem of re-factoring our largest class, the skater class. Since it contained a vast amount of functionality, it was almost impossible to re-factor a piece at a time. In addition, it could not really be re-factored until the other object systems in the game conformed to the component way of doing things. These in turn could not be cleanly refactored as components unless the skater was also a component. The solution here was to create a "blob component." This was a single huge component, which encapsulated much of the functionality of the skater class. A few other blob components were required in other places, and we eventually shoehorned the entire object system into a collection of components. Once this was in place, the blob components could gradually be refactored into more atomic components. Results The first results of this re-factoring were barely tangible. But over time the code became cleaner and easier to maintain as functionality was encapsulated in discreet components. Programmers began to create new types of object in less time simply by combining a few components and adding a new one. We created a system of data-driven object creation, so that entirely new types of object could be created by the designers. This proved invaluable in the speedy creation and configuration of new types of objects. Eventually the programmers came (at different rates) to embrace the component system, and became very adept at adding new functionality via components. The common interface and the strict encapsulation led to a reduction in bugs, and code that that was easier to read, maintain and re-use. Implementation Details Giving each component a common interface means deriving from a base class with virtual functions. This introduces some additional overhead. Do not let this turn you against the idea, as the additional overhead is small, compared to the savings due to simplification of objects. Since each component has a common interface, it is very easy to add additional debug member functions to each component. That made it a relatively simple matter to add an object inspector that could dump the contents of the components of a composite object in a human readable manner. Later this would evolve into a sophisticated remote debugging tool that was always up to date with all possible types of game object. This is something that would have been very tiresome to implement and maintain with the traditional hierarchy. Ideally, components should not know about each other. However, in a practical world, there are always going to be dependencies between specific components. Performance issues also dictate that components should be able to quickly access other components. Initially we had all component references going through the component manager, however when this started using up over 5% of our CPU time, we allowed the components to store pointers to one another, and call member functions in other components directly. The order of composition of the components in an object can be important. In our initial system, we stored the components as a list inside a container object. Each component had an update function, which was called as we iterated over the list of components for each object. Since the object creation was data driven, it could create problems if the list of components is in an unexpected order. If one object updates physics before animation, and the other updates animation before physics, then they might get out of sync with each other. Dependencies such as this need to be identified, and then enforced in code. Conclusions Moving from blob style object hierarchies to composite objects made from a collection of components was one of the best decisions I made. The initial results were disappointing as it took a long time to re-factor existing code. However, the end results were well worth it, with lightweight, flexible, robust and re-usable code. Resources Scott Bilas: GDC 2002 Presentation: A Data-Driven Game Object System Bjarne Rene: Component Based Object Management. Game Programming Gems 5, 2005, page 25. Kyle Wilson: Game Object Structure: Inheritence vs Aggregation, 2002, This article is reprinted with permission from
  2. Alan, here's a link to a simple implementation of the GPG component system: (From )   That's very bare bones. Really the power of a component system comes from how you integrate it with script and data, and that part can get a bit more complicated. Just implementing a component system in C++ alone is often not particularly useful.   It also depends on exactly what you are trying to do. :)
  3. Note this article was written in 2006, and was based on my experience with Tony Hawk 3 & 4 from back in about 2000-2003   Hence it's a bit old. While the basic principles remain, the world has shifted on a little, in particular there's more variety in scripting language, and this can alter the variety of ways objects can be aggregated. 
  4.   Thanks, I'm afraid I don't have any example source links off-hand. From a high level, you might want to look at the Unity implementation: I know it's not open source for the implementation, but it gives a good practical example of how it can be used, and you can probably find a bunch of example Unity games.  
  5. Quote:Original post by Tesshu Keeping in mind that I am liking my component system but if I was doing a smaller project , there is no way I would use one. You pay for it in the debugging and code management. Using a simple class hierarchy with interfaces works more than fine if you don't have heavy requirements. It's very easy to fall into over engineering code. I agree to some extent, I only came to use components after the existing system became too unwieldy. I would certainly do it again if I was to do any game with a large enough variety of objects. It's not all about aggregation vs inheritance, you also have to think about how message passing and data binding fits into the whole model. You also need to be prepared to make some comprises from a "pure" model, for speed consideration.
  6. You don't really have a quad. You have two triangles. Change your code now to use triangles, and you'll save a lot of trouble later, and it will look a lot better.
  7. OpenGL 3d N-resolution spherical spacing?

    Apart from the platonic solids, you can't distribute them perfectly. Check this out:
  8. Floor renders slower than ceiling

    Perhaps it is to do with clipping. Try moving the viewpoint around.
  9. C++ comments are bad!

    Here's a good source of debatable comments: (et al), Comments like "Pound the ESR really hard over the head with a big hammer" Now, you might consider that comments like this are in some way warranted, for this being the Linux Kernal source, it needs to deal with a bunch of indeterministic hardware. Or maybe you say the names of the hardware registers are not quite meaningful enough to be self-explanatory. Well, unfortunately, that's the way it is with a lot of software. Not everyone works in teams of one, or even seven, or twenty. Not everyone gets to write their own game engine with a group of close friends. Some people have to work with what are essentially huge indeterministic systems that have badly named interfaces. The interactions you perform with these systems often need comments. Even on small teams, the sad truth is that the twin furies of deadlines and inertia tend to fold your code into something slightly fishy. Comments at that point might be a symptom, a palliative, even a crutch, but that does not mean you go cold turkey. Comments are not in and of themselves a bad thing. You don't want to get rid of comments. You want to get rid of the need for comments.
  10. Buffer overrun or what?

    More likely a dangling pointer. Look at the "corruption" and see what it is in bytes. Is it a float? Zero? The nature of the corruption can give you a clue.
  11. Applying Simple Joints To 2D Particles

    Well, if (d.LengthSquared() != (length * length)) is never going to return false, so they are never going to settle. You need some kind of epsilon range if you want them to actually stop. You might also want to look at verlet integration for this kind of thing. For example, see my blob code.
  12. Applying Simple Joints To 2D Particles

    How are you handling the velocity change?
  13. Engine Resource Recommendations

    "Introduction to Game Development", edited by Steve Rabin, is a good book. It covers all aspects of building a game, including software architecture.
  14. Quote:Original post by JohnBolton You also need to realize that people commonly use the word "class" when the really mean "an object of the class". Yes, I was looking up random numbers in C# today, when I came across this beauty: Random RandomClass = new Random(); RandomClass is an instance of the Random Class, but it is not a class. "Random" on the other hand, is the class.
  15. Quote:Original post by skittleo Quote:Original post by gharen2 A simpler example: "Husky", "Beagle", and "Doberman" are classes. They represent a general type of dog. "Rover", "Fido", and "Rex" are objects. The represent a particular individual dog, that belongs to one of the types of dog. And to that end Dog would be the abstract base class. Clearly dog subclasses from "Animal"! (Or "Mammal"?). Dog can also be an aggregation of various physical and qualitative components, which is better.