Nifty GUI - Start + Options as AppStates

Hi,



after having several problems with getting started I went back to the “Best Practices” and want to follow the right steps.

So I am working on my BaseGame and try to start with a GUI, having 2 Screens and every Screen it’s own custom control.

Implement generic game features in the MyBaseGame class: GUI (start screen, highscore screen), screen switching and pausing, the in-game HUD, level loading, saving and loading games, AppSettings, the main() method, etc.


StartScreen.xml and OptionsScreen.xml start with nifty tags and describe one screen each, showing only a simple text.

I try to follow the example in: https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:nifty_gui_scenarios

The Controlers are implemented in StartScreenState and OptionsScreenState, extending AbstractAppstate and Implementing ScreenController:

public class StartScreenState extends AbstractAppState implements ScreenController


The Exception occurs in simpleInitApp:
@Override
public void simpleInitApp() {
NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, viewPort);
Nifty nifty = niftyDisplay.getNifty();
nifty.addXml("Interface/Screens/OptionsScreen.xml");
nifty.addXml("Interface/Screens/StartScreen.xml");
nifty.gotoScreen("startScreen");
StartScreenState screenControl = (StartScreenState) nifty.getScreen("startScreen").getScreenController();
OptionsScreenState optionsControl = (OptionsScreenState) nifty.getScreen("optionsScreen").getScreenController();
stateManager.attach(screenControl);
stateManager.attach(optionsControl);
guiViewPort.addProcessor(niftyDisplay);
}


The cast to StartScreenState does not work:
java.lang.ClassCastException: de.lessvoid.nifty.screen.DefaultScreenController cannot be cast to mygame.StartScreenState
at mygame.BasicGame.simpleInitApp(BasicGame.java:26)
at com.jme3.app.SimpleApplication.initialize(SimpleApplication.java:231)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.initInThread(LwjglAbstractDisplay.java:129)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:205)
at java.lang.Thread.run(Thread.java:662)


I appreciate any help, hints to existing projects, but the code-snippets in the documentation realy don't help me getting all the things working together...

Thanks in advance,
thesun

The code snippets are more helpful when you read the text around them and not just copy/paste them. You never set the screen controller, why should you be able to get it? You get a default screen controller and that cannot be casted to your own obviously.

Yes. Have a look how I did it :



Thanks for the fast replies!

Still struggling but getting warmer I think:



@normen: I try to read anything there is to read

I try to use nifty with xml Input mainly and thought that:


should bind the Controller.

Documentation tells me it will try to find a corresponding controller (don't know where it will look ^^) and instantiate a new one else.
This does not seem to work, because my Constructor needs a SimpleApplication parameter, therefore defaultController is used and this one can't be cast to my StartScreenState.

So now I create the States beforehand and try to load Xml after the fact.
public void simpleInitApp() {
NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, viewPort);
Nifty nifty = niftyDisplay.getNifty();
StartScreenState startControl = new StartScreenState(this);
OptionsScreenState optionsControl = new OptionsScreenState(this);

nifty.fromXml("Interface/Screens/StartScreen.xml", "start", startControl);
//nifty.addXml("Interface/Screens/OptionsScreen.xml");
//nifty.addXml("Interface/Screens/StartScreen.xml");
nifty.gotoScreen("startScreen");
//StartScreenState startControl = (StartScreenState) nifty.getScreen("startScreen").getScreenController();
//OptionsScreenState optionsControl = (OptionsScreenState) nifty.getScreen("optionsScreen").getScreenController();
stateManager.attach(startControl);
stateManager.attach(optionsControl);
guiViewPort.addProcessor(niftyDisplay);
}


As addXml doesnt take a control I have to use fromXml which works for the first startControl but can't be used a second time with a different controller.
So this approach fails for the OptionController.

Is my approach, handling nifty in the BasicGame (there is Nifty object, required by bind in the States as well) wrong and I should try to change the initialize Operations of my States or where lies the problem.

@glaucomardano:
Mine looks nearly the same. I don't use an AbstractScreenController (what did you put there) and call the Controllers State, because they are both at the same time, as proposed in the documentation.
How do you initialize nifty with the different Screen.xml files?

All controllers should implements ScreenController.

do this for all controll classes:

nifty.registerScreenController(this);

and end with nifty.addControls();

Then you can go add the xml files.



ALSO, important!

In your xml file at the top in the screen initiation part you see:



Remember for your option xml file to change

controller=“start.MainClass” to controller=“OptionsScreenState” or where ever you have that file.



Hope this helps :slight_smile:

1 Like

the removed line looked like this:

screen id=“end” controller=“start.MainClass”

This helped me advance a lot, or so I hope:



Now i can start the app without Errors, but I will have to stop the Main Update Loop and get a Mouse to test advancing from one screen to the other.

The simpleInitApp looks now like this:


NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, viewPort);
Nifty nifty = niftyDisplay.getNifty();
StartScreenState startControl = new StartScreenState(this);
OptionsScreenState optionsControl = new OptionsScreenState(this);
nifty.registerScreenController(startControl);
nifty.registerScreenController(optionsControl);
nifty.addXml("Interface/Screens/OptionsScreen.xml");
nifty.addXml("Interface/Screens/StartScreen.xml");
nifty.gotoScreen("startScreen");
stateManager.attach(startControl);
stateManager.attach(optionsControl);
guiViewPort.addProcessor(niftyDisplay);


Now I will have to find out what should be initialized in the initializes of my AppStates.
They look like this now:

public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
/** init the screen */
}

You forgot to add the

nifty.addControls();

after

nifty.registerScreenController(startControl);

nifty.registerScreenController(optionsControl);

btw, it’s not needed to register the controllers yourself, they will be registered automatically when added to the nifty object. Then these tow lines aren’t needed:



[java]

nifty.registerScreenController(startControl);

nifty.registerScreenController(optionsControl);

[/java]



@thesun: Just create your xml screens like this:



[xml]

<screen id=“myscreen” controller=“MyScreen”>



</screen>

[/xml]



and for each screen xml file create its controller, and if you wanna access the app and stateManager objects, just extends AbstractAppState:



[java]

public class MyScreen extends AbstractAppState implements ScreenController {



//You’ll access the app and stateManager objects here

public void initialize(Application app, StateManager stateManager){}

public void bind(…){}

public void onStartScreen(){}

public void onEndScreen(){}

}

[/java]



THen in simpleInitApp you do:



[java]

@Override

public void simpleInitApp() {

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

Nifty nifty = niftyDisplay.getNifty();

nifty.addXml(“Interface/Screens/myscreen.xml”);

nifty.gotoScreen(“startScreen”);

MyScreen screenControl = (MyScreen) nifty.getScreen(“myscreen”).getScreenController();

stateManager.attach(screenControl);

guiViewPort.addProcessor(niftyDisplay);

}

[/java]



btw wtf? All this is on wiki man!!! Just read!

@Addez: thanks, changed that



@glaucomardano: Thanks, I tried it your way and this was the same I startet from. Triedt with controller=“package.Classname” and controller=“Classname” but the MyScreen cast fails because it tries to cast a DefaultScreenControl as did before.

I read every last line on guis, apps, states and controls in the wiki but I don’t get the examples to work.

Must be missing some crucial point

Could you give us the output error?

of course:



Error is

com.jme3.app.Application handleError

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

java.lang.ClassCastException: de.lessvoid.nifty.screen.DefaultScreenController cannot be cast to mygame.StartScreenState

at mygame.BasicGame.simpleInitApp(BasicGame.java:42)

at com.jme3.app.SimpleApplication.initialize(SimpleApplication.java:231)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.initInThread(LwjglAbstractDisplay.java:129)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:205)

at java.lang.Thread.run(Thread.java:662)




and this is preceded by:

13.11.2011 13:15:09 de.lessvoid.xml.tools.ClassHelper getInstance
WARNUNG: class [StartScreenState] could not be instanziated (java.lang.ClassNotFoundException: StartScreenState)
13.11.2011 13:15:09 de.lessvoid.nifty.screen.Screen
INFO: Missing ScreenController for screen [startScreen] using DefaultScreenController() instead but this might not be what you want.

when controller="StartScreenState" and:
13.11.2011 13:20:01 de.lessvoid.xml.tools.ClassHelper getInstance
WARNUNG: class [mygame.StartScreenState] could not be instanziated (java.lang.InstantiationException: mygame.StartScreenState)
13.11.2011 13:20:01 de.lessvoid.nifty.screen.Screen
INFO: Missing ScreenController for screen [startScreen] using DefaultScreenController() instead but this might not be what you want.


when controller="mygame.StartScreenState".

As I read this, the first approach doesn't find the class and so packagename is indeed a good idea.
But still it looks like nifty can't instantiate a ScreenController that implements AbstractAppState and therefore needs a SimpleApplication parameter to it's constructor.
But still it looks like nifty can’t instantiate a ScreenController that implements AbstractAppState and therefore needs a SimpleApplication parameter to it’s constructor.

No. You are thinking bad things ;). Just do how I do. That example you saw at your first post was retired from my project, then that really work. I'm using my screen controllers as AppState as well. You simply aren't mapping your screens correctly, then the nifty uses the default screen controller instead.

If you are letting nifty instantiate your screen controller then it will need a no-arg constructor. Otherwise you have to instantiate it yourself and pass it to nifty.loadXml().