Dealing with multiple scenes /levels

| | August 8, 2015

Overview

I have a scene manager in my game and in each ‘Scene’ I have a render() and a update() method for drawing and updating my game’s logic.

Basically, when I want to switch to another scene, let’s say, when the player progresses to the next level or going from game-over back to the main menu, I just do something like this:

SceneManager.getInstance().setCurrentScene(level2); //Where level 2 is a 'Scene' class.

I have a ‘MainGame’ scene who’s sole purpose is to house all the methods I call from my individual levels. So I might then do something like this:

Current method

public class Level1 extends MainGame implements Scene {

    private Resources res;
    private MyGLRenderer r;

    Level1(MyGLRenderer r, Resources res){
        super(r, res);
        this.res = res;
        this.r = r; 
    }

    @Override
    public void render() {      
        draw(background);
        draw(playerSprite);
        draw(blue-enemies);
    }

    @Override
    public void update(){
        super.movePlayer();
        super.moveEnemies();
        super.checkCollision();
    }

}

However, in my game, when the player survives for a certain amount of time, I want to introduce more elements into the game, so I might then have a ‘level2′ Scene (although strictly, from the player’s perspective, the game is perceived to be one big rolling level)…..

    public class Level2 extends MainGame implements Scene {

        private Resources res;
        private MyGLRenderer r;

    Level2(MyGLRenderer r, Resources res){

        super(r, res);
        this.res = res;
        this.r = r; 
    }

    @Override
    public void render() {      
        draw(background);
        draw(playerSprite);
        draw(blue-enemies);
        draw(pink-enemies);  //New!!!! *********************************
    }

    @Override
    public void update(){

        super.movePlayer();
        super.moveEnemies();
        super.checkCollision();
    }

}

So, as you can see, Level2 is exactly the same as Leve1 but now I’m also drawing my ‘pink-enemies’, I can keep doing this for ‘levels’ 3 through 10 but I’ll end up duplicating and simply adding to each level each time. Seems like a lot of effort just to introduce 1 or 2 new things every ‘level’.

Using conditions within my render / logic?

Is there a cleaner / tidier way of achieving that which I’m trying to achieve? I could do away with all my individual ‘Level’ classes altogether and just work straight out my MainGame class’s Render() and Update() methods, but then I would have to have something like:

Render(){

    //Render elements common to all levels
    draw(background);
    draw(playerSprite);
    draw(blue-enemies);

    //Render specific level objects

    Switch (Level){

        case 4:{draw(giantAlienSpaceship);}
        case 3:{draw(orange-enemies);}
        case 2:{draw(pinkEnemies);break;}
    }
}

And then a similar switch in my logic. I’m not sure about this however, as it adds additional switch/conditional statements in my rendering and seems somewhat ‘amateur’ however, I could be completely wrong and this may be an acceptable method that people do use.

Am I missing any other obvious cleaner / cleverer way to do this (similar to my first method but without the duplicated super method calls)

2 Responses to “Dealing with multiple scenes /levels”

  1. why not create a list of enemies in the level so the draw becomes:

    List<GenericEnemy> enemies;//filled during construction
    Render(){    
        //Render elements common to all levels
        draw(background);
        draw(playerSprite);
        for(GenericEnemy enemy : enemies) {
            draw(enemy);
        }    
    }
    

    where GenericEnemy is a superclass of all enemy types, then you only need one Scene class

  2. Before addressing the specifics of your question, I do want to point out that I disagree with your approach to your inheritance model.

    A Game generally does not implement a Scene but instead a Game consists of one or more active Scenes that are being rendered and updated in a main loop. It’s important to think about whether a class relationship can be described as has-a versus is-a. In the former, it implies composition and therefore a property/variable of the class. In the latter, it implies inheritance.

    Therefore as I said, a game has a list of active scenes so the code might look like:

     public class Game {
       List<Scene> activeScenes;
       public void mainLoop() {
         while(!shutdown) {
           for(Scene scene : activeScenes) {
             scene.update(deltaTime);
           }
           for(Scene scene : activeScenes) {
             scene.draw(renderer);
           }
         }
       }
     }
    

    What’s important here is that you have a solid basis for core game logic that is independent of what your scene logic may be. This class could hold a list of active rendered game objects, it might hold a reference to your camera, graphics context, etc.

    Now your Scene class looks somewhat like what you’ve described.

     public interface Scene {
       void load(Game game);
       void unload(Game game);
       void update(float deltaTime);
       void draw(MyGLRenderer renderer);
     }
    

    If you build off what ratchet said, your Scene essentially has a list of GameObject instances that represent your various enemies and get instantiated when the scene is loaded. As a scene gets updated, any enemy logic can be updated and during draw you actually render each one. During the unload, you free any resources for each GameObject associated with the level.

    Not only does this give you good separation of concerns and responsibility, but it also opens the door for having multiple active scenes that are contributing to the same rendered view; such as when you pause the game and render the main menu or as you want to introduce a complex scenario into a level randomly :p.

    If you now use the term State rather than the term Scene; you’ll notice this is precisely the State design pattern and is often implemented in most game engines in some variant providing precisely this kinda functionality.

Leave a Reply