Sign in to follow this  
Charybdizs

Standard structure of a large scale game

Recommended Posts

Charybdizs    115

I'm not technically a new programmer. I've designed various programs, and games of a small scale, 2D and 3D, on various platforms. I've begun developing a game now using the Monogame platform, and it's significantly bigger than any of my other projects I've ever undertaken. Built into the XNA (and therefore Monogame) framework is the idea of the Game Loop. You know.

 

 

Initialization of a game

-

Loading the game's content

-

Update all the algorithms behind the game

-

Draw all things necessary to the screen

-

Repeat Update and Draw indefinitely until the game exits.

 

 

This pattern works, and works very nicely indeed, but for a small game. What about big games? Games that have segments that are very different from each other? The code for navigating a menu is very different from the code for playing pinball, and the code for playing pinball is very different from the code for adventuring around a 3D field. And yet, many games contain all these types of activities, and more. How are different states of a game best handled? Especially when each different state may be very big in size, and require loading resources of its own when it begins? I can think of several ways to do this. One would be a switch.

Update()
{

                switch(gamestate)
                {
                                case Menu:
                                
                                // here update all things pertaining to the menu state

                                                break;

                                case Pinball:
                                
                                // here update all things pertaining to the pinball game state

                                                break;

                                case Adventure:
                                
                                // here update all the computation for a 3D world and player controlled avatar

                                                break;

}

Draw()
{

                switch(gamestate)
                {
                                case Menu:
                                
                                // here draw all menu objects

                                                break;

                                case Pinball:
                                
                                // here here drew the pinball board, ball, scores, all that jazz

                                                break;

                                case Adventure:
                                
                                // here draw the 3d world and everything in it that is relevant

                                                break;

}

But this seems REALLY messy, especially when a game (for example take something like mario party where it has dozens of minigames) may have LOTS of different states, all with very unique code. And it gets even more complicated when you realize that Update and Draw are happening every tic, and there are things you may not want to happen EVERY tic. Like specific things that only run at the beginning of a new state. A case in point might be something like the splash screen before the pinball game. There's nowhere to put a "ShowSplashScreen()" method where it wouldn't be part of the code that runs every tic.

 

This is probably a lot more difficult than I'm making it out to be, but I can't think of anything within the realm of good coding practices to overcome this problem.

 

The tl;dr is this: How do YOU structure your games? What's the common, the traditional, or better yet, the best system for handling a game which contains many different activities, and has to load/unload resources according to what it needs for the task at hand?

Share this post


Link to post
Share on other sites
Ashaman73    13715

One way of handling this is to use components. Eg create a component interface like this (pseudo-code):

interface GameComponent
{
   init(List<GameComponent> allComponents);
   update();
}

Then each component implements this interface (physics, input,rendering,gamelogic,ai,sound components etc.) and is initialized, afterward call the update methods of each component per frame, something like this:

// create components
List<GameComponent> componentList=...;
componentList.add(new SoundComponent());
componentList.add(new InputComponent());
componentList.add(new RenderComponent());

// init components
for(GameComponent currentComponet : componentList) {
   currentComponet.init(componentList);
}

// update loop
while(gameIsStillRunning()==true) {

  // update all components
  for(GameComponent currentComponet : componentList) {
     currentComponet.update();
  }
}

// clean up
...

Share this post


Link to post
Share on other sites
Krohm    5031

Try considering games such as Quake 1. You will see they were considerably more homogeneous in mechanics.

 

Special cases in code are... I wouldn't say they are canon, but they happen and sometimes they save the day. I recall of someone admitting they shipped a game with code such as:

if(levelIndex == 5) {
    entity[12].position.y += 1.0f; // because it doesn't get placed correctly for some reason and we really need to ship!
}

Of course it is bad practice.

 

What changed from old games to new games? They become more data-oriented and, as they grown more complicated they embraced scripting.

 

While my "full scale" game has been in long-term cryo-stasis for a while, I still think its design was really well tought-out. It had no gamestates at all. Everything went through something similar to components and the engine knew nothing about the relationships between those components. Those were provided by gameplay-specific code by scripting.

 

 

Now, I'm not suggesting you go scripting right away. It involves complicated things. Data oriented design could still get you far.

 

Example: we have a FPS with hit-spacebar to action mechanic.

We enumerate the "actions" required. Doors open. Buttons get pushed. Pinballs gets played.

When loading up the data, we find an entity using action[2]. We create a PinballAction object and attach it to the entity.

PinballAction will be very complicated (push current keybinding, set new keys, set new camera parameters, activate the complicated pinball simulation with the newly bound keys... ).

That still requires a switch to instance the correct class, but that switch is no more in the "live" parts but rather in the loading paths (which are hopefully simpler).

 

If you go scripting, you save that switch as well. Of course, if your actions are simple going scripting will likely be not worth it.

Share this post


Link to post
Share on other sites
noizex    1437

That's a question I was going to ask many times myself but can't even phrase it clear enough. I started with small project but now it's grown, has over 300+ classes and client-server architecture. More and more I'm finding myself pondering how to approach something in terms of data-object interaction and it takes many hours to figure some things. For example, I have network synchronized modifiable terrain. I needed it to synchronize when player moves and changes grid position, but I didn't want to clutter player movement logic (which is tricky in itself, as it's over network with authoritative server) so I created some TerrainDb class that receives network messages and deals with response and updating terrain caches. What is my major problem is relation between different systems and classes. Then it turns out such class needs some networking access to receive/send data to server, requires access to World/Terrain and so on. And it starts to get messy already.

 

Top game logic is handled by GameState, each of which has access to several main objects (like Connection, ScriptingEngine etc.) but most of these states are simple (Login, LoadingScreen, Character menu) until I get to "gameplay" state. This is where so many things need to happen, so many systems need to be initialized and propagated that I have no idea how to design these relations. Should I initialize them all in state's Init() func, making it 100-200 lines long? Pass dependencies to constructors? So far I avoided singletons and I have not a single one, but the net of class relationships becomes very hard to maintain. 

 

While I'm not making an "MMO" but way smaller networked "sandboxy" game, I checked Ryzom/Planeshift sources and several others that had sources available. They all seem to be just as messed and without deep knowledge you have no idea what's going and and can't even track relations. So many weird classes initialized in weird places, passed around. Is that what every project ends up as? I admit I saw WarZ source code too and was terrified - 200kB classes that do every freaking thing from rendering to physics all messed up and mixed, not even divided into sections of similar theme. 

 

Mind that I'm talking about lower level and unique things - terrain, networking, scripting etc. I don't think this can and should be done as components (terrain itself is really unique, which would lead to a special case game object that has really unique components and has systems that work just on that one object which seems silly). Component systems seems more like game logic where you fit all pieces together, but this requires some bigger things to exist first. I don't really think I could fit everything in Component-System manner. 

 

So anyone has any pointers where such design could be learned? Is it just experience? Everyone starts talking about game loop and game states but these things are nothing compared to vast net of systems/services/"managers" required for a more complex game. 

Edited by noizex

Share this post


Link to post
Share on other sites
Serapth    6671

Since you are already looking at organizing your game into states, run with that.  Phaser actually does a really good job of using State as the fundamental building block.  I did a video tutorial on creating a complete game with Phaser/Typescript a little while back, that clearly shows how the state driven approach can work.   

 

At the end of the day though, there are multiple different ways...  State is certainly one model people use.  A component or data driven design is also quite popular.  A stage/actor model is common enough too.  Really it comes down to your preference and the type of game.

 

https://www.youtube.com/watch?v=T8a8-SO6vP0

Share this post


Link to post
Share on other sites
CC Ricers    1491

Splitting your code into game states also helps clear your mind more in dealing with a subset of game logic instead of sifting through code which can jump from mode to mode. Like keeping the core gameplay code separate from menu code, code for mini-games, etc.

 

Bigger games also typically need more careful resource and memory management. You would have a set of functions or classes that are made to load different types assets into the engine, and different data structures to pool them in memory and keep them efficiently organized.

Edited by CC Ricers

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this