Sign in to follow this  
Followers 0
DF Oliver

Programming Update 7: Bluebot's Dilemma

150 posts in this topic

It’s has been a long time since the last programming update, but now the wait is finally over. This time we are going to take a deep dive into software design and take a close look at how we structure our gameplay code. This post will be a bit more ‘hardcore’ than the previous updates, because it requires some basic understanding of programming paradigms (mostly OOP). We have a lot of content to cover, so let’s get started!

The basic question we want to answer is how to optimally represent the world and its containing objects in terms of code modules. This is an extremely important subject and has a lot of implications not only on how we implement game-play logic, but it also affects the way our tools work and therefore how the game is authored.

Let’s look at a specific example. It’s time to meet Redbot’s good friend Bluebot!

1_Meet_Bluebot.png

Bluebot is somewhat depressed because he is bored and is looking for a new occupation. So he asks Redbot for help. His friend immediately starts to look for a good solution. After some searching the internet for possible solutions Redbot remembers two items he picked up while spelunking many clock cycles ago: a fashionable top hat and a binary broom (used to create neat memory heaps).

He offers the hat and the broom to Bluebot who happily tries them out.

2_Broombot_Hatbot.png

Now how can we represent the different versions of Bluebot in terms of software design? Since Object Oriented Programming is widely used these days we can look at a possible solution expressed as a UML class diagram [http://en.wikipedia.org/wiki/Class_diagram].

3_Single_Inheritance.png

But what do the boxes and lines mean? A class diagram essentially shows the relationships between different classes in a system. In this (simple) example the triangle-arrows points at a shared base class. Redbot is a Robot and so is Bluebot. That means if the class Robot describes a certain behavior (e.g. walking around, robot dance, …) then its subclasses inherit this behavior. This means that both Bluebot and Redbot can do whatever a (generic) Robot can do plus some additional actions described by the specific subclass. In order to add the extra functionality for the hat and the broom we could simply create two new subclasses: Broombot and Hatbot.

So are we done? Can we describe any object in our game world like this? The answer is yes and no. You could certainly write an entire game like this (and it has been done), but it get’s messy very quickly when you want to combine functionality. For example what if Bluebot wants to wear the hat while using the broom? Here is a picture of Bluebot as a Janitorial Gentlebot!

4_Bluebot_Costume.png

While Bluebot is very happy with his new occupation representing this situation with class inheritance is messy. Let’s look at the UML diagram for the Janitorial Gentlebot.

5_Multiple_Inheritance.png

So both Broombot and Hatbot inherit behavior from Bluebot and the Janitorial Gentlebot combines the behavior of both classes. This is called “multiple inheritance” and is generally frowned upon by software engineers because the child class needs to explicitly describe which flavor of a certain behavior it wants to inherit. For example if the class robot defines a function called Dance() and both the Broombot (e.g. robot pole dancing) and Hatbot (e.g. Michaeal Jackson dance style) override this behavior then it isn’t obvious which one of the two flavors or Dance() the Janitorial Gentlebot should use.

So how can we improve this and come up with a cleaner solution? Instead of using class inheritance to describe the functionality of an object we use object composition. This model is known as the ‘Entity system’ in the games industry and is widely used these days. But how does it work?

In this paradigm an object in the world is called ‘Entity’ and it is essentially only a container for the functional units called components. But let’s look at our Janitorial Gentlebot dilemma using the Entity/Component model.

6_Prototypes.png

This diagram might look a bit confusing at first, but it is actually pretty simple, so let’s have a closer look.

In the upper part of the diagram you can see the different components (their names are prefixed by “Co”), which describe a certain type of functionality. For example CoJanitor describes how to use a broom whereas CoGentlebot contains the logic to wear a top hat (with style). As you can see we can still leverage the power of class inheritance. In this example CoRedbot and CoBluebot are both subclasses of a generic CoRobot component and therefore inherit its behavior.

At the bottom of the diagram you can see the different entity types that could exist in the world. In UML the diamond shaped arrow represents a composition relationship or in other words the arrow points at a component owned by a specific entity.

The real power of this paradigm stems from the fact that it is very easy to mix and match behavior. For example the entity Broombot combines the functionality of CoBluebot and the CoJanitor (green dotted arrows), which means that Broombot essentially is Bluebot with the additional functionality of a janitor. What is really cool though is that it’s very easy to cleanly represent the Janitorial Gentlebot because this specific entity simply combines the behavior of CoBluebot, CoJanitor and CoGentlebot. Awesome!

Another really nice feature of this model is that interaction between entities can be implemented cleanly by inter-component communication. Let’s say we want to implement Broombot in such a way that he automatically organizes nearby memory fragments into heaps. So all we need to do is to add a CoFragment component to the Memory entities in the world. The behavior in CoJanitor could then search for entities with a CoFragment component in a certain radius and combine them into a heap entity.

7_Communication.png

This means that no other component in the Broombot entity needs to know about memory fragments, which makes the code much cleaner and easier to reuse. This is called decoupling and considered to be a very favorable quality in software design.

If the memory fragmentation becomes too bad then we might need to use multiple Broombots to clean up all that mess. Of course we want the different bots to have a slightly modified behavior (e.g. they should all start at unique locations in the world), but how can we achieve this using the Entity architecture?

It’s pretty simple actually because components can expose attributes which are configurable on a per-instance basis. Very often these different versions are created and tweaked by a game designer and then saved as a file. These pre-configured Entity types are called prototypes (or archetypes). So when we setup a scene we simply place prototypes in the world which then get instantiated into entities when the scene is loaded.

Of course all the different types of component mentioned above are (silly) examples and they don’t really exist in our code base. I want to end this programming update by looking at a few real-world components that we actually use in the game:

- CoRig: Loads a skinned mesh and creates the internal (C++) object that sorts and draws the geometry.

- CoAnimation: Loads animations that control the position and rotation of the joints of a rig.

- CoNavigator: Uses the navigation system to plan a path through the world and then moves the entity along the path.

- CoInventory: Manages the inventory of an entity.

- CoTriggerVolume: Checks which entities have entered or left a certain area in the scene. The trigger volume is the base class for more specific classes (e.g. scene-change triggers, …)

Well that’s it for today. I hope you guys enjoyed this post about the software design. My goal was to show you guys that the Entity architecture combines the advantages of class inheritance and object composition which makes it very powerful.

Thanks for reading and please feel free to ask questions about this topic!

Share this post


Link to post
Share on other sites

I would have never guessed the forum would contain basic OO design explanations! Nice primer for those who may not be familiar with programming.

Do you intend to share your actual full UML?

Share this post


Link to post
Share on other sites

Great examples and explanations! So much easier to understand than all those damn programming text books =D

Share this post


Link to post
Share on other sites

I feel like you've left one very important question un-answered: How does your component system solve our Dance() problem?

Share this post


Link to post
Share on other sites

Thanks for the interesting post.

Your real-world examples here mostly deal with game logic (managing entities, loading and manipulating graphics). Do you also use this component template for developing lower level systems, such as memory management? I'd imagine it might not lend it self well to a high degree of optimization.

Or do you not have to worry about the lower level stuff, since that's handled by the engine (Moai in this case)?

Share this post


Link to post
Share on other sites

@gsm: We would release it if we would actually use UML to plan our tech. In reality this rarely happens though and even if I end up planning a system in UML-lite the diagram tends to be out of date before the day is over :-)

@noelho: Thanks dude! I appreciate it.

@DiscoDonut: Good point. You are absolutely correct. The solution is that we would have a CoPoleDance and a CoHatDance component (which would inherit from CoDance) and the specific version of Bluebot would only use one or the other. Therefore the entity always "knows" explicitly which behavior to use.

@relativistic: This is used purely for game-play related code. The underlying engine code is a completely different beast. I could dissect Moai for you guys in another post if people are interested.

Share this post


Link to post
Share on other sites

As a person making a feeble attempt to teach himself enough to design simple games of his own, I enjoyed the way this post helped me see that there are different ways of taking advantage of inheritance, since I've really only used it to do very simple and straightforward class relationships like those you showed in the first diagram. Hmmm... will have to try something like this... *puts on crash helmet*

Share this post


Link to post
Share on other sites

@relativistic: This is used purely for game-play related code. The underlying engine code is a completely different beast. I could dissect Moai for you guys in another post if people are interested.

Definitely interested. Moai could really benefit from some great docs.

Share this post


Link to post
Share on other sites

@relativistic: This is used purely for game-play related code. The underlying engine code is a completely different beast. I could dissect Moai for you guys in another post if people are interested.

I would definitely be interested in this. This post is great - I'm getting so much value out of the updates on the forums!

Share this post


Link to post
Share on other sites

I am lousy at UML, but the arrow you're using in component diagrams looks like "has-a" relation. Is it? Basically, I am curious about the way you implement components in C++. Is it just by storing a list of components (i.e. objects with generic "Component" base class) inside each entity?

One of the articles on entity/component design makes a point that communication between components can be hard to get right (e.g., what should we do if component CoA requires data or functionality from another component CoB?). How do you plan to get around it?

Intro into Moai sounds interesting. Count me in.

Share this post


Link to post
Share on other sites

@relativistic: This is used purely for game-play related code. The underlying engine code is a completely different beast. I could dissect Moai for you guys in another post if people are interested.

I would definitely be interested in this. This post is great - I'm getting so much value out of the updates on the forums!

Seconded on this. Most of my programming lately has been higher level OOP stuff but I did almost all of my schooling in ANSI C and would love to see some dissection of production quality low-level code. Protip: The fastest way to become a pointer ninja is to program a few advanced data structures in C, nothing like passing a level 3 pointer (my friends affectionately call them "spointers") to give your head a few contortions.

Share this post


Link to post
Share on other sites

Wow. I understood most of that. Either you're an amazing teacher, or I should have been an engineer.

Share this post


Link to post
Share on other sites

Out of curiosity, why would you combine the classes (or inherit them in this way) instead of, say, making the broom and hat separate objects, that can be "used".

For instance, blue robot is still blue robot, but he has a collection of objects that he has equipped, and is able to "use" each of them. The use of an object changes his (in game) behaviour, as opposed to creating an entirely new class of object?

"Use"-ing the hat wile equipped might make him tip his hat. "Use"-ing the broom might make him find the nearest memory fragment and turn it into a neat heap?

Share this post


Link to post
Share on other sites

I think I understood that but it also pisses me off...

why must orogramming be more difficult than being a human.

If you hand a human a hat, and a broom and tell them to dance... they do it.

but a robot with programming, you need to spell out each and every step of that equation, throw in all variables and and lose your mind.

Luckily it's you losing your mind Oliver and not me... so you bring us computing and we'll bring smiles and PR.

Share this post


Link to post
Share on other sites

That was really clear and interesting, thanks! Now how can I use this knowledge in my FORTRAN77 codes...

Share this post


Link to post
Share on other sites
I think I understood that but it also pisses me off...

why must orogramming be more difficult than being a human.

If you hand a human a hat, and a broom and tell them to dance... they do it.

but a robot with programming, you need to spell out each and every step of that equation, throw in all variables and and lose your mind.

On the other hand it takes 9 months creating a human with the potential of doing this and even then it's more time until they will actually dance with hat and broom and several years if you'd like them to do a specific complex type of dance.

You also can't teach one human to dance and then apply it to thousands/millions of humans immediately, but have to start all over again.

And there are those pesky things called emotions and free will so a human may refuse to your request to dance with a hat and broom.

Conclusion: humans and robots, each suck in its own unique way :-)

Share this post


Link to post
Share on other sites

Great post! I have a couple of questions :D

How does a component 'know' which other components are available in its entity?

How do you facilitate communication between components (either in the same, or different entitys?)

In this model would you update the game world entity by entity or in batches of components (of the same type)?

Sorry for the question dump!, loving the in-sights keep up the good work!

Share this post


Link to post
Share on other sites

hmm, the beginning said that i should have some basics, but this is really the basics. i would also try to stay away from using metaphors, like "Memory" or "Heap", if they have some other meaning in programming.

however, a nice update anyways.

Share this post


Link to post
Share on other sites
I feel like you've left one very important question un-answered: How does your component system solve our Dance() problem?

Well when they call for the function dance they have to go through the loaded component. Therefore they have to chose which component to call the function through eg. CoJanitor.dance() or CoGentleBot.dance(). But even smarter (and more likely) there is a separate competent (not used in the examples) to control the bots gestures and the dance() function lies in there. That way you'd only have to call for that when you want to make the bot gesture, like so:

CoGestures.dance();

CoGestures.wave();

...and so on!

So CoJanitor/CoGentleBot doesn't hold any information about the gestures och movement of the bot, it only holds the janitor/gentlebot specific behaviors.

Hope that helped! :)

Share this post


Link to post
Share on other sites
hmm, the beginning said that i should have some basics, but this is really the basics. i would also try to stay away from using metaphors, like "Memory" or "Heap", if they have some other meaning in programming.

however, a nice update anyways.

The basics are needed to get the memory heap jokes ;P

Share this post


Link to post
Share on other sites
Great post! I have a couple of questions :D

How does a component 'know' which other components are available in its entity?

How do you facilitate communication between components (either in the same, or different entitys?)

In this model would you update the game world entity by entity or in batches of components (of the same type)?

Sorry for the question dump!, loving the in-sights keep up the good work!

I would guess that there has to be a Component loaded for the movement at all times. Therefore the program knows that there has to be a movement component loaded and it uses it. I'd think that this is true for most other components as well.

Some might have to check if it's loaded first eg. if(CoAttack = loaded){ //then do the attack things here, if not skip it } (**not real code i know**).

Another way might be to have an array or vector. That's basically a list that stores information. When you load a new component you add it in the list and then there is a loop which checks how many components are in the list and calls them all respectively. But for this to work all the components "main-call function" has to be named the same. example:

for(check and load item from list here)

{

itemLoaded.action();

}

Hope this cleared it up for you!

Share this post


Link to post
Share on other sites
I think I understood that but it also pisses me off...

why must orogramming be more difficult than being a human.

If you hand a human a hat, and a broom and tell them to dance... they do it.

but a robot with programming, you need to spell out each and every step of that equation, throw in all variables and and lose your mind.

Luckily it's you losing your mind Oliver and not me... so you bring us computing and we'll bring smiles and PR.

You could also think of it this way;

Our brain is a giant class that sorts out different components. Whenever we want to do something it calls the correct component(s). Clearly more advance than the code we can make but this was just to give you a different perspective :)

Share this post


Link to post
Share on other sites
hmm, the beginning said that i should have some basics, but this is really the basics. i would also try to stay away from using metaphors, like "Memory" or "Heap", if they have some other meaning in programming.

however, a nice update anyways.

The basics are needed to get the memory heap jokes ;P

heheh, yeh :).

Share this post


Link to post
Share on other sites
I think I understood that but it also pisses me off...

why must orogramming be more difficult than being a human.

If you hand a human a hat, and a broom and tell them to dance... they do it.

but a robot with programming, you need to spell out each and every step of that equation, throw in all variables and and lose your mind.

Luckily it's you losing your mind Oliver and not me... so you bring us computing and we'll bring smiles and PR.

Haha, I understand a little about programming but it's my collaborator who does most of it for our projects, since he's the expert. I can write scripts and stuff, but he does the heavy lifting (although weirdly I sometimes have a better intuitive maths-sense leading to the odd situation where I work out something tricky to do with angles and then he programs it in). This leads to conversations where I ask him to implement something that in my head seems easy, but is actually difficult or time consuming. Or sometimes, I ask him to do something that I think sounds complex, but then he does it in 5 minutes.

Share this post


Link to post
Share on other sites
Sign in to follow this  
Followers 0