0
\$\begingroup\$

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)

\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

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.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks @crancran, yeah I literally changed my code just yesterday to extend MainGame - before that I wasn't extending and was passing MainGame into my Level1 Scene, so I will probably switch back to doing that. Your answer is intersting because it also kind of solves another problem that I've been dealing with, that being how to draw multiple scenes at once. I want to make sure I understand properly though. So the List is acting almost like a stack of Scenes? I can just put a Scene onto it and the remove one at will correct? One thing I don't quite understand is how the load and (continued) \$\endgroup\$ Commented Oct 29, 2014 at 14:30
  • \$\begingroup\$ ..unload methods work here. Shouldn't these methods be in the SceneManager class rather than the Interface? I mean so I can do something like SceneManager.getInstance().loadScene(Level2); Rather than overriding a load method in the interface. If you see what I mean?! Would be grateful if you could clarify this, just so I'm clear. Thanks. \$\endgroup\$ Commented Oct 29, 2014 at 14:34
  • \$\begingroup\$ @user22241 if game has a number of methods you need everywhere else, then you probably need to move those outside of your game class and make them accessible, rather than inheriting or creating circular references. I can't speak for crancran but I would expect Scene.load to give the scene to do all the actions it needs to perform when it is loaded. So create any entities or get resources it previously released. The SceneManager in your case wouldn't know what the scene needs, resource wise but the Scene does. \$\endgroup\$
    – Joeblade
    Commented Nov 13, 2014 at 12:01
3
\$\begingroup\$

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

\$\endgroup\$
1
  • \$\begingroup\$ Thanks @ratchetFreak, this is at interesting way of looking at the problem that I hadn't considered. \$\endgroup\$ Commented Oct 29, 2014 at 14:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .