Scene - Lebby/ForScene GitHub Wiki

What is a Scene?

It is an object container. Refering to classical 2d game, a scene is a game screen. Refering game like Pong, Pacman, Pang, 2h4u, Platform Game, Flipper game simulation, etc., scene rappresents "the stage" concept. It's a game context. In a scene you can add all that you want like: images, sounds, texts, effects, buttons, HUD, menus, timers, objects, etc.

A Scene can be also interpreted like a presentation slide that can be very complex ( or simpler ... ).

We can imagine a standard simplier game that contains a splash, a home menu and the game ( i.e. Pong ). You can do it all in a very complex scene ( if you prefer it ) or you can build 3 different scene implementation:

  • Splash Scene ( timed scene, with one background, one or more images and various effect like fadein and fadeout )
  • Main Menu Scene ( triggered scene, with various button and image, various sound and music )
  • Game Scene ( contains HUD, input management, 2 different instance of paddle , a background, various image, various sound, etc. )

IMHO, it could be a best practice split game in more scene as possible, because it could be easier to debug, to check and to implement.

Implement a Scene

You must implement a Scene by implementing an AbstractScene. AbstractScene is an abstract class that has two abstract method which must be implemented: build and updateState. AbstractScene is derived from AbstractSceneObjectGroup then you can add AbstractSceneObject to your scene and add other scene to your main scene.

VERY IMPORTANT NOTE: I suggest to use AbstractSceneGroup to chain scenes.

Scenes is automatically switched by LoopManager. LoopManager switch scene verifying internal scene flag. A scene can be switched by:

  • a condition
  • a time event
  • generating a LoadSceneEvent or a LoadSceneEventGroup

To use condition: You must invoke setConditional(true) of scene object and override isReadyToSwitch(); example:

public class ConditionalScene extends AbstractScene
...
    @Override
    public boolean isReadyToSwitch() 
    {
        if(condition)
        {
            ...
            return true; // then loop manager switch scene
        }
        else return false; // loop manager doesn't switches scene.
    }
...
}

To use time event You must use setTimeout(sec); When you use this method LoopManager start a timer at scene loading.

generating a LoadSceneEvent or a LoadSceneEventGroup You must invoke EventManager.getInstance().push(new LoadSceneEvent(scene)) or EventManager.getInstance().push(new LoadSceneGroupEvent(sceneGroup)); When you generate LoadSceneGroupEvent, LoopManager automatically load first scene of sceneGroup.

Scene Skelethon

public class SimpleScene extends AbstractScene
{
    @Override
    public void build() {
        ...
    }
    @Override
    public void updateState() {
        ...
    }
}

Build Method

In Build you must add all object that must be ready before use scene . I want to underline "BEFORE USE SCENE" . It has many consideration on advanced use in a ForScene Project. Build method is called automatically by system when system wants to load it.

Build method is the answer of question: "Where i can add my object? Where i can setup my scene?"

Very Important: All that are instantiated in build method are available ONLY AFTER LOADSCENE . If you want that your objects, inside a scene, must be available before LoadScene, or on Scene definition, you must define them in Scene Constructor!

Example:

public class ExampleScene extends AbstractScene
{

// objects defined here is available on Scene definition. If scene exist then they can be accessed.
public ExamleScene()
{
        //It adds an object in scene.
        addSceneObject("exampleObject",exampleObject); // exampleObject is available before scene is loaded.
}

// objects defined here is loaded on Scene load then it can be correctly accessed only after load.
    @Override
    public void build() {
        //It adds an image on scene. It can be done adding an AbstractSceneObject
        GraphicFactory.addImage("images/bg.png",this); 
        //It adds an AbstractSceneObject in scene. "logo" is inner scene name  and logo is its object instance;
        addSceneObject("logo",logo); //logo is available only after this scene is loaded.

    }

UpdateState Method

Update state will manage your scene state change. If your scene need change, like add or delete objects, start or stop animation, or calculate complex physic formula, switching image, etc. You can do it here ( if you want ). There is a more modern approach that is event based system. It is implemented too, but i think that UpdateState and Event are both useful to a better separation of state and a smart way to manage state. UpdateState method is called every time system notice that scene must be update. You are master and you choose when it will be happen by use setToUpdate(true) on your scene. If you set and update rate by setUpdateRate(int rate);, updateState method will be called by system each 1/rate seconds. ( rate = "how many time in one second"; 0 = none )

I want do a logic example: Sequence key recognition We want to identify a sequence of arrow keys ( i.e. left arrow , down arrow , right arrow ... hadoooken ) I can identify 4 useful state : NONE, left_pressed, left_then_down_pressed, left_then_down_then_right_pressed.

public class HadokenRec extends AbstractScene
{
   private Ryu ryu; // Just 4 fun!

   static enum STATE
   {
      NONE,LEFT_PRESSED,LEFT_THEN_DOWN_PRESSED,LEFT_THEN_DOWN_THEN_RIGHT_PRESSED,
      RIGHT_PRESSED,DOWN_PRESSED,UP_PRESSED
   };

   private STATE currentState=NONE;

   @Override
   void updateState()
   {
      switch(currentState)
      {
         case LEFT_PRESSED:  ryu.goLeft();  break;
         case RIGHT_PRESSED: ryu.goRight(); break;
         case UP_PRESSED:    ryu.jump();    break;
         case DOWN_PRESSED:  ryu.goDown();  break;
         case LEFT_THEN_DOWN_PRESSED:   return; //DON'T GO TO NONE;
         case LEFT_THEN_DOWN_THEN_RIGHT_PRESSED: ryu.hadoken(); break;
         case NONE: breath(); break;
      }
      currentState=NONE;
      setToUpdate(false);
   }


// called by an external event
   public void keyPressed(STATE input)
   {
...
      if((currentState == STATE.LEFT_PRESSED) && (inputState == STATE.DOWN_PRESSED )) 
         currentState=STATE.LEFT_THEN_DOWN_PRESSED;
      else if((currentState == STATE.LEFT_THEN_DOWN_PRESSED) && (inputState == STATE.RIGHT_PRESSED )) 
         currentState=STATE.LEFT_THEN_DOWN_THEN_RIGHT_PRESSED;
      else currentState=input;
...
      setUpdate(true);
   }

}
      

This example show how to manage switching state from an input ( event based ) and how to manage state with updateState that switch inner state.

Very Important: Understanding Call Sequence : Why Build Method?

It is very important to understand Build Method. The Build method is an optimization of this engine. It permits to load object AT RUNTIME AFTER SCENE IS LOADED. This allow to load object only when you needs. If you want to access to an object or something you have defined in build method, it can't be available until scene is loaded! If you want instantiate an object before building, you must do on scene constructor.