|
There are many approaches to game engine design, and this is far from the best in all cases, but it is certainly the most common overall. Welcome to the wide world of component-based entities.
First, let's address the way most people fresh out of Data Structures, CS 101, etc think of game objects:
class Engine { int numberOfCylinders; .... }
class Car : Public Engine { bool hasComfySeats; bool numSeats; ... }
... which is, in a word - bad. In two words, really bad. In two different words, painfully inflexible. You can make a game this way, and many have, and it was even "the" way to do it way back in the day, but those days have passed, game engines have blossomed in size, and now we're willing to spent a bit of processing power on making our tech flexible and resuable.
Let's take a peak at why. Imagine you need 10 different enemies. Ok, great, now we have 10 different enemy classes. So far so good, right? So now we realize that each of those entities is 90% the same, and only 10% different. We don't want to copy-paste identical code that many times, it would be entirely unmaintainable as it grew and we wanted to tweak the base functionality of entities, so we think - "ah hah! we'll make a shared function for the equivalent bits!". Ok, great. But now one of those entities has a slightly different AI routine too, but the same everything else. So now we break the AI out of the shared code, and give him a unique AI section. Now there's another entity, that wants that AI, but has different physics, so now we have to break the physics out of that shared code, make a 2 versions of physics, and then the new entity has a unique physics but shares the other different AI and then AAARRRRRGGGGH.
So we stop just short of hanging ourselves, back out, and realize that that route is unsupportable. So we go back to thinking about keeping each entity separate, and just copy-pasting code around, and even if it's identical - whatever, if we change one, we just remember to change them all. Now a year later, you have 100 entity types... and you realize you made a typo in their physics stepping. All of their physics routines have to be changed. AAAAARRRRRRGGGHHH.
Thus - enter components. Which, and excuse the extremely rough pseudo code, might go something like this...
class Entity {
void AttachComponent(ComponentType, argumentList, name)
void DetachComponent(name) void UpdateComponents()
void UpdateComponentType(ComponentType typeOfComponentToUpdate)
void HandleMessage(MessageType message) // echoes to all attached components
...
BaseComponentList attachedComponentList
.... }
class BaseComponent {
virtual void Startup()
virtual void Update()
virtual void Shutdown()
virtual void HandleMessage(MessageType message) .... }
class RenderComponent: Public BaseComponent {
virtual void Startup() // (registers the renderable mesh with the rendering system)
virtual void Shutdown() // (removes the renderable mesh from the rendering system)
... }
Abolish the idea of an object. There is no such thing as a "chair", or an "orc" - there is only data, and components, that together may resemble such. A chair has "physics", "rendering", and "the ability to be sat upon" (which we'll call "interaction"). An orc has "moving physics" since it can walk, "rendering", and "AI". Each of these components, and this is a very stripped-down example, stands alone, and has no actual knowledge of whatever else may be attached to a given entity. The entity itself, such as it is, only has a list of components attached to it, and a generic data "bucket" that all components can write to or read from. It may also have a messaging system, which lets it broadcast one of a list of messages out to every attached component, and the components may be able to themselves send messages to the entity they're attached to (which then broadcasts the message out to all components attached to the entity... including the component that sent the message, but it's smart enough to ignore it). This messaging system is also likely exposed to the world, allowing other actors in the world to send messages to the entity, and thus all components attached to that entity, but more on that in a sec.
Now the beauty of this is reusability. Two rendering components are basically the same thing - they take a mesh and make it render - meaning that every renderable object in your game can, mostly, share that one component. All that changes, really, is the argument you pass the component on creation, which'll probably be the filename of a mesh to render amongst other things. Even if you have a few different classes of renderables, particles vs skinned objects vs rigid vs whatever, that's still just a handful of renderable components that are easily tracked, which can all be transparently assigned to whatever entity you choose. Physics are likely even more generalizable.
Things will likely break down if you assign a ton of physics components to a single object, which is to say that not EVERY component can be put on one object with the expectation of them all just working things out, but the point is flexibility, not being completely idiot-proof. You still have to consider component interactions, and in general the idea is that some components expect that only one of its class of component will be attached at a time - if a given component wants to "own" a particular chunk of data that lives on the entity, like the name of an attached mesh resource or the entity's physical position, ideally you will have attached nothing else that also tries to own that data. There's also a balancing act with how aware a component is allowed to be of other attached components, with one extreme being no communication allowed aside from messaging and data putting/reading, the other extreme not looking much different from never having used components in the first place. Generally, you want to start with isolated components, and only skew the other way as you note performance problems (THROUGH PROFILING! DO NOT PRE-OPTIMIZE!) and rework your component interactions.
Your update cycle thus becomes something like this: your world manager says "update the world", and that update the world function likely has some specific order in which it updates components - physics needs to come before rendering, and so on - and for each class of component, it gathers a list of all entities with that component, and says "update your physics." So the entities run Update() on their list of components of that type. Not everything will necessarily update that way - rendering probably keeps track of meshes directly and renders them directly, with rendering components just updating their mesh's information during their Update() step, and the actual physics-sim portion of physics would do likewise - but much will.
If you're still not getting it, let me give a practical example of how this might work:
You have a mobile animated creature. It has a skinned mesh component, and a mobile physics component, and a movement AI component. The AI component calculates a movement vector and puts it into the entity's data store, the mobile physics component takes the movement vector data from the entity and physically moves the entity along (and adds current velocity data to the entity), and the skinned mesh component determines if the object is moving based on velocity and either does a walking animation or a standing animation depending.
You have a second entity, which is a trigger physics component and an explosion AI component. The trigger physics component defines a bound you can walk through, it just waits for a collision, which sends a message to the attached-to entity when anything collides with it. The explosion AI component waits until it gets a collision message, waits 5 seconds, and then does a collection (by asking the entity manager) for any nearby entities - and to each, sends a physical impulse message.
The player, walking in the circle, eventually enters the explosion region, and then 5 seconds later gets the physical impulse message. Its render and AI components ignore it, but the physics component recognizes that message, and applies the requested physical impulse - launching the player up and away. The player lands, and then continues walking in a circle as defined by the other two components.
Done. A complex interaction broken down by components, that can be varied in a hundred different ways while writing barely any new code. You could change the way the entity responds (maybe the player's AI ignores the explosion message, whereas an enemy's AI responds by running away - but all you do to do that is change the enemy's AI component), the arguments to any of those messages, etc, and nobody has to go editing 20 different entities by hand to do it.
Now there is another important aspect to components - namely that they easily allow for your engine to be data-driven. However, that is a topic for another posting, so... for the moment, just stick with what we've got here.
[see more from Megan Fox at her primary site, Glass Bottom Games]
|
The pattern you're talking about is just as easily referred to as the case for multiple inheritance: you want class A to have behavior B but not behavior C, and class D to have behavior C but not behavior B but they both share behavior E, etc... The common solution to this is to implement the different behaviors at higher level parent classes, and have them inherit as they make their way down. In languages such as C++ where multiple inheritance is supported, it's possible to inherit AIBehaviorOne and RenderingMethodTwo from two different and unrelated parent classes, which accomplishes more or less the sort of requirements you have here. For languages which don't support this, on the other hand, there are Interfaces, and you could just as easily write a Class that contained an IArtificialIntelligenceHandle and an IRenderer, which you could specify in a text file if the language supported reflection. You only implement the logic for any individual component once, and create and use it just as your Entity/Component model describes.
Inheritance vs. Aggregation is a very sound and useful distinction to make--there are a lot of cases where inheritance alone won't do you any favors, but it's important to remember the tradeoffs you make when deciding between the two, as one is not universally better than the other. In fact, in general it' s not only a good idea, but very useful to have a bit of both.
Inheritance produces (perhaps enforces, even) a clean hierarchy; when you derive from a parent class, you know exactly what you're making, and exactly what it's supposed to do. In any situation where you're designing a runtime library or such, for instance, you want people who derive from the IOStream class to get compiler errors when they forget the write() function, and for them to be able to trust that the IOStream parent class handles Allocate() on its own constructor for them. A well-designed object-oriented architecture is easier to understand, maintain, and extend.
Aggregation allows for far more freedom, since you're assembling different behaviors into one class, but this means that someone who's trying to use your code has to actually see each component (and all its variations), which components your class contains, and understand how your class uses them together to do what you want it to do before they can start writing their own classes. Aggregation makes it more difficult to understand how every individual piece works together, and simultaneously requires anyone trying to work with the code to do so instead of abstracting it away for them. In many ways some object-oriented design is required to reduce the code into cleanly isolated components--just like poorly implemented inheritance introduces unnecessary boilerplate and can causelarge architectural change, aggregation that isn't well thought-through will confuse and distract from the interesting parts of the code in large part because of the added flexibility.
For instance, the classic Model-View-Controller pattern, which handles the same problem you're talking about, aggregates and inherits in an intuitive, logical fashion without eschewing one or the other quite so passionately as you seem to be doing: the model represents everything in the world, updating as time passes, the view renders the images the player sees, and the controller manipulates the world according to player input. A game engine could aggregate a Model, View and Controller without any knowledge of what they are (and you can create alternate engines by swapping them in and out), while implementations of IView could inherit the basic draw APIs so that every View would draw a blue rectangle the same way without copy-pasted code. The two are not mutually exclusive, so it's perhaps a good idea to not mislead the reader into abandoning a tool in the programmer's toolbox for another.
I don't disagree with what you have written, I'm a strong supporter of good programming practices and methodology. However there is a reason why components have come into existence and supplanted the traditional inheritance model for game entity's, to put it simply the old way doesn't adapt well to changes in design.
I was wandering what sort of complex entity inheritance system have you worked with in the past? Just some rough breadth and depth numbers. Also what sort of game was it and how much did it's inherited structure change from beginning to end?
Thanks
Michael
The above noted example could be gracefully solved with refactoring common code into common classes and aggregating them. But then that would start to look allot like the entity/component model...
The streams in the small subset of the code I work with have four layers of abstraction, each with different and important behavior, and a whole gamut of components mixed and matched in as needed. After the several hours it took for me to really understand the what was going on, I've come to the opinion that sufficiently complex code warrants incredibly complex design. Complex code can be elegant, to be sure, but I don't know of any way to put together a truly large number of features without combining a series of simple designs into a complex network of dependencies. That said, I'm all for aggregation when inheritance produces classes you don't use, and a stout believer of inheritance when you have a function you can refactor out of four places into one.
Sadly, I've never been paid to write a game engine; my own implementations (I wouldn't be here if I wasn't at least an aspiring hobbyist) took the MVC pattern to heart, and I believe that if I hadn't distinguished the engine into the aggregation of a Model, a View, and a Controller, the inheritance hierarchy would've changed even more than the several dramatic overhauls I put it through. I chalk that up to inexperience, myself, but I do stand by my belief that it's a good idea to inherit when you can, and aggregate when you need to.
I'd be happy to talk with you further about you experiences--I am by no means a veteran of any field, and am open to any potential error in my ways.
Thanks again,
Jonathan
Example:
Favour cats.meow(): dogs.bark(); monkeys.howl() to animals.communicate();
I would love to read any article you might know of that talks about simplifying how I write cache-friendly, multi-threadable components. Or is it just that "if I want it to run faster, I have to do more work per component"?
Over the years, I've gone through a number of different implementations of components from pure 100% data driven, to compile time configuration and even a hybrid system with the old style entity heirachy with attached components. Each implementation has there own good and bad points which was a driving force behind their development. Anyway, anyone that is looking to experiment or get their heads around components and inter-component communication I would highly recommend having a play with Unity3d which implements components in a way that makes building a game entity a pleasure.
Now the entity/component model addresses very well the Single Responsibility Principle and solves it quite elegantly. But I have a critique about it, that is you bind the simulation to the presentation. Think about what you would do when you want to build a server? You definitely don't want to bring up some rendering or sound code on the server. Of course the trick here is to filter the presentation components out when instantiating. The problem is, can you grantee that the system even works properly without them? What is if you want your world to be "skinable"? Swap out the components on instantiation...
As Jonathan Jou points out, Model View Control might be a better solution. (I look at a slightly different angle here.) The nice thing here is that MVC makes things clearer and is almost idiot proof. Let me explain.
Starting with the model, which is mostly simulation data and no behavior. Here you probably have either a very concrete inheritance hierarchy or a data driven model. You know what data makes an Orc.
The view is simple, a representation of the model. This can be audio only, 3D, 2D text based you name it. A game can have different views, each showing different types or even styles. The single camera problem many FPS have is a nobrainer to solve.
And finally the control. This is rather interesting, since you can define different controls for one type of model you can swap from AI to human controlled without changing any other part of the code.
With MVC you can trivially solve many problems. For example network synchronization becomes a really simple task, on the one end it is a view and on the other it is a control...
Of course you can have components on each part of MVC, which adds flexibility. I am thinking at something like Blender's Sensor / Actor system.
P.S. For the Car -> Engine inheritance you would fail my OO class; a Car HAS and Engine not a Car IS an Engine. Just to be a little nit picky here...
For server vs client, yes, the expectation is that your system is stable with components removed. So long as you maintain your components properly and separate responsibilities clearly and consistently, it shouldn't be a problem - but there are edge cases, of course. Often your picking will rely on your visual mesh, which itself comes from your rendering component, and perhaps you want some threadbare app on the server that supports picking for debug dumps, meaning your server might swap in a just-the-mesh component instead of loading the full renderable component (or you might use fancy #ifdef'ing within your renderable based on server vs client). You might also load a different physics comp, and so on... but still, it does work quite well, so long as you design your components with server vs client in mind.
The big problem I have with MVC is that it tends to be a coder-enforced and policed system - it tends inflexible to everyone but programmers, and does not play as well with data-driven development. Components, on the other hand, can be made to even hot swap at runtime, which is a huge, huge boon for iterative development, bug tracking, systems reuse by designers, etc. Like I said though, data-driven development is a different discussion, and probably the next article I'll do.
Still, to be honest, I've never used MVC in a large project. Maybe it's amendable to being data-driven as well, if you massage it just so? Do tell, if you've tried that. I'm also of the mind that components are far better for cache coherency and parallel processing, but that could also be a simple lack of experience with MVC on a large scale.
Really though, I didn't write this to toot the horn of components as The One True Path. I'm honestly on a bit of a functional programming kick right now, and am kind of inclined to think that THAT is more The One True Path. But, components are hugely prevalent, and if you go into games programming, you WILL eventually work on an engine that uses them... hence, this article - to just try and introduce folks to thinking in terms of them, and to get them familiar with the pattern.
Here is a problem to solve for you: You have one "state" and you want to show it in two different ways. For example a RTS that has a 3D view and a minimap. Now add a 3D tactical display where all units are represented by schematics. With a component model this becomes quite cumbersome, with MVC you just add more views.
If done properly you can make MVC quite data driven. The big problem here is tool support. You can't just export any scene graph and expect perfect results. You need a object/entity editor that first builds the model part, then offers the ability to build the corresponding views. Controls are probably done problematically, but can probably be also configured.
MVC is good software design, the problem is humans don't really think that way. Often secondary views such as a minimap are a afterthought in the design.
To your RTS example, the solution I would suggest is separating your data elements from your functional elements. That's also how I would suggest solving my own mentioned problem of picking being tied to rendering. Have components that do nothing but provide data - one that gives you a mesh, one that does just AI, the data-side - and then components that do nothing but operate on that data - so they find and render a mesh, they move you physically based on AI input, etc.
Your RTS example then boils down to a single entity with data exposed. You then attach 2 sets of components - 1 set takes that data and makes it make sense in the game view, the other takes that data and communicates with a minimap system to phrase it all in minimap terms (it uses the same position, probably, but might visualize vertical movement differently, it uses an icon instead of the mesh, and so on).
... which, in a sense, does start to resemble MVC, or even more so, functional programming. Data elements and transformers/operators, with strict separation between the two. Components are just a way of constructing the basic world hierarchy that happen to fit well with how people logically construct concepts, which makes it possibly a better fit for designers and artists, and might be easier to expose to them. Though as you say, there are ways around that for MVC too.
(for a personal example, we solved the minimap problem by simply having a minimap component that uses the position, and uses data from a few other components - like the overhead icon component - to visualize it in the minimap. Attach that component, that object starts appearing in the minimap. Disconnect it, it is removed / not shown in the map. There was some data duplication between the renderer and the minimap, but nothing heinous.)
Don't get me wrong, they're handy. C# gives you a great, great many of those handy little things "for free". They're just not unique to C# - C# is simply a slightly higher level language.
If you want to talk about languages that make phrasing entities easier, rather than just a language that gives you a few extra high level general tools, you need to go elsewhere - it could be argued that LUA has certain properties that make it easier, or SQL, both for how "classes" become "tables", and MEL script has some nice properties too for that matter. Unity's implementation of Javascript is another stand-out example (where messaging is a default interaction means rather than a separate system / every member function is a message listener), and UnrealScript has also been purpose-built to the task. But C#, eh, it's really just a higher level general purpose language.
C#'s real limit is being too much constrained to the Windows platform. It is VASTLY superior to C++, though not for system programming. Reflection, clean efficient enumeration and lambda functions can double or triple development speed and reduce the sources for errors by the same ratio. Unfortunately it does not support co-routines, which means it is not good for writing complex games.
It seems to me that any C# feature you listed has anything to do with the general design of the system. You talk about reflection and iteration concepts and just can't see what they have to do with it. True there will be some iteration but a properly designed system does not need any form of type inspection.
All should be clearly solved though polymorphism and delegation, no need for C#'s fancy language features to solve that. Many C# specific features, especially reflection, seem to me to favor worse design. Don't get me wrong, there is a validity in all design concepts of C#. The question is, is it the right tool for the task.
You talk of GetCount and GetItem, any system I build would never iterate over anything like that. Nor would it expose internal containers. Delegation is your friend here.
2. C++ vs C# is not a question of hate, but of role. C++ is for memory management, or hardware control. C# is for building complex applications on top of such APIs.
3. Language feature do effect the design of a system. You can always mimic a feature of one language with the features of another, but the point is not efficiently. Reflection is a perfect example. When you want reflection as powerful as C# in C++ it becomes a major hassle, messy ugly code. It needs to be done in the compiler. If you think otherwise then in my opinion you have spent too long in one language. One language may fit all, but it is a bad fit, a bit like a unisex jump suit for all sizes.
Though I'm still struggling to understand the concept... I am going to try re-reading the article 6 or 7 more times as I think I want to utilize this technique.
I think most modern game engines architectures have embraced the component philosophy for it's benefits, not only Unity ... Our team has made the switch 3 years ago, but we still face issues with people having trouble designing architectures in such a way.
I do, however, have some questions for you. Why do you need entities at all ? Can't components and the interactions they have with each other define the context of the entity ? Can't it be an abstract concept ? Why not go further and remove the entity concept ? Why store movement in the entity, when it could be stored in the physical object or within the transform matrix component ? The only purpose I see to entities is the container aspect they provide, which can be done by implementing a specific entity like component, which acts as glue defining this containment.
What I want to express is this : The entity is an hoax ! Why not express the entity itself as a component ? Why would you want to store data in the entity itself, and not the components ? Why should components need to speak with the entity in order to speak with another component ? Can't they get along without someone bossing them around ?
Our implementation does not have an entity system built-in as a aggregate of components. An entity, from the point of view of a game programmer, is only a component who knows other components and controls their interactions. The core of the engine doesn't know a single thing about the entity concept. He knows of component and knows how to enumerate them by a given type.
Our main loop itself is made of components, who execute sequential treatment on all instances of a given component type. Since all objects of a given type are treated sequentially, you have much less ICMs than if you have to ask each entity to call each update of each type. Also, since we control the allocation of each component, we can have those components who are to be processed sequentially stored in a contiguous memory region which helps tremendously when it comes to DCMs.
Don't get me wrong : I'm not saying that entities should not exist in concept. But they don't need to exist as part of the system. The entity IS a component, not made of components.
I hope this clarifies my earlier post !
Perhaps 'entity' is a poor choice of name for an object or class.
No, 6 is an idea or a concept. Yes, the universe is, and so is a specific dog; but dog itself is an idea/concept until you're talking about a specific instance of one. And yes, an atom making up my body is an entity.
So no, I don't think entity is a poor choice of a name for an object at all. Frankly, I don't see why that even matters, you're arguing over mere semantics.
There is still the concept of an entity, but also the concept of a system that operates over sets of components. Components themselves have no logic, it's all in the systems.
For instance, the rendering system declares that it is interested in entities with both the "Placement" and "Aspect" components. Whenever an entity is added to the world with both those components, it is added to the rendering system.
The rendering system is iterating through entities, but only requesting the Placement and Aspect components for each. Depending how you store the components, you could still achieve the benefit of less DCMs.
The use of "systems" also addresses issues with "which components need to talk to other components". No component talks to other components directly - all this logic is in the systems.
I'm wondering if anyone has opinions on this.
I think this also ties into some of the Entity-Component vs MVC discussion. This confused me at first, because I don't see it as a choice between one or the other. With a framework like Artemis, I think MVC essentially fits right into the Entity-Component framework. The Components are the Model, and the systems are the View and/or Controller.
There is still the concept of an entity, but also the concept of a system that operates over sets of components. Components themselves have no logic, it's all in the systems.
For instance, the rendering system declares that it is interested in entities with both the "Placement" and "Aspect" components. Whenever an entity is added to the world with both those components, it is added to the rendering system.
The rendering system is iterating through entities, but only requesting the Placement and Aspect components for each. Depending how you store the components, you could still achieve the benefit of less DCMs.
The use of "systems" also addresses issues with "which components need to talk to other components". No component talks to other components directly - all this logic is in the systems.
I'm wondering if anyone has opinions on this.
I think this also ties into some of the Entity-Component vs MVC discussion. This confused me at first, because I don't see it as a choice between one or the other. With a framework like Artemis, I think MVC essentially fits right into the Entity-Component framework. The Components are the Model, and the systems are the View and/or Controller.
The topic has been covered extensively, and I recommend reading the following article for a very comprehensible explanation and example implementation:
Game Programming Gems 6 – “Game Object Component System” – by Chris Stoy.
Unless you're trying to write a specific, very simple toy-game, such as pacman or tetris-like, component-based entities (aggregation) is the way to go.
I'm designing my first mobile title now and I'm really considering using a component based system over a hierarchical just to mess around a little. Either or would would work fine for this project.
So with respect to event/messaging between entities and sub-systems. You could still have an Entity class with all these aggregated components but have the Entity class inherit an IListener type interface to become an event listener? Then a central, singleton event manager is taking in messages from various systems and shooting them out to all respective listeners.
Or is event handling another component?
Thoughts anyone?