Chaging appState from niftyGUI event

Hi all!



I’m trying to develop a game using appStates (one for main menu and other for the game itself). I am able to change between states correctly using keys, but not the events of niftyGUI.



I’ve made a simple test case based upon TestAppStates which I publish the code here. Pease notice that the piece of code used to change states is the same for keyboard input and nifty event (changeState() method). I am using the appTasks because otherwise I was getting a NullPointerException (original code in changeState_OLD() method).



Thank you for your help.



Main class (TestAppStates)

[java]

package mygame;



import com.jme3.app.Application;

import com.jme3.input.KeyInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.niftygui.NiftyJmeDisplay;

import com.jme3.scene.Spatial;

import com.jme3.system.AppSettings;

import com.jme3.system.JmeContext;

import de.lessvoid.nifty.Nifty;

import de.lessvoid.nifty.screen.Screen;

import de.lessvoid.nifty.screen.ScreenController;



public class TestAppStates extends Application implements ScreenController, ActionListener{



RootNodeState state1;

RootNodeState state2;

boolean state=false; //false stands for state1



public static void main(String[] args){

TestAppStates app = new TestAppStates();

app.start();

}



@Override

public void start(JmeContext.Type contextType){

AppSettings settings = new AppSettings(true);

settings.setResolution(800, 600);

setSettings(settings);



super.start(contextType);

}



@Override

public void initialize(){

super.initialize();



System.out.println(“Initialize”);



state1 = new RootNodeState();

viewPort.attachScene(state1.getRootNode());

stateManager.attach(state1);



Spatial model1 = assetManager.loadModel(“Models/Teapot/Teapot.obj”);

model1.scale(3);

model1.setLocalTranslation(0, 1, 0);

model1.setMaterial(assetManager.loadMaterial(“Interface/Logo/Logo.j3m”));

state1.getRootNode().attachChild(model1);



state2 = new RootNodeState();

Spatial model2 = assetManager.loadModel(“Models/Teapot/Teapot.obj”);

model2.scale(2);

model2.setLocalTranslation(0, 1, 0);

model2.setMaterial(assetManager.loadMaterial(“Interface/Logo/Logo.j3m”));

state2.getRootNode().attachChild(model2);



NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,

inputManager,

audioRenderer,

guiViewPort);

niftyDisplay.getNifty().fromXml(“Interface/menu.xml”, “start”);

guiViewPort.addProcessor(niftyDisplay);



setupKeys();

}



@Override

public void update(){

super.update();



// do some animation

float tpf = timer.getTimePerFrame();



stateManager.update(tpf);

stateManager.render(renderManager);



// render the viewports

renderManager.render(tpf);

}



@Override

public void destroy(){

super.destroy();



System.out.println(“Destroy”);

}



//Old method (nifty gives null pointer exception)

public void changeState_OLD()

{

if(state)

{

viewPort.detachScene(state2.getRootNode());

stateManager.detach(state2);



viewPort.attachScene(state1.getRootNode());

stateManager.attach(state1);



state=false;

}

else

{

viewPort.detachScene(state1.getRootNode());

stateManager.detach(state1);



viewPort.attachScene(state2.getRootNode());

stateManager.attach(state2);



state=true;

}

}



public void changeState()

{

System.out.println(“changeState Called”);

if(state)

{

this.enqueue(new ChangeStateTask(state2,state1,viewPort,stateManager));

state=false;

}

else

{

this.enqueue(new ChangeStateTask(state1,state2,viewPort,stateManager));

state=true;

}

}



private void setupKeys() {

inputManager.addMapping(“change”, new KeyTrigger(KeyInput.KEY_C));

inputManager.addListener(this,“change”);

}



public void bind(Nifty nifty, Screen screen) {

}



public void onStartScreen() {

}



public void onEndScreen() {

}



public void onAction(String name, boolean isPressed, float tpf)

{

if (name.equals(“change”)) {

if(isPressed)

{

changeState();

}

}

}

}

[/java]



The Callable object for the task

[java]

/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.

    */



    package mygame;



    import com.jme3.app.state.AppState;

    import com.jme3.app.state.AppStateManager;

    import com.jme3.renderer.ViewPort;

    import java.util.concurrent.Callable;



    public class ChangeStateTask implements Callable

    {

    private RootNodeState oldState;

    private RootNodeState newState;

    private ViewPort viewPort;

    private AppStateManager stateManager;





    public ChangeStateTask(RootNodeState oldState, RootNodeState newState, ViewPort view, AppStateManager sm)

    {

    this.oldState=oldState;

    this.newState=newState;

    this.viewPort=view;

    this.stateManager=sm;

    }



    public Object call() throws Exception

    {

    System.out.println("ChangeStateTask called");



    viewPort.detachScene(oldState.getRootNode());

    stateManager.detach(oldState);



    viewPort.attachScene(newState.getRootNode());

    stateManager.attach(newState);



    return null;

    }

    }

    [/java]





    NiftyGUI file (menu.xml)

    [xml]

    <?xml version="1.0" encoding="UTF-8"?>

    <nifty xmlns="http://nifty-gui.sourceforge.net/nifty.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://nifty-gui.sourceforge.net/nifty.xsd http://nifty-gui.sourceforge.net/nifty.xsd">

    <!-- +++++++++++++++++++++++++++++++++++++++ -->

    <!-- start screen -->

    <!-- +++++++++++++++++++++++++++++++++++++++ -->

    <screen id="start" controller="mygame.TestAppStates">

    <layer id="layer" childLayout="center">

    <panel id="panel" height="25%" width="35%" align="center" valign="center" backgroundColor="#f60f" childLayout="center" visibleToMouse="true">

    <interact onClick="changeState()"/>

    <effect>

    <onStartScreen name="move" mode="in" direction="top" length="300" startDelay="0" inherit="true"/>

    <onEndScreen name="move" mode="out" direction="bottom" length="300" startDelay="0" inherit="true"/>

    <onHover name="pulsate" scaleFactor="0.008" startColor="#f600" endColor="#ffff" post="true"/>

    </effect>

    <text id="text" font="aurulent-sans-17.fnt" color="#000f" text="Hello World!" align="center" valign="center" />

    </panel>

    </layer>

    </screen>

    </nifty>

    [/xml]



    PS: forgot to mention, I am using jME3 alpha3 (jMP)

Maybe you have the same issue as me. I don’t have a solution either. This is my topic:

http://hub.jmonkeyengine.org/groups/gui/forum/topic/java-util-concurrentmodificationexception-with-niftygui/#post-112996

@joliver82

For the null pointer exception : It’s a classic error.



In your xml, you defined [java]controller=“mygame.TestAppStates”[/java]

With that, Nifty know what class it has to instanciate.

When you call [java]niftyDisplay.getNifty().fromXml(“Interface/menu.xml”, “start”);[/java]

Nifty will create a new instance of your class TestAppStates ( and not use the one you already set )



That’s why you get error like NullPointer exception - you use a class that has not been initialized;



For solving this, use this instead : [java]niftyDisplay.getNifty().fromXml(“Interface/menu.xml”, “start”, this);[/java]



@Vortex

All actions interfereing with jme3 should be done inside the task queue of your application.

Create a Callable object that do your action and call it like that

[java]mainApp.enqueue( callable );[/java]



That way, you’re sure that Nifty and JME3 are not trying to do concurrent changes.

@didialchichi: I have implemted what you suggested and it works now.



Big thanks from me :slight_smile:

Hello,



Sorry for the late reply, I was on hollidays.



Thank you @didialchichi for your comment, but I already solved it. I implemented the changing state class as a singleton and created other class for the interaction with nifty which gets the instance and calls the change state method (I didn’t know about the third parameter of fromXml :wink: ).



Probably I’ll modify my code following your advice