= Simple AppStates Demo
:author: 
:revnumber: 
:revdate: 2016/03/17 20:48
:relfileprefix: ../../
:imagesdir: ../..
ifdef::env-github,env-browser[:outfilesuffix: .adoc]



[IMPORTANT]
====

THIS DEMO IS OUT OF DATE AND NEEDS CORRECTING

====



== THIS DEMO IS OUT OF DATE AND NEEDS CORRECTING FOR NOW PLEASE SEE

 <<jme3/advanced/application_states#,jme3:advanced:application_states>>

Note: this tutorial needs to be fixed and is currently not correct.  One should almost never override stateDetached and stateAttached… and should certainly never do anything scene related in them.

This demo is a simple example of how you use AppStates to toggle between a StartScreen and a SettingsScreen (press RETURN) while the game is paused, and start the game by switching to a GameRunning state (press BACKSPACE). 

There are four files, Main.java, GameRunningState.java, StartScreenState.java, SettingsScreenState.java. 


=== Main.java

[source,java]
----

package chapter04.appstatedemo;

import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.Trigger;

/**
 * This demo shows a simple "game" with three AppStates. Instead of game content, 
 * it just displays three cubes on different backgrounds.
 * <ul>
 * <li>StartScreenState: This state is enabled 
 *     when the user starts the application, or the the game is paused. 
 *     Press BACKSPACE to return to the game, press RETURN to go to Settings.</li>
 * <li>GameRunningState: This state shows the game content and is enabled while the game is running. 
 *     Press BACKSPACE to pause and return to the start screen.</li>
 * <li>SettingsScreenState: This Settings screen state can be reached from the start screen
 *     Press RETURN to toggle it on and off.</li>
 * </ul>
 */
public class Main extends SimpleApplication {

  private Trigger pause_trigger = new KeyTrigger(KeyInput.KEY_BACK);
  private Trigger save_trigger = new KeyTrigger(KeyInput.KEY_RETURN);
  private boolean isRunning = false; // starts at startscreen
  private GameRunningState gameRunningState;
  private StartScreenState startScreenState;
  private SettingsScreenState settingsScreenState;

  
  /** Start the jMonkeyEngine application */
  public static void main(String[] args) {
    Main app = new Main();
    app.start();
  }

  /**
   * initialize the scene here
   */
  @Override
  public void simpleInitApp() {
    setDisplayFps(false);
    setDisplayStatView(false);

    gameRunningState    = new GameRunningState(this);
    startScreenState    = new StartScreenState(this);
    settingsScreenState = new SettingsScreenState(this);

    stateManager.attach(startScreenState);

    inputManager.addMapping("Game Pause Unpause", pause_trigger);
    inputManager.addListener(actionListener, new String[]{"Game Pause Unpause"});
    inputManager.addMapping("Toggle Settings", save_trigger);
    inputManager.addListener(actionListener, new String[]{"Toggle Settings"});
  }
  
  private ActionListener actionListener = new ActionListener() {
    public void onAction(String name, boolean isPressed, float tpf) {
      System.out.println("key" + name);
      if (name.equals("Game Pause Unpause") && !isPressed) {
        if (isRunning) {
          stateManager.detach(gameRunningState);
          stateManager.attach(startScreenState);
          System.out.println("switching to startscreen...");

        } else {
          stateManager.detach(startScreenState);
          stateManager.attach(gameRunningState);
          System.out.println("switching to game...");
        }
        isRunning = !isRunning;
      } else if (name.equals("Toggle Settings") && !isPressed && !isRunning) {
        if (!isRunning && stateManager.hasState(startScreenState)) {
          stateManager.detach(startScreenState);
          stateManager.attach(settingsScreenState);
          System.out.println("switching to settings...");
        } else if (!isRunning && stateManager.hasState(settingsScreenState)) {
          stateManager.detach(settingsScreenState);
          stateManager.attach(startScreenState);
          System.out.println("switching to startscreen...");
        }
      }
    }
  };

  @Override
  public void simpleUpdate(float tpf) {}

}
 

----


=== GameRunningState.java

[source,java]
----

package chapter04.appstatedemo;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;

/**
 * A template how to create an Application State. This example state simply
 * changes the background color depending on the camera position.
 */
public class GameRunningState extends AbstractAppState {

  private ViewPort viewPort;
  private Node rootNode;
  private Node guiNode;
  private AssetManager assetManager;
  private Node localRootNode = new Node("Game Screen RootNode");
  private Node localGuiNode = new Node("Game Screen GuiNode");
  private final ColorRGBA backgroundColor = ColorRGBA.Blue;

  public GameRunningState(SimpleApplication app){
    this.rootNode     = app.getRootNode();
    this.viewPort      = app.getViewPort();
    this.guiNode       = app.getGuiNode();
    this.assetManager  = app.getAssetManager();  
  }

  @Override
  public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app);

    /** Load this scene */
    viewPort.setBackgroundColor(backgroundColor);

    Box mesh = new Box(Vector3f.ZERO, 1, 1, 1);
    Geometry geom = new Geometry("Box", mesh);
    Material mat = new Material(assetManager,
            "Common/MatDefs/Misc/Unshaded.j3md");
    mat.setColor("Color", ColorRGBA.Green);
    geom.setMaterial(mat);
    geom.setLocalTranslation(1, 0, 0);
    localRootNode.attachChild(geom);

    /** Load the HUD*/
    BitmapFont guiFont = assetManager.loadFont(
            "Interface/Fonts/Default.fnt");
    BitmapText displaytext = new BitmapText(guiFont);
    displaytext.setSize(guiFont.getCharSet().getRenderedSize());
    displaytext.move(10, displaytext.getLineHeight() + 20, 0);
    displaytext.setText("Game running. Press BACKSPACE to pause and return to the start screen.");
    localGuiNode.attachChild(displaytext);
  }

  @Override
  public void update(float tpf) {
    /** the action happens here */
    Vector3f v = viewPort.getCamera().getLocation();
    viewPort.setBackgroundColor(new ColorRGBA(v.getX() / 10, v.getY() / 10, v.getZ() / 10, 1));
    rootNode.getChild("Box").rotate(tpf, tpf, tpf);
  }
  
  @Override
  public void stateAttached(AppStateManager stateManager) {
    rootNode.attachChild(localRootNode);
    guiNode.attachChild(localGuiNode);
    viewPort.setBackgroundColor(backgroundColor);
  }

  @Override
  public void stateDetached(AppStateManager stateManager) {
    rootNode.detachChild(localRootNode);
    guiNode.detachChild(localGuiNode);

  }

}

----


=== SettingsScreenState.java

[source,java]
----

package chapter04.appstatedemo;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;

/**
 * A template how to create an Application State. This example state simply
 * changes the background color depending on the camera position.
 */
public class SettingsScreenState extends AbstractAppState {

  private ViewPort viewPort;
  private Node rootNode;
  private Node guiNode;
  private AssetManager assetManager;
  private Node localRootNode = new Node("Settings Screen RootNode");
  private Node localGuiNode = new Node("Settings Screen GuiNode");
  private final ColorRGBA backgroundColor = ColorRGBA.DarkGray;

  public SettingsScreenState(SimpleApplication app) {
    this.rootNode     = app.getRootNode();
    this.viewPort      = app.getViewPort();
    this.guiNode       = app.getGuiNode();
    this.assetManager  = app.getAssetManager();
  }

  @Override
  public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app);

    /** Load this scene */
    viewPort.setBackgroundColor(backgroundColor);

    Box mesh = new Box(new Vector3f(-1, -1, 0), .5f, .5f, .5f);
    Geometry geom = new Geometry("Box", mesh);
    Material mat = new Material(assetManager,
            "Common/MatDefs/Misc/Unshaded.j3md");
    mat.setColor("Color", ColorRGBA.Red);
    geom.setMaterial(mat);
    geom.setLocalTranslation(1, 0, 0);
    localRootNode.attachChild(geom);

    /** Load the HUD */
    BitmapFont guiFont = assetManager.loadFont(
            "Interface/Fonts/Default.fnt");
    BitmapText displaytext = new BitmapText(guiFont);
    displaytext.setSize(guiFont.getCharSet().getRenderedSize());
    displaytext.move(10, displaytext.getLineHeight() + 20, 0);
    displaytext.setText("Settings screen. Press RETURN to save "
            + "and return to start screen.");
    localGuiNode.attachChild(displaytext);
  }

  @Override
  public void update(float tpf) {
     /** the action happens here */
  }

  @Override
  public void stateAttached(AppStateManager stateManager) {
    rootNode.attachChild(localRootNode);
    guiNode.attachChild(localGuiNode);
    viewPort.setBackgroundColor(backgroundColor);
  }

  @Override
  public void stateDetached(AppStateManager stateManager) {
    rootNode.detachChild(localRootNode);
    guiNode.detachChild(localGuiNode);
  }
  
}

----


=== StartScreenState.java

[source,java]
----

package chapter04.appstatedemo;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;

/**
 * A template how to create an Application State. This example state simply
 * changes the background color depending on the camera position.
 */
public class StartScreenState extends AbstractAppState {

  private ViewPort viewPort;
  private Node rootNode;
  private Node guiNode;
  private AssetManager assetManager;
  private Node localRootNode = new Node("Start Screen RootNode");
  private Node localGuiNode = new Node("Start Screen GuiNode");
  private final ColorRGBA backgroundColor = ColorRGBA.Gray;  

public StartScreenState(SimpleApplication app){
    this.rootNode     = app.getRootNode();
    this.viewPort     = app.getViewPort();
    this.guiNode      = app.getGuiNode();
    this.assetManager = app.getAssetManager();  
  }

  @Override
  public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app);
    
    /** Init this scene */
    viewPort.setBackgroundColor(backgroundColor);

    Box mesh = new Box(new Vector3f(-1,1,0), .5f,.5f,.5f);
    Geometry geom = new Geometry("Box", mesh);
    Material mat = new Material(assetManager,
            "Common/MatDefs/Misc/Unshaded.j3md");
    mat.setColor("Color", ColorRGBA.Yellow);
    geom.setMaterial(mat);
    geom.setLocalTranslation(1, 0, 0);
    localRootNode.attachChild(geom);

    /** Load a HUD */
    BitmapFont guiFont = assetManager.loadFont(
            "Interface/Fonts/Default.fnt");
    BitmapText displaytext = new BitmapText(guiFont);
    displaytext.setSize(guiFont.getCharSet().getRenderedSize());
    displaytext.move( 10, displaytext.getLineHeight() + 20,  0);
    displaytext.setText("Start screen. Press BACKSPACE to resume the game, "
            + "press RETURN to edit Settings.");
    localGuiNode.attachChild(displaytext);
  }

  @Override
  public void update(float tpf) {
    /** the action happens here */
  }

  @Override
  public void stateAttached(AppStateManager stateManager) {
    rootNode.attachChild(localRootNode);
    guiNode.attachChild(localGuiNode);
    viewPort.setBackgroundColor(backgroundColor);
  }

  @Override
  public void stateDetached(AppStateManager stateManager) {
    rootNode.detachChild(localRootNode);
    guiNode.detachChild(localGuiNode);
  }
  
}

----
