How should I manage level progression?

In my code, I just have a Level class right now in the main game loop that just blits a background and spawns some enemies for testing purposes...But I can't figure out how to go about implementing multiple levels and level progression. What is the best way to manage multiple levels and level progression in a game?

I'm thinking that I would make the Level class just read level data from a file, and create a Level vector...But I'm not sure if that's the best way to do it.

• Are you wanting to go from level 1 straight to level 2, like the old sonic games, or would there be an overworld, like in Super Mario World? – Jordaan Mylonas Jan 8 '12 at 21:54
• No overworld...At the end of a level it would have a cut scene for plot, and then go straight to the next level, I may possibly want to have a screen that shows score and kills and such after the level ends and before the cut scene. – Adam Jan 8 '12 at 21:56
• How are you currently loading the level? Is it as simple as LevelInstance.load()? – Jordaan Mylonas Jan 8 '12 at 22:07
• Right now the main loop just creates an instance of the level class (Level level) and the game loop calls the update and draw function from the class (level.update(); and level.draw();). The data for level is loaded in the constructor of the Level class for the time being. – Adam Jan 8 '12 at 22:17
• You might consider then, loading each sequential level into an array of Levels, and having a pointer to the "current level", from which you may call update() and draw(). When it comes time to progress to the next level, just increment the pointer. – Jordaan Mylonas Jan 8 '12 at 22:50

You'd handle this using a state machine, and a couple of variables.

Something like this:

enum GameState
{
Intro, // title screen
Running, // gameplay running
Cutscene, // showing cutscene
End     // game over
} ;
// the variables:
GameState gameState = GameState::Intro ; // maintain the current gamestate.
int cutSceneNo = 1 ; // what cutscene we're currently displaying
// (only used WHEN in GameState::Cutscene)
int levelNo = 1 ; // what level we're going load and play next
// (only used WHEN in GameState::Running)


It's really simple. Have a different "state" for every "state" the game can be in! This gives you a really easy way to transition from Intro screen to Cutscene to Running.. see pseudocode below.

Main loop:

while( 1 )
{
update() ;
draw() ;
}


Now, in both the update() and draw() basic functions, the decision what to do can be made based on gameState:

Update function: look at gameState to figure out what to do. Include code to watch for key events that will transition to the next state.

void update()
{
getInput() ; //read current state of the keys.. used below

switch( gameState )
{
case GameState::Intro:
if (any key) was pushed
{
cutSceneNo = 1 ;  // ON FIRST CUTSCENE
gameState = GameState::Cutscene ; // display a cutscene next time
// the update() function is called
}
break ;

case GameState::Cutscene:

runCutscene() ; // RUN THE CUTSCENE, whatever that entails

// WATCH FOR EVENTS THAT WILL CAUSE TRANSITION
// TO NEXT STATE (this code could actually go in runCutscene())
if( cutsceneDone OR (any key) was pressed )
{
// FROM A FILE OR WHATEVER, WHEN THE CUTSCENE IS DONE
// Once that's done and the level is loaded:
gameState = GameState::Running ; // jump into game when cutscene done
}
break ;

case GameState::Running:
run() ; // run the game itself
// if you die in game, then transition to GameState::End
break ;
}
}

void run()
{
// here you run the game.
// if the level is DONE, increment cutSceneNo
// and transition to cutScene state.

// RUN GAME LOGIC BASED ON LOADED LEVEL

// now if the player FINISHES the level, transition
// to next cutscene
if( player finished level )
{
cutSceneNo++ ;
gameState = GameState::CutScene ;
}
}


Draw function: also look at gameState to figure out what to do

void draw()
{
switch( gameState )
{
case GameState::Intro:
drawIntro() ;
break ;

case GameState::Cutscene:
drawCurrentCutscene() ;
break ;

case GameState::Running:
drawGame() ;
break ;
}
}


So the idea is, you maintain enough state variables so the game knows exactly what it's doing at each moment. Depending on gameState, different things happen in the update() and run() loops.

• is there a chance that i can do a code like this in C not C++? because i think i need something like this. – matthews Mar 19 at 20:45

I think it entirely depends on the amount of data (and memory-resources available) you'll have to load for each level. If your level-data alone is huge, then your level-progress might be just an array (or vector) of files:

levels = { "Level1.dat", "Level2.dat", "Level3.dat" };


Then you keep track of level-index and load the level file at the current index. If the level is complete, you load the level at the given index.

If you always use the same assets and the level-data easily fits into memory, you could preload everything. But since you have intermediate cut-scenes, that isn't really necessary as you can use them to load the next level.

Another idea would be to have some sort of "story" or "progress" data file you load. Sometimes you need meta-data that is external to your actual level data.. for example if you have a time-limit on the level. I prefer to not have this time-limit inside my level-data, but rather separate from it, so that I can easily tweak it. Also if your level should have a name, you could put it into the "story" data-file, and this could easily be translated.

Here's how such a story-file could look like (JSON):

{
"progress" : [
{
"Type"      : "Level",
"Title"     : "Hello world!",
"Data"      : "level1.dat",
"Timelimit" : 30000
},
{
"Type"      : "Cutscene",
"Title"     : "You completed level 1!",
"Text"      : "I completed level 1, but then I took an arrow in the knee"
},
{
"Type"      : "Level",
"Title"     : "Arrow begone",
"Data"      : "level2.dat",
"Timelimit" : 40000
},
...
]
}


Having such a story- or progress-file will easily allow you to swap and re-arrange levels and cut-scenes. If your cut-scenes are really simple, you could even get away with something like above, where the entire data of the cut-scene is in the story-file. Otherwise you might just have to reference a file for the cut-scene. Also you're free to add more meta-data. For example you could add an array of resources to load for each level (if that isn't already specified in your level file).

So in your game you would load the story-file and then move from one entry to the next...

• I think choosing/using a JSON parser might be a little steeper than necessary, unless you're really into working with a JSON parser! – bobobobo Jan 9 '12 at 9:15
• @bobobobo That was just an example... it could be any type of data. But having stuff as a file is going to help a lot, as you can tweak stuff without recompilation. – bummzack Jan 9 '12 at 9:20