How should I manage level progression?

Adam
  • How should I manage level progression? Adam

    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.

  • 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...

  • 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 )
              {
                LOAD_LEVEL(   levelNo   ) ; // LOAD THE NEXT LEVEL
                // 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.

Tags
c++ sdl shoot-em-up
Related questions and answers
  • I'm trying to create a formula that can be modified simply by changing two values: number_of_levels, and last_level_experience. This is to enable people modding the game to change the levelling.... For example, if I have 40 levels, and 1,000,000 XP for the last level, the first level up requirement is then 625. But if I change the levels to 80, the first level up becomes 156. In both cases, the last level needs 1,000,000. There must be some way to get the computer to work out a suitable curve given just these two basic values. #include <iostream> int main() { int levels = 40

  • I'm hard-coding my levels and coding each level in its own class...The problem is that I need to have a pointer that points to the class of the current level. I don't know how I would go about doing this. I ideally want to have a vector that stores the levels, and I'm working on a level managing class to load, update, draw, and unload the levels in.

  • I'm trying to write my level classes by having a base class that each level class inherits from...The base class uses pure virtual functions. My base class is only going to be used as a vector... supposed to include in the inherited class structure, but this is what I have at the moment... //level1.h class Level1: public Level { public: Level1(); ~Level1(); void load(); void... code in them... Right now for testing purposes, I just have the main loop call Level1 level1; and use the functions, but when I run the game I get a segmentation fault. This is the first time I've

  • I'm working on a platformer where I'd like to be able to move or copy bodies between multiple Box2D b2Worlds. The desire has cropped up three times now - moving objects between documents in my editor, moving the player between levels in normal game play, and giving the player a preview of a non-trivial projectile they can shoot in the game, which seems to be most accurately done by simulating the world for a few seconds ahead. It looks like I can make a parallel world the "long way" - for each body in the world, copy the body data back into a def and re-create it, then copy the fixtures

  • I've decided I want to write a central ResourceManager/ResourceCache class for my hobby game engine, but am having trouble designing a caching scheme. The idea is that the ResourceManager has... in advance of first use. Here is a bit a sketch of the classes I am using: typedef unsigned int ResourceId; // Resource is an abstract data type. class Resource { Resource(); virtual... a level is running, // load/reload/unload might get called during level transitions. template <class T> ResourceGuard<T> &getResource(ResourceId resourceId

  • problem is that if you had to compare all the players every time an update is sent, it would just take way too long as the number of players scales (think up to a few thousand players per map and each of them updating up to 3-5 times a second). If the players are in a 2D space (it's 3D but the z difference shouldn't affect visibility) what is the best way to manage the visibility of other players...I'm looking into designing and implementing an online RPG (or more accurately, redesigning an existing piece of server software). One of the problems is that of managing visibility. Update data

  • I'm writing a game engine which is going very fine. However, I'm now posed with handling textures. My Engine is in 2D, for simplicity reasons mostly to get a good idea of how to work with OpenGL. I do this in C++, and I have a hierarchical set-up when it comes to classes. There's a base class and some other classes derive from that. So now I'm onto the part where I want to load up some textures... to render the scene. What I'm interested in, what is the best practice to handle this, and how to handle OpenGL texture storage. I understand OpenGL keeps the textures in it's texture memory, and that I

  • I'm considering using micropather to help me with pathfinding. I'm not using a discrete map : I'm working in 2d with sectors and portales. However, I'm just wondering what is the best way to compute.... Micropather exposes a pure virtual Graph class that you must inherate and overrides 3 functions. I understand how pathfinding works, so there's no problem in overriding those functions. Right now, my implementation give me results, i.e I'm able to find a path in my map, but I'm not sure I'm using an optimal solution. For the AdjacentCost method : I just take the distance between sector's centers

  • I'm attempting to program an offline puzzle game in C++. I'm trying to figure out the best way (particularly during the alpha) to report back to me Whether people finish the level Where people died How long they took on the level The current version that they are playing I don't currently have a public database server which I could dump it in to. Is there any already written libraries that (for instance) drop analytics into a Google App Engine Database or Google Docs Spreadsheet? What approach (if any) have you used for storing analytics?

Data information