How to Structure a Game

Published May 28, 2013 by Josh Klint, posted by Josh Klint
Do you see issues with this article? Let us know.
Advertisement
Basic game structure is a topic that many people have trouble with, yet it somehow gets overlooked. In this lesson, I will show you exactly how to set up and structure code for commercial games. We're going to use Leadwerks 3 in this lesson because it makes C++ game development faster, but the ideas here are applicable to any programming environment.

Direct vs. Component Programming

Although the component-based script system Leadwerks supports is a convenient way to get simple demos running quickly with Leadwerks, it can be limiting when you try to make a full game. Fortunately Leadwerks supports direct programming, in both C++ and Lua. This gives a lot more power and control than component-based systems. To really take advantage of this power, we need to understand some basic principles on how to set up and structure our game.

Class Structure

We start with a base class for all objects in our game. We'll call this the Node class, and derive it from the Leadwerks::Object class. The Node class is not an entity, but it has an entity as a member. Think of a Node as your own game object that is associated with an entity. For this lesson we'll create an imaginary class called Foo derived from the Node class. The Foo class could represent an enemy, an NPC, a bullet, a grenade, or anything else. We can use the same structure for all of these things. The Foo class has one function called Update. This is where all our game code that updates that single instance of this class would go. This code could control the trajectory of a bullet, the AI of an enemy, or anything else. The point is all the code that controls that object is compartmentalized into this function, and it gets called over and over again, for each instance of the class. In order to keep track of each instance of the Foo class, let's use a standard C++ list. This is listed in the header file as a static member, so that each instance of the class can access this list: static std::list list; Each instance of the Foo class will also have a list iterator so we can remove it from the list when it's deleted: std::list::iterator it; In the Foo() constructor, the object will add itself to the list of all objects in this class: Foo::Foo() { list.push_front(this); it = list.begin(); } And in the destructor, we will use the iterator to remove the object from the list: Foo::~Foo() { list.erase(it); } WARNING Removing the iterator from a list can cause a crash if this object is deleted while your code is iteratoring through a list. To avoid this problem, you can add the object to be deleted to a queue of objects to be deleted in your main application loop. However, this is beyond the scope of this lesson, which is only meant to convey a simple program structure. Why do we need a list of all the instances of our Foo class? Well, this means we can now iterate through each one, at any point in our program. This is very powerful because it means we can create new instances of the Foo class at any time, and our game will adjust to keep them all running, without hard-coding a lot of specific behavior. Iterating through the list is done with the following code: //First we declare an iterator so we can cycle through our loop std::list::iterator it; //Loop for (it = Foo::List.begin(); it!=Foo::List.end(); it++) { //The Foo object is gotten with (*it) (*it)->Update(); //Alternatively, you could declare a Foo* variable and set it to this value //Foo* foo = *it; //foo->Update(); } This code should go somewhere in your main game loop. You'll end up with a loop like that for each class your game uses, if it's a class that needs to be continuously updated. (Some types of objects can just sit there until something happens to make them react. For those situations, I recommend using a collision callback or other means to activate them.) So your main loop will look something like this now: bool App::Loop() { std::list::iterator it; for (it = Foo::List.begin(); it!=Foo::List.end(); it++) { (*it)->Update(); } world->Update(); world->Render(); context->Sync(); } We're going to do one more thing to make our code a little cleaner. Because we'll probably end up with a dozen or more classes by the time our game code is done, we can take that loop and put it into a static function: void Foo::UpdateEach() { std::list::iterator it; for (it = List.begin(); it!=List.end(); it++) { (*it)->Update(); } }

The Main Loop

Our main game loop becomes a little easier to manage now: bool App::Loop() { Foo::UpdateEach(); world->Update(); world->Render(); context->Sync(); } By the time our game is done, the main loop will look something like this: bool App::Loop() { Enemy::UpdateEach(); Player::UpdateEach(); Projectile::UpdateEach(); world->Update(); Explosion::UpdateEach(); world->Render(); context->Sync(); } You might wonder why I didn't just create a list in the Node class, and have an Update function there. After all, any class derived from the Node class could override that function, and a single loop could be used to update all game objects. There's two reasons we don't do that. First, not all of our game objects need an Update function to be called each frame. Iterating through hundreds or thousands of unnecessary objects would hurt our performance for no good reason. We can put an Update function in a base Enemy class, however, and have both goblins and trolls use the same Update loop, when Enemy::UpdateEach() is called. Second, we want to control the order and time at which each class is updated. Some classes work best when they are updated at the beginning of the loop. Some work best when they are updated between the call to World::Update() and World::Render(). It's different for each class, depending on what you make them do, and we want to leave room for ourselves to experiment and not get locked into a design that can't be easily changed when needed. We could try working around this by setting a priority for each class, so objects are updated in a specified order, but I wouldn't do this. In my opinion, this is the point where your structure is done and you should think about structuring the classes for your game, and filling in their code. So what does the Foo:Update() function do that's so important, anyways? Foo::Update() presently does nothing, but it does everything. This is where your game code goes. We can use this structure for AI, bullets, rockets, explosions, enemies, tanks, planes, ninjas, pirates, robots, or giant enemy crabs that shoot laser beams out of their eyes. In fact we can also use the same structure for those laser beam objects the crab is emitting!

Conclusion

The main point of this is to show how to graduate from writing simple single-file demos, and to start thinking in terms of classes. Your game code should be written in such a way that it doesn't matter how many objects there are of each class, when they get created, or when they get destroyed. Each class Update() function is written from the point of view of that single object, looking out at the world around it. This simple concept can be used to make just about any type of game, from first-person shooters to MMOs. The image for this article was provided by oppenheimer.
Cancel Save
0 Likes 14 Comments

Comments

Josh Klint

We also provide a system of hooks. I recommend making use of the Collision and UpdatePhysics hooks, when appropriate. There is an UpdateWorld hook is available, and does pretty much the exact same thing we set up here. You could use this to control the Update() function for all objects, but you may find you need control over the time and order in which objects are updated, for the reasons described in this article.

May 03, 2013 03:59 PM
jsvcycling

Great article! Very well written!

May 10, 2013 02:45 PM
Puyover

Very good article, I have to read it more in deep.

May 10, 2013 03:09 PM
Cygon

From the title, I was expecting an article that discusses high level architectural patterns, whereas the article mostly is a tutorial on the basic usage of an std::list. I think "How to Structure a Small Game" or "Basic Game Loop Design" would better convey the contents of the article.

The article also displays several undesirable patterns, such as usage of globals, static methods with side-effects (threading!), duplicating functionality (Enemy::UpdateEach(), Player::UpdateEach(), Projectile::UpdateEach()) and bool returns (the least helpful error handling choice),

If, as it seems, the article is targeting absolute beginners, I would also clarify this part of the article a bit: "This is listed in the header file as a static member, so that each instance of the class can access this list: static std::list<Foo*> list;". Without indicating that this is supposed to be part of the "Foo" class, someone might put that as-is in his header and be surprised when each source file gets its own, independent list. Or move it into Foo.cpp directly, though in all cases the mutating global state issue remains.

You example code also includes a "MyGame.h", which likely was renamed to "App.h", unless "MyGame.h" is somehow part of the Leadwerks Engine (btw. the "Object" base-class-of-everything in it is another thing I'd strongly discourage versus writing reusable, independent code that can be isolated for testing).

In overall, I'd wish for a bit more substance and I think the design presented could use some input from someone with firm OOP / design knowledge, even if it is aimed at beginners.

May 10, 2013 03:25 PM
Fahmim Rezuan

Very good article, im going to study more about this.

May 10, 2013 05:07 PM
NightCreature83
This static list and erasing from the Destructor is really worrying, if an object in any of your lists decides it needs to be erased during an update you have just invalidated the Foreach loop and you will crash. You are better of having a gameobject manager or something that takes care of creation and destruction of game objects. This will also clean up your main loop as you only have to call the game object manager update function there.
May 12, 2013 11:18 AM
rip-off

To me, this article starts off by reading like a "Leadwerkz" marketing piece. When we get to the main content of the article, Leadwerkz becomes irrelevant to the points it tries to get across.

The Node class is confusing, as it does not appear to be used in the example program. The author does not make the purpose of the class very clear.

The structure the author proposes relies heavily on poor practise, but the article does not address the downsides and limitations of the structure.

I think the article could benefit from more concrete examples, rather than talking about abstract game objects such as the "Foo" class. In particular, the example program doesn't really demonstrate the concepts as it does not dynamically create or destroy game objects.

Even a trivial example "game", for example balls bouncing around the screen that are split into two smaller balls when clicked (e.g. destroy original ball, add new new balls) would give some much needed clarity about how this structure could actually be used in a real game.

May 12, 2013 01:21 PM
Josh Klint

This static list and erasing from the Destructor is really worrying, if an object in any of your lists decides it needs to be erased during an update you have just invalidated the Foreach loop and you will crash. You are better of having a gameobject manager or something that takes care of creation and destruction of game objects. This will also clean up your main loop as you only have to call the game object manager update function there.

Yes, this is one of the challenges to watch out for. Your suggestion of adding objects to a cleanup list is one we have used, but I wanted to keep this article simple. The intended reader is someone still working in the "one-page demo" mentality.

The article also displays several undesirable patterns, such as usage of globals, static methods with side-effects (threading!), duplicating functionality (Enemy::UpdateEach(), Player::UpdateEach(), Projectile::UpdateEach()) and bool returns (the least helpful error handling choice),

I have no problem with static members, when they are appropriate. They're not a problem unless you plan on having two instances of your game running side-by-side in the same application. The reason for the separate functions was clearly explained. You could put it all in one function, if the order of updated classes does not matter to you. I prefer booleans to return success/failure. I can never remember detailed error codes, and it's simple enough to debug the program if something goes wrong.

The structure the author proposes relies heavily on poor practise, but the article does not address the downsides and limitations of the structure.

This article was written for beginners who are still stuck in the "one-page demo" mentality. If you have an alternate approach to suggest, I would like to hear more about it.

May 15, 2013 05:18 PM
NightCreature83

This static list and erasing from the Destructor is really worrying, if an object in any of your lists decides it needs to be erased during an update you have just invalidated the Foreach loop and you will crash. You are better of having a gameobject manager or something that takes care of creation and destruction of game objects. This will also clean up your main loop as you only have to call the game object manager update function there.

Yes, this is one of the challenges to watch out for. Your suggestion of adding objects to a cleanup list is one we have used, but I wanted to keep this article simple. The intended reader is someone still working in the "one-page demo" mentality.

But you are teaching them something that just will not work and will confuse them when it actually crashes due to removing a dead player or monster object in an update. STL debugging is not a beginners task especially not with that complexity in the code.

Static members are bad OO design you should shy away from them as much as you can, the only decent reasons for them are deserialise tags that won't change for any class instance. I hate any game or application that doesn't allow me to start another instance of it on the same machine, especially during development.

The point we are trying to make is that it is fine to do a tutorial for the one page demo mentality beginners. However it is not fine for them to learn bad practices from the get go, it is really hard to unlearn those so it is better to learn them the proper structure from the beginning.

May 16, 2013 10:02 AM
Josh Klint

I hate any game or application that doesn't allow me to start another instance of it on the same machine, especially during development.

It seems silly to me to make design decisions around a theoretical situation (unless you actually will use that), but I have my own preferences as well.

I find Class::StaticMember a lot better at keeping code encapsulated than making that static member a member of some kind of manager class. I prefer to keep code self-contained in classes, and see static members as a way of doing this.

I did add a big warning to the bit about the object destructor. It sounds like you use another structure other than lists. For rendering and retrieving objects within a certain area, you can use an octree or other structure, but ultimately you need some global list of all updateable entities. How would you suggest going about doing that?

May 18, 2013 03:10 AM
NightCreature83

Have an object manager that manages all the interactions with deletion, creation and updating of the entities. This way you can guarantee that you will never do a delete or insertion during an update and you will not invalidate your iterator.

Objects should never manage their own lists they don't have enough information to govern themselves that's part of the system they belong to and not the object themselves.

May 27, 2013 10:03 AM
Alpha_ProgDes

    Enemy::UpdateEach();
    Player::UpdateEach();
    Projectile::UpdateEach();

    world->Update();

    Explosion::UpdateEach();

I'm curious as to why Explosion is updated after the world update and not before?

May 28, 2013 02:25 AM
SuperVGA

I never picked up the pencil to write an article like this, so it's easy for me to sit here and think the overall approach seems a bit counterintuitive and a bit rigid.

Maybe you will elaborate on further distributing the code in a later article? I guess overall it does show a way to code something, I just wouldn't call it "structuring a game" - that title is too general.

Perhaps something along the lines of "altering lists", "using inheritance for game components" with "lines you can use in the main loop".

I'd involve source file organization and talk about ways to divide the project into layers or subsystems. Perhaps encouraging parallel processing (don't know whether your article is meant for beginners or not)

and talk about a few design patterns they can apply to connect the separate parts.

That is in my opinion much more fundamental to structuring a project, while this is basically just a mixed C++ tutorial.

(This is why I don't agree with rip-off that the examples should be more concrete: Concrete is for specific programming tutorials. If a person wants to read your article it's likely because they have difficulties managing all their files or actual project structure, rather than wanting to learn about std::list. I think it's better not to tell how the structure should be, but elaborate on a few general ways to structure a project.)

Also, class Foo does not contain a member named List, yet you refer to it in App::Loop() :


...
Foo::List.begin();
...

Perhaps you're referring to a list of Foos inside the App class?

How many App instances will there be for us to benefit from having that list as a static?

May 28, 2013 06:33 AM
serumas

I think its a bad article for novice like me, becouse there is no explanaton

why use static list inside each object rather than use

simple std::list<Foo> list; what is real benefits of it?...

ghtCreature83 is right, there is big chance to dissopoint novices with this unsafe and creashy design.Dont you think?

why std::list, not std::vector?

Im shure its very possible to revalidate iterator after concrete deletion inside update loop.So why instead use deleteques?

So thats my questions....

May 28, 2013 12:04 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!

Basic game structure is a topic that many people have trouble with, yet it somehow gets overlooked. In this lesson, I will show you exactly how to set up and structure code for commercial games.

Advertisement

Other Tutorials by Josh Klint

Advertisement