Nifty + AbstractAppState : How to attach a state from inside a method of another state?

Hello,



I’ve got 2 states:



[java]

public class State_Start extends AbstractAppState implements ScreenController

public class State_Game extends AbstractAppState implements ScreenController

[/java]



This is the Main class:



[java]

public class Main extends SimpleApplication {



private State_Start state_Start;

private State_Game state_Game;





public static void main(String[] args) {

Main app = new Main();

app.start();

}



@Override

public void simpleInitApp() {



state_Start = new State_Start(this);

state_Game = new State_Game(this);

//stateManager.attach(state_Game);

stateManager.attach(state_Start);

//state_Game.setEnabled(true);// false => no update

state_Start.setEnabled(true);



}

[/java]



And now my question, I try to do [java]stateManager.attach(state_Game); [/java] from inside

a method of State_Start, actually on my nifty screen i have a simple button to start the game. The nifty screen switches to nextScreen, but i also want to detach “state_Start” and attach “state_Game”. But it’s just not working.



In my main class i did instantiate both states:

[java]

state_Start = new State_Start(this);

state_Game = new State_Game(this);

[/java]

I created a method in my main class to return “state_Game”

[java]

public State_Game getstate_Game() {

return state_Game;

}

[/java]

an then tried this in State_Start

[java]

public void startGame(String nextScreen) {

stateManager.attach(app. getstate_Game());

nifty.gotoScreen(nextScreen);

}

[/java]

But its not attaching state_Game.

What do i do wrong?

You really don’t need to pass the app param in your controllers’ contructors. Once your controllers extends AbstractAppState they have access to the app and stateManager objects. Just override the initialize(Application app, StateManager stateManager) and initialize your app and stateManager objects. Then if you wanna detaching a “Screen AppState” from stateManager object when the user leaves it, then just call stateManager.detach(MyScreenState.class) inside its onEndScreen() method. You can do the same for the onStartScreen() and instead of detaching it from stateManager, you attach it. Hope this helps!

1 Like

How you know its not attaching state_Game? Does nifty go to the next screen? You do not detach your state_Start, maybe you just hide your other state somehow?

What version of JME are you running?



Do your states set initialized to true right away or otherwise always return true from isInitialized()?



I too wonder how you know it’s not getting attached. Did you put logging in the state’s initialize() or update()?

@normen

Actually when I said it’s not attaching i meant “i receive an error” => cannot find symbol: method getstate_Game(), so it didnt even run.



@pspeed

I’m running the beta release, no further updates.



@glaucomardano

ok, i did remove the app param in my controllers constructors, but now i got a nullpointerproblem:

=>SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]

java.lang.NullPointerException

at mygame.State_Start.(State_Start.java:48)

[java]

public State_Start(){

System.out.println(“Inside constructor”);

rootNode = app.getRootNode(); //<== line 48

viewPort = app.getViewPort();

guiNode = app.getGuiNode();

assetManager = app.getAssetManager();

inputManager = app.getInputManager();

audioRenderer = app.getAudioRenderer();

}

[/java]

This is my @Override initialize:

[java]

@Override

public void initialize(AppStateManager stateManager, Application app) {

System.out.println(“Inside initialize”);

super.initialize(stateManager, app);

this.app= (SimpleApplication)app;

/** Init this scene /

viewPort.setBackgroundColor(backgroundColor);

NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(

assetManager, inputManager, audioRenderer, viewPort);

/
* Create a new NiftyGUI object /

Nifty nifty = niftyDisplay.getNifty();

/
* Read your XML and initialize your custom ScreenController */

nifty.fromXml(“Interface/screen.xml”, “start”, this);

// attach the Nifty display to the gui view port as a processor

viewPort.addProcessor(niftyDisplay);

inputManager.setCursorVisible(true);

}

[/java]

I don’t see why i’m getting this NullPointerException

App is null. Maybe you are calling the app variable before it is initialized, because you called it in constructor, and at this stage it wasn’t attached to the stateManager yet. And, Wtf is that method “State_Start”? Screen Controller has a method called “onStartScreen”, just override it and attach it to the stateManager as well. ALso, you don’t have to initialize the variables there, initialize them in bind(Screen screen, Nifty nifty) method by overriding it.


>the bind() method is to initialize the screen and nifty objects, the other objects initialize in constructor method.

EDIT: Make sure you attached your screen to the stateManager before you try to access the app and stateManager variables in initialize(Application app, StateManager stateManager) method. Otherwise you'll get a NPE of course.

“cannot find symbol: method getstate_Game()” means you try to call that method on another class and not on your application class. You probably have to cast your application variable to your application class name: MyApplication app = (Application) application;

@normen said:
MyApplication app = (Application) application;


MyApplication app = (MyApplication) application; :)
1 Like

Yeah, right :slight_smile:

Well i did try as suggested but without success. So i took another approach.



I created a Mainclass extends SimpleApplication:

[java]

package mygame;



import com.jme3.app.SimpleApplication;

import com.jme3.renderer.RenderManager;



import de.lessvoid.nifty.Nifty;

import com.jme3.niftygui.NiftyJmeDisplay;



public class MainClass extends SimpleApplication {



public static void main(String args) {

MainClass appChess = new MainClass();

appChess.start();

}



@Override

public void simpleInitApp() {



NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, viewPort);

Nifty nifty = niftyDisplay.getNifty();

nifty.addXml("Interface/screens/screenOptions.xml");

nifty.addXml("Interface/screens/screenGame.xml");

nifty.addXml("Interface/screens/screenStart.xml");

nifty.gotoScreen("start");



StartScreenController startScreenController = (StartScreenController) nifty.getScreen("start").getScreenController();

OptionsScreenController optionsScreenController = (OptionsScreenController) nifty.getScreen("options").getScreenController();

GameScreenController gameScreenController = (GameScreenController) nifty.getScreen("game").getScreenController();



stateManager.attach(startScreenController);

stateManager.attach(optionsScreenController);

stateManager.attach(gameScreenController);



guiViewPort.addProcessor(niftyDisplay);

inputManager.setCursorVisible(true);



}



@Override

public void simpleUpdate(float tpf) {

//TODO: add update code

}



@Override

public void simpleRender(RenderManager rm) {

//TODO: add render code

}



}



[/java]

Next i created 1 AbstractScreenController extends AbstractAppState implements ScreenController:

[java]

package mygame;



import com.jme3.app.Application;

import com.jme3.app.SimpleApplication;

import com.jme3.app.state.AbstractAppState;

import com.jme3.app.state.AppStateManager;

import de.lessvoid.nifty.Nifty;

import de.lessvoid.nifty.screen.Screen;

import de.lessvoid.nifty.screen.ScreenController;



public class AbstractScreenController extends AbstractAppState implements ScreenController {



protected Nifty nifty;

protected Screen screen;

protected SimpleApplication sapp;

protected AppStateManager myStateManager;



public AbstractScreenController(){

}



@Override

public void initialize(AppStateManager stateManager, Application app) {

}



@Override

public void update(float tpf) {

}



@Override

public void stateAttached(AppStateManager stateManager) {

}



@Override

public void stateDetached(AppStateManager stateManager) {

}



public void bind(Nifty nifty, Screen screen) {

this.nifty = nifty;

this.screen = screen;

}



public void onStartScreen() {

}



public void onEndScreen() {

myStateManager.detach(this);

}



public void gotoscreen(String nextscreen) {

nifty.gotoScreen(nextscreen);

}



public void quitGame() {

System.out.println("quitGame");

sapp.stop();

}

}

[/java]

Next i created 3 Appstates:

StartScreenController extends AbstractScreenController

GameScreenController extends AbstractScreenController

OptionsScreenController extends AbstractScreenController



[java]

package mygame;



import com.jme3.app.Application;

import com.jme3.app.state.AppStateManager;

import com.jme3.app.SimpleApplication;



public class StartScreenController extends AbstractScreenController {



public StartScreenController(){

}



@Override

public void initialize(AppStateManager stateManager, Application app) {

//super.initialize(stateManager, app);

sapp = (SimpleApplication)app;

myStateManager = stateManager;

}



@Override

public void update(float tpf) {

System.out.println("state_start");

}



@Override

public void stateAttached(AppStateManager stateManager) {

}



@Override

public void stateDetached(AppStateManager stateManager) {

}



@Override

public void onStartScreen() {

}



@Override

public void onEndScreen() {

super.onEndScreen();

//myStateManager.detach(this);

}

}

[/java]



Now my question: for now I’m attaching the 3 AppStates to …in my MainClass:

stateManager.attach(startScreenController);

stateManager.attach(optionsScreenController);

stateManager.attach(gameScreenController);



Switching from screen to screen works perfectly. But what i would like is to have only 1 AppState attached to the stateManager at any given time. So when im at the start screen i only want startScreenController to be attached, when i’m at the game screen i want only gameScreenController to be attached, same for optionsscreen.



I think this is good practise to save recources. Ok not in my example but i can imagine having states that load models, textures, sounds,… that you dont need all the time, but only when a particular screen is open



Ok so this has to be done by Overriding onStartScreen() and onEndScreen().

Detach works as intended just like this: myStateManager.detach(this);

I test this with a println in the update of each state

System.out.println(“state_game”);

System.out.println(“state_start”);

System.out.println(“state_options”);

i can see the state detaching when i switch screens.

Problem now is attaching


@glaucomardano said:
Then if you wanna detaching a "Screen AppState" from stateManager object when the user leaves it, then just call stateManager.detach(MyScreenState.class) inside its onEndScreen() method. You can do the same for the onStartScreen() and instead of detaching it from stateManager, you attach it. Hope this helps!


Ok so i tried this : myStateManager.attach(StartScreenController.class);
But this gives an error:

method attach in class com.jme3.app.state.AppStateManager cannot be applied to given types;
required: com.jme3.app.state.AppState
found: java.lang.Class
reason: actual argument java.lang.Class cannot be converted to com.jme3.app.state.AppState by method invocation conversion

Actually what i would like to be able to do is:
1. Create and attach an Appstate instance when needed, and destroy when no longer needed.(based on nifty screen switching, by using onStartScreen() and onEndScreen().
2. re-Attach an existing Appstate instance that i didnt destroy beceause it's still needed.

I guess the fact that my Appstates are also Screencontrollers do make this more complicate.

Sorry for this extremely long post, By the way i started java only 2-3 weeks ago so that also explains alot.

Ok so i tried this : myStateManager.attach(StartScreenController.class);


Do you know the difference between a class and an instance? Attach needs an actual instance, ie: like new StartScreenController() ... or a previously created one.

This is one of the problems I have with the prescribed method of letting nifty create these for you. I think it makes the code more complicated and a little too magic for most new developers.

At this point, I'm not sure how to continue without confusing you more, though.
1 Like

Okey, so in my code this line:

StartScreenController startScreenController = (StartScreenController) nifty.getScreen(“start”).getScreenController();

will create a screencontroller instance and assign it to startScreenController , since nifty will create an instance if no instance of the screencontroller exists.



in other words nifty.getScreen(“start”) invokes the creation of a screencontroller, the class is defined in the xml

==>

So its creates an instance of StartScreenController and assigns it to startScreenController (StartScreenController startScreenController :slight_smile:



Now in my mainclass i commented the following lines:

[java]

StartScreenController startScreenController = (StartScreenController) nifty.getScreen(“start”).getScreenController();

// OptionsScreenController optionsScreenController = (OptionsScreenController) nifty.getScreen(“options”).getScreenController();

// GameScreenController gameScreenController = (GameScreenController) nifty.getScreen(“game”).getScreenController();



stateManager.attach(startScreenController);

// stateManager.attach(optionsScreenController);

// stateManager.attach(gameScreenController);

[/java]

So at the start of the game i have only 1 existing screencontroller which is also an AppState, that is attached to the statemanager.

i hope so far i understand things correctly.



Next i created 3 functions, 1 for each possible controller / state, in my AbstractScreenController, so these functions are called when i switch screens



[java]

public void gotogame() {

GameScreenController gameScreenController = (GameScreenController) nifty.getScreen(“game”).getScreenController();

myStateManager.attach(gameScreenController);

nifty.gotoScreen(“game”);

}



public void gotooptions() {

OptionsScreenController optionsScreenController = (OptionsScreenController) nifty.getScreen(“options”).getScreenController();

myStateManager.attach(optionsScreenController);

nifty.gotoScreen(“options”);

}



public void gotostart() {

StartScreenController startScreenController = (StartScreenController) nifty.getScreen(“start”).getScreenController();

myStateManager.attach(startScreenController);

nifty.gotoScreen(“start”);

}

[/java]



Detaching each time is done by onEndScreen() in AbstractScreenController

[java]

public void onEndScreen() {

myStateManager.detach(this);

}

[/java]

This seem to do exactly what i want, create , attach, detach a controller / state when needed



Do you think this " a " good way to do it. By “it” i mean attaching, detaching, creating screencontroller / states at will.

I really don’t think it’s needed to create the controllers by hand in goToOptions() and goToStart() methods, once you add your xml screens to the nifty by nifty.addXml(“myScreen.xml”) it will automatically create a controller for you, then inside your goTo’s just use nifty.gotoScreen(“somehell”); . Aditionally you can attach the screen controller to the stateManager in your goTo’ by:



[java][/java]

public void goToSomeHell() {

nifty.gotoScreen(“someHell”);

SomeHellController someHell = (SomeHellController)nifty.getCurrentScreen().getScreenController();

stateManager.attach(someHell);

}

[java][/java]



Your onEndScreen() is fine.

@glaucomardano said:
I really don't think it's needed to create the controllers by hand in goToOptions() and goToStart() methods, once you add your xml screens to the nifty by nifty.addXml("myScreen.xml") it will automatically create a controller for you, then inside your goTo's just use nifty.gotoScreen("somehell"); . Aditionally you can attach the screen controller to the stateManager in your goTo' by:

[java][/java]
public void goToSomeHell() {
nifty.gotoScreen(&quot;someHell&quot;);
SomeHellController someHell = (SomeHellController)nifty.getCurrentScreen().getScreenController();
stateManager.attach(someHell);
}
[java][/java]

Your onEndScreen() is fine.


I think his way is safer than yours as yours makes quite a few assumptions about when things are actually realized. For example, you don't know for sure that getCurrentScreen() will return the correct screen right away after calling gotoScreen. It might... but it seems risky to rely on it. Furthermore, if it _does_ switch right away then you run the risk of having methods called on your screen controller before it's been attached and initialized.

In my opinion, it's way better to retrieve, attach, then goto as in @mr-jor's most recent version.
1 Like

Thankyou for the advice, its working. I really appriciate the help i received from everybody



[java]

public void gotogame() {

nifty.gotoScreen("game");

GameScreenController gameScreenController = (GameScreenController) nifty.getCurrentScreen().getScreenController();

myStateManager.attach(gameScreenController);



}



public void gotooptions() {

nifty.gotoScreen("options");

OptionsScreenController optionsScreenController = (OptionsScreenController) nifty.getCurrentScreen().getScreenController();

myStateManager.attach(optionsScreenController);



}



public void gotostart() {

nifty.gotoScreen("start");

StartScreenController startScreenController = (StartScreenController) nifty.getCurrentScreen().getScreenController();

myStateManager.attach(startScreenController);



}

[/java]

oeps, i better click refresh before i make a new post :slight_smile:

Then i will revert the changes as it is more safe:



[java]

public void gotogame() {

GameScreenController gameScreenController = (GameScreenController) nifty.getScreen("game").getScreenController();

myStateManager.attach(gameScreenController);

nifty.gotoScreen("game");

}



public void gotooptions() {

OptionsScreenController optionsScreenController = (OptionsScreenController) nifty.getScreen("options").getScreenController();

myStateManager.attach(optionsScreenController);

nifty.gotoScreen("options");

}



public void gotostart() {

StartScreenController startScreenController = (StartScreenController) nifty.getScreen("start").getScreenController();

myStateManager.attach(startScreenController);

nifty.gotoScreen("start");

}

[/java]

@pspeed: Yes, the getCurrentScreen() might don’t be much safe, but there’s another way to get the screen :slight_smile: :



[java]

getNifty().getScreen(“somehell”);

[/java]



Does not it be safe? :slight_smile:

@glaucomardano said:
@pspeed: Yes, the getCurrentScreen() might don't be much safe, but there's another way to get the screen :) :

[java]
getNifty().getScreen(&quot;somehell&quot;);
[/java]

Does not it be safe? :)


Yeah, but that's exactly what mr-jor is doing... so what's your point?

Sequence of events as I saw them:
-mr-jor showed code that uses getScreen()
-you showed code that uses getCurrentScreen() and suggested he use it instead.
-I said your code wasn't as safe as his original code
-mr-jor is back to using getScreen() instead.

The only other difference is that I felt you should also attach the state before going to the screen... which is also what mr-jor is doing.

Hmmmm! yeah xD. I thought he was creating a screen controller instance in each goTo method, but he’s just retrieving the existent screen controllers :).

Then the code above is fine :).

just checked how my abstract screen controller was, and it’s using the nifty.getScreen(id) method. I didn’t know why I was talking about the getCurrentScreen() method, maybe I was drunk xD.



[java]

public void goToScreen(String id) {

getStateManager().attach((AppState) getNifty().getScreen(id).getScreenController());

getNifty().gotoScreen(id);

}

[/java]