Howto obtain/getting rootNode inside AppState or (Game) State

I am trying to use the AppStates to whip up a menu and a small game scene and being able to switch between them. I created a BaseGameAction that implements AppState.

BaseGameState

[java]

package sector.soda.states;

import com.jme3.app.Application;

import com.jme3.app.state.AppState;

import com.jme3.app.state.AppStateManager;

import com.jme3.renderer.RenderManager;

/**

*

  • @author cheesecake

    */

    public class BaseGameState implements AppState {

    protected boolean initialized = false;

    private boolean active = true;

    public void initialize(AppStateManager stateManager, Application app) {

    initialized = true;

    }

    public boolean isInitialized() {

    return initialized;

    }

    public void setActive(boolean active) {

    this.active = active;

    }

    public boolean isActive() {

    return active;

    }

    public void stateAttached(AppStateManager stateManager) {

    }

    public void stateDetached(AppStateManager stateManager) {

    cleanup();

    }

    public void render(RenderManager rm) {

    }

    public void postRender() {

    }

    public void cleanup() {

    initialized = false;

    }

    public void update(float tpf) {

    }

    }

    [/java]

    I got a GameState that extends the BaseGameState. It sets up a small scene attaching the Spatial to the rootNode from Main

    [java]

    package sector.soda.states;

    import com.jme3.app.Application;

    import com.jme3.app.state.AppStateManager;

    import com.jme3.input.KeyInput;

    import com.jme3.input.controls.ActionListener;

    import com.jme3.input.controls.AnalogListener;

    import com.jme3.input.controls.KeyTrigger;

    import com.jme3.light.DirectionalLight;

    import com.jme3.math.Vector3f;

    import com.jme3.scene.Spatial;

    import java.util.logging.Level;

    import sector.soda.Main;

    import sector.soda.util.MyLogger;

    /**

    *
  • @author cheesecake

    */

    public class GameState extends BaseGameState {

    public AppStateManager mySm;

    public Application myApp;

    private boolean gamePaused = false;

    private static MyLogger logger = Main.log;

    DirectionalLight sun;

    public GameState() {

    logger.log(Level.INFO, "GameState constructor");

    }

    @Override

    public void initialize(AppStateManager stateManager, Application app) {

    logger.log(Level.INFO, "GameState initialize");

    initialized = true;

    mySm = stateManager;

    myApp = app;

    setupScene();

    initKeys();

    }

    private void setupScene() {

    logger.log(Level.INFO, "GameState setupScene");

    Spatial alien = myApp.getAssetManager().loadModel("Models/Alien/Skin_Sym_Low_6.mesh.j3o");

    alien.setLocalTranslation(0.0f, 0.0f, -2.0f);

    Main.node.attachChild(alien);

    sun = new DirectionalLight();

    sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f));

    Main.node.addLight(sun);

    }

    private void initKeys() {

    logger.log(Level.INFO, "GameState initKeys");

    myApp.getInputManager().addMapping("Pause", new KeyTrigger(KeyInput.KEY_P));

    myApp.getInputManager().addMapping("Menu", new KeyTrigger(KeyInput.KEY_F10));

    myApp.getInputManager().addMapping("Left", new KeyTrigger(KeyInput.KEY_J));

    myApp.getInputManager().addMapping("Right", new KeyTrigger(KeyInput.KEY_K));

    myApp.getInputManager().addListener(actionListener, new String[]{"Pause", "Menu"});

    myApp.getInputManager().addListener(analogListener, new String[]{"Left", "Right"});

    }

    @Override

    public void cleanup() {

    logger.log(Level.INFO, "GameState cleanup");

    initialized = false;

    Main.node.detachAllChildren();

    Main.node.removeLight(sun);

    }

    private ActionListener actionListener = new ActionListener() {

    public void onAction(String name, boolean keyPressed, float tpf) {

    if (name.equals("Pause") && !keyPressed) {

    if (!gamePaused) {

    gamePaused = true;

    } else {

    gamePaused = false;

    }

    }

    if (name.equals("Menu") && !keyPressed) {

    GameState gameState = myApp.getStateManager().getState(GameState.class);

    if (gameState != null && gameState.isActive()) {

    myApp.getStateManager().detach(gameState);

    MenuState menuState = myApp.getStateManager().getState(MenuState.class);

    if (menuState != null && !menuState.isActive()) {

    menuState.setActive(true);

    } else {

    MenuState state = new MenuState();

    myApp.getStateManager().attach(state);

    }

    }

    }

    }

    };

    private AnalogListener analogListener = new AnalogListener() {

    public void onAnalog(String name, float value, float tpf) {

    if (!gamePaused) {

    if (name.equals("Right")) {

    }

    if (name.equals("Left")) {

    }

    }

    }

    };

    }

    [/java]

    I also have a working MenuState which displays a simple Nifty GUI. I am currently trying to just load the GameState first to see if something will show up. Is the way I am attaching it to the rootNode like this ok ? Should I give every state its own Node for rendering and how would I go about attaching that to the rootNode and getting something to display ?

    Or is it better to try and use the GameState I found on the svn:

    http://www.google.com/codesearch/p?hl=nl#Z8vJL-JqpQ0/trunk/src/com/jmex/game/state/GameState.java&q=State%20package:http://jmonkeyengine.googlecode.com&sa=N&cd=5&ct=rc

    This class is not in JME3 by default ?

Seems to only work if I pass along the first rootNode to the GameState.

Main

[java]

package sector.soda;

import com.jme3.app.SimpleApplication;

import com.jme3.renderer.RenderManager;

import sector.soda.states.GameState;

import sector.soda.util.MyLogger;

/**

  • @author cheesecake

    */

    public class Main extends SimpleApplication {

    public static final MyLogger log = new MyLogger(“Sector_Soda.log”);

    public static void main(String[] args) {

    Main app = new Main();

    app.start();

    }

    @Override

    public void simpleInitApp() {

    GameState gameState = new GameState(rootNode);

    stateManager.attach(gameState);

    }

    @Override

    public void simpleUpdate(float tpf) {

    }

    @Override

    public void simpleRender(RenderManager rm) {

    }

    }

    [/java]

    The getRootNode() that is used in GameState is probably not the same “Root Node” that is needed. This is the getRootNode() from the BaseGameState that extends SimpleApplication;

    [java]

    getRootNode().attachChild(alien);

    [/java]



    It just seems a bit ugly passing along the rootNode to all states that need to draw something on the screen.

You should only have one Application/SimpleApplication based class. Only the real one will have real information. Your subclasses will just contain nothing of use… besides, app states are given a reference to the main application instance when they are initialized.



Why did you feel the need to have your game state extend application?

pspeed said:
You should only have one Application/SimpleApplication based class. Only the real one will have real information. Your subclasses will just contain nothing of use... besides, app states are given a reference to the main application instance when they are initialized.
Why did you feel the need to have your game state extend application?

At the moment only Main extends Application/SimpleApplication and the states extend BaseGameState which implements AppState. At first I tried that to see if I could get to the rootNode that way.
My question now would be. How do I get access to the rootNode to load scene stuff in a state ?
[java]
Spatial alien = myApp.getAssetManager().loadModel("Models/Alien/Skin_Sym_Low_6.mesh.j3o");
alien.setLocalTranslation(0.0f, 0.0f, -2.0f);
// to which node do I attach the alien ?
[/java]
Is there a way to get it from the stateManager or Application app ? Or maybe I should not be (mis) using GameState this way to load scenes.

I updated the OP. For now I use the Main.node which gets set when starting the game.

Application is passed to AppState.initialize()… just cast it to SimpleApplication (or your app class) and call getRootNode().

pspeed said:
Application is passed to AppState.initialize()... just cast it to SimpleApplication (or your app class) and call getRootNode().

SOLVED: Thanks for your solution pspeed. I solved it like below.

In GameState initialize:
[java]
@Override
public void initialize(AppStateManager stateManager, Application app) {
initialized = true;
this.app = (SimpleApplication) app;
}
[/java]
In GameState setupScene() use app.getRootNode():
[java]
private void setupScene() {
Spatial alien = app.getAssetManager().loadModel("Models/Alien/Skin_Sym_Low_6.mesh.j3o");
alien.setLocalTranslation(0.0f, 0.0f, -2.0f);
app.getRootNode().attachChild(alien);
}
[/java]

Hello,

I’m trying to attach a Geometry to the root node in a GameState initialize() method using your solution, but I’ve got the following error:

GRAVE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]

java.lang.IllegalStateException: Scene graph is not properly updated for rendering.

Make sure scene graph state was not changed after

rootNode.updateGeometricState() call.

Problem spatial name: Root Node

[java]

@Override

public void initialize(AppStateManager stateManager, Application app) {

this.sapp = (SimpleApplication) app;

stateManager = sapp.getStateManager();



/** A cube with base color “leaking” through a partially transparent texture /

Box boxshape4 = new Box(new Vector3f(3f,-1f,0f), 1f,1f,1f);

Geometry cube_leak = new Geometry(“Leak-through color cube”, boxshape4);

Material mat_tl = new Material(sapp.getAssetManager(), “Common/MatDefs/Misc/Unshaded.j3md”);

mat_tl.setTexture(“ColorMap”, sapp.getAssetManager().loadTexture(“Textures/ColoredTex/Monkey.png”));

mat_tl.setColor(“Color”, new ColorRGBA(1f,0f,1f, 1f)); // purple

cube_leak.setMaterial(mat_tl);

sapp.getRootNode().attachChild(cube_leak);



/
* Must add a light to make the lit object visible! */

DirectionalLight sun = new DirectionalLight();

sun.setDirection(new Vector3f(1,0,-2).normalizeLocal());

sun.setColor(ColorRGBA.White);

sapp.getRootNode().addLight(sun);

}

[/java]



any idea?

Pretty sure that should work. Is there anything else you’ve changed? Like, do you override getRootNode() to do something strange or is it the default?



If you comment the attachChild() calls above does your error go away? ie: is it really the above code causing the problem or something else…



The above code looks correct so it must be something else that is the problem but it is hard to guess.

I have to comment the two getRootNode() calls to get it working, I get the very same error with both of them.

I don’t override anything I guess, I’m just testing and trying to work with the states. There is my code:

[java]

import com.jme3.app.SimpleApplication;

import com.jme3.renderer.RenderManager;

import com.jme3.system.AppSettings;



public class Main extends SimpleApplication {



public static void main(String[] args) {

Main app = new Main();

AppSettings settings = new AppSettings(true);

settings.setFrameRate(60);

app.setSettings(settings);

app.start();

}



@Override

public void simpleInitApp() {

//flyCam.setEnabled(false);

MainMenuState mainMenuState = new MainMenuState();

stateManager.attach(mainMenuState);

}



@Override

public void simpleUpdate(float tpf) {



}



@Override

public void simpleRender(RenderManager rm) {

//TODO: add render code

}

}

[/java]



and



[java]

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.light.DirectionalLight;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector3f;

import com.jme3.scene.Geometry;

import com.jme3.scene.shape.Box;



/**

*

  • @author Arnaud

    /

    public class MainMenuState extends AbstractAppState {



    AppStateManager stateManager;

    SimpleApplication sapp;



    @Override

    public void initialize(AppStateManager stateManager, Application app) {

    this.sapp = (SimpleApplication) app;

    stateManager = sapp.getStateManager();



    /
    * A cube with base color "leaking" through a partially transparent texture /

    Box boxshape4 = new Box(new Vector3f(3f,-1f,0f), 1f,1f,1f);

    Geometry cube_leak = new Geometry("Leak-through color cube", boxshape4);

    Material mat_tl = new Material(sapp.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");

    mat_tl.setTexture("ColorMap", sapp.getAssetManager().loadTexture("Textures/ColoredTex/Monkey.png"));

    mat_tl.setColor("Color", new ColorRGBA(1f,0f,1f, 1f)); // purple

    cube_leak.setMaterial(mat_tl);

    sapp.getRootNode().attachChild(cube_leak);



    /
    * Must add a light to make the lit object visible! */

    DirectionalLight sun = new DirectionalLight();

    sun.setDirection(new Vector3f(1,0,-2).normalizeLocal());

    sun.setColor(ColorRGBA.White);

    sapp.getRootNode().addLight(sun);

    }



    @Override

    public void update(float tpf) {



    }

    }

    [/java]

It looks like it should work. I’m at a loss, actually.

In my use-cases, very similar code works fine. Maybe because I also do things on update()?



Edit: and note, if that’s why it works for me then I think this is a jme bug. Unless someone can spot the issue with your code.

Thanks for your replies pspeed,



I found a workaround, I finish my initialization in the update() method. Sounds ugly but it works:



[java]

@Override

public void update(float tpf) {

if(!attached) {

sapp.getRootNode().attachChild(cube_leak);

sapp.getRootNode().addLight(sun);

attached = true;

}

}

[/java]



I wonder if I’m using the game states correctly, there aren’t much advanced tutorials about them.

Yeah, you look like you are using them right to me. I think there is a JME bug maybe. Something that isn’t detecting the “dirtiness” of root node unless you change it during update().



…but I’m only guessing and have not looked at any of that code recently.

There is something else I want to notice, with this code:

[java]public class InGameState extends AbstractAppState {

AppStateManager myStateManager;

SimpleApplication sapp;

Geometry player;

DirectionalLight sun;

boolean attached = false;

@Override

public void initialize(AppStateManager stateManager, Application app) {

this.sapp = (SimpleApplication) app;

myStateManager = stateManager;

/** A simple textured cube – in good MIP map quality. /

Box boxshape1 = new Box(new Vector3f(-3f,1.1f,0f), 1f,1f,1f);

player = new Geometry(“My Textured Box”, boxshape1);

Material mat_stl = new Material(sapp.getAssetManager(), “Common/MatDefs/Misc/Unshaded.j3md”);

Texture tex_ml = sapp.getAssetManager().loadTexture(“Interface/Logo/Monkey.jpg”);

mat_stl.setTexture(“ColorMap”, tex_ml);

player.setMaterial(mat_stl);

[/java]

I can’t translate the playerGeometry like this:

[java]public void update(float tpf) {

if(MyGame.getPlayer().isEast()) {

Vector3f v = player.getLocalTranslation();

player.setLocalTranslation(v.x + value
speed, v.y, v.z);

}

}[/java]

The strange thing is that I can output player.getName() in the update() method but I have to do the whole player “initialization” inside update() to get the translation to work.

And how to get the speed value in a game state? is it always set to 1.0?

You should probably use SimpleApplication. Its easier and you can initialize all your stuff in simpleInitApp(). Works without problems… :slight_smile:

Actually I’m using SimpleApplication :). But you’re right, I’ll probably put my game objects in global variables and initialize them in simpleInitApp().

You can use an app state. That’s what they are for. I have almost no logic at all in my SimpleApplication subclass.



I’m not sure from your description exactly what the problem is, though. Was player ever added to the scene?