Simple POC with Nifty, but problems switching from screen to screen

Hi everyone,



I’m learning jMonkey and I decided to try my hand at Nifty Gui. I can’t seem to find the right way to switch from screen to screen. I feel that I’m not too far from a simple working example, but it seems that two things are missing:

1- Why should I need to reload from the xml file each time I want to switch screen, it’s very slow? There’s gotta be something missing.

2- When I debug the code, I see that my screencontrollers are initialized. So the field “Main app” is loaded at that time. But when I get to click on a button that calls a method from the “app” field, the app field is null…



Oh, I forgot to mention. Everything works fine on the first screen. Problems appear when I switch to another screen.



The project is pretty small, so here are the 5 files required to make this work:



the nifty gui xml file (no problem here)

[xml]<?xml version=“1.0” encoding=“UTF-8”?>

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

<useControls filename=“nifty-default-controls.xml” />

<useStyles filename=“nifty-default-styles.xml” />

<screen id=“start” controller=“niftypoc.screen1”>

<layer id=“layer” backgroundColor="#0000" childLayout=“vertical”>

<panel id=“topPanel” height="" width=“100%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical” />

<panel id=“bottomPanel” height=“85px” width=“100%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“horizontal”>

<panel id=“bottomPanel1” height=“100%” width=“33%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical” style=“nifty-panel-no-shadow”>

<effect>

<onStartScreen name=“move” mode=“in” direction=“bottom” length=“300” startDelay=“100” inherit=“true”/>

<onEndScreen name=“move” mode=“out” direction=“bottom” length=“300” startDelay=“100” inherit=“true”/>

</effect>

<control name=“label” text=“This is screen 1” width=“100%” />

<control name=“button” id=“btnRedCube” label=“Make cube red” width=“100%”>

<interact onClick=“btnRedCubeClick()”/>

</control>

<control name=“button” id=“btnGoToScreen2” label=“Go to screen 2” width=“100%”>

<interact onClick=“btnGoToScreen2Click()”/>

</control>

</panel>

<panel id=“bottomPanel2” height=“100%” width=“33%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical”>

</panel>

<panel id=“bottomPanel3” height=“100%” width="
" align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical”>

</panel>

</panel>

</layer>

</screen>

<screen id=“screen2” controller=“niftypoc.screen2”>

<layer id=“layer” backgroundColor="#0000" childLayout=“vertical”>

<panel id=“topPanel” height="" width=“100%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical” />

<panel id=“bottomPanel” height=“85px” width=“100%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“horizontal”>

<panel id=“bottomPanel1” height=“100%” width=“33%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical”>

</panel>

<panel id=“bottomPanel2” height=“100%” width=“33%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical” style=“nifty-panel-no-shadow”>

<effect>

<onStartScreen name=“move” mode=“in” direction=“bottom” length=“300” startDelay=“100” inherit=“true”/>

<onEndScreen name=“move” mode=“out” direction=“bottom” length=“300” startDelay=“100” inherit=“true”/>

</effect>

<control name=“label” text=“This is screen 2” width=“100%” />

<control name=“button” id=“btnGreenCube” label=“Make cube green” width=“100%”>

<interact onClick=“btnGreenCubeClick()”/>

</control>

<control name=“button” id=“btnGoToScreen3” label=“Go to screen 3” width=“100%”>

<interact onClick=“btnGoToScreen3Click()”/>

</control>

</panel>

<panel id=“bottomPanel3” height=“100%” width="
" align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical”>

</panel>

</panel>

</layer>

</screen>

<screen id=“screen3” controller=“niftypoc.screen3”>

<layer id=“layer” backgroundColor="#0000" childLayout=“vertical”>

<panel id=“topPanel” height="" width=“100%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical” />

<panel id=“bottomPanel” height=“85px” width=“100%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“horizontal”>

<panel id=“bottomPanel1” height=“100%” width=“33%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical”>

</panel>

<panel id=“bottomPanel2” height=“100%” width=“33%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical”>

</panel>

<panel id=“bottomPanel3” height=“100%” width="
" align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical” style=“nifty-panel-no-shadow”>

<effect>

<onStartScreen name=“move” mode=“in” direction=“bottom” length=“300” startDelay=“100” inherit=“true”/>

<onEndScreen name=“move” mode=“out” direction=“bottom” length=“300” startDelay=“100” inherit=“true”/>

</effect>

<control name=“label” text=“This is screen 3” width=“100%” />

<control name=“button” id=“btnBlueCube” label=“Make cube blue” width=“100%”>

<interact onClick=“btnBlueCubeClick()”/>

</control>

<control name=“button” id=“btnGoToScreen1” label=“Go to screen 1” width=“100%”>

<interact onClick=“btnGoToScreen1Click()”/>

</control>

</panel>

</panel>

</layer>

</screen>

</nifty>[/xml]



the main class (maybe something missing?)

[java]package niftypoc;



import com.jme3.app.SimpleApplication;

import com.jme3.app.state.AppState;

import com.jme3.input.MouseInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.MouseButtonTrigger;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector3f;

import com.jme3.renderer.RenderManager;

import com.jme3.scene.Geometry;

import com.jme3.scene.shape.Box;

import java.util.logging.Level;

import java.util.logging.Logger;



/**

  • test
  • @author normenhansen

    */

    public class Main extends SimpleApplication {



    Geometry geom;

    /**
  • Field to hold the Gui main screen

    */

    public AppState niftyScreen1 = new screen1();

    /**
  • Field to hold the Gui mesh edit screen

    */

    public AppState niftyScreen2 = new screen2();

    /**
  • Field to hold the Gui hidden screen

    */

    public AppState niftyScreen3 = new screen3();



    /**
  • Standard code to get logger

    */

    private static final Logger logger = Logger.getLogger(Main.class.getName());



    public static void main(String[] args) {

    Main app = new Main();

    app.start();

    }



    @Override

    public void simpleInitApp() {

    SetupInterface();



    Box b = new Box(Vector3f.ZERO, 1, 1, 1);

    geom = new Geometry("Box", b);



    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");

    mat.setColor("Color", ColorRGBA.Blue);

    geom.setMaterial(mat);



    rootNode.attachChild(geom);

    }



    @Override

    public void simpleUpdate(float tpf) {

    //TODO: add update code

    }



    @Override

    public void simpleRender(RenderManager rm) {

    //TODO: add render code

    }



    public void MakeCubeRed() {

    logger.log(Level.INFO, "Color was changed to Red");

    geom.getMaterial().setColor("Color", ColorRGBA.Red);

    }



    public void MakeCubeGreen() {

    logger.log(Level.INFO, "Color was changed to Green");

    geom.getMaterial().setColor("Color", ColorRGBA.Green);

    }



    public void MakeCubeBlue() {

    logger.log(Level.INFO, "Color was changed to Blue");

    geom.getMaterial().setColor("Color", ColorRGBA.Blue);

    }



    private void SetupInterface() {

    flyCam.setEnabled(false);

    //Allow mouse be visible Much easier to interact with gui

    inputManager.setCursorVisible(true);

    /**
  • Attach the gui class to the application

    /

    stateManager.attach(niftyScreen1);

    }

    }[/java]



    The first screen controller

    [java]/

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

    */

    package niftypoc;



    import com.jme3.app.Application;

    import com.jme3.app.state.AbstractAppState;

    import com.jme3.app.state.AppStateManager;

    import com.jme3.niftygui.NiftyJmeDisplay;

    import de.lessvoid.nifty.Nifty;

    import de.lessvoid.nifty.screen.Screen;

    import de.lessvoid.nifty.screen.ScreenController;

    import java.util.logging.Logger;



    /**

    *
  • @author vchevali

    */

    public class screen1 extends AbstractAppState implements ScreenController {



    //standard field

    private Nifty nifty;

    //standard field

    private Screen screen;

    //standard field

    private Main app;



    /**
  • Standard code to get logger

    /

    private static final Logger logger = Logger.getLogger(Main.class.getName());



    public void bind(Nifty nifty, Screen screen) {

    //standard code to bind controller to nifty gui elements

    this.nifty = nifty;

    this.screen = screen;

    }



    public void onStartScreen() {

    //nothing

    }



    public void onEndScreen() {

    //nothing

    }



    /
    * jME3 AppState methods /

    @Override

    public void initialize(AppStateManager stateManager, Application app) {

    //standard code to bid app state to the application

    super.initialize(stateManager, app);

    this.app=(Main)app;



    //Create nifty Gui display, load screen from the xml file and display screen, standard behavior

    //only for the main screen, no need to load from xml for other screens

    NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(app.getAssetManager(),

    app.getInputManager(),

    app.getAudioRenderer(),

    app.getGuiViewPort());

    nifty = niftyDisplay.getNifty();

    nifty.fromXml("Interface/NiftyPoc.xml", "start", this);

    app.getGuiViewPort().addProcessor(niftyDisplay);

    }



    @Override

    public void update(float tpf) {

    /
    * jME update loop! */

    }



    @Override

    public void stateAttached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached to the application

    }



    @Override

    public void stateDetached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached from the application

    }



    /**
  • Method called by the btnGoToScreen2

    */

    public void btnGoToScreen2Click() {

    app.getStateManager().detach(app.niftyScreen1);

    app.getStateManager().attach(app.niftyScreen2);

    nifty.gotoScreen("screen2");

    }



    /**
  • Method called by the btnRedCube

    /

    public void btnRedCubeClick() {

    app.MakeCubeRed();

    }



    }

    [/java]



    The second screen controller (almost identical to the first)

    [java]/

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

    */

    package niftypoc;



    import com.jme3.app.Application;

    import com.jme3.app.state.AbstractAppState;

    import com.jme3.app.state.AppStateManager;

    import com.jme3.niftygui.NiftyJmeDisplay;

    import de.lessvoid.nifty.Nifty;

    import de.lessvoid.nifty.screen.Screen;

    import de.lessvoid.nifty.screen.ScreenController;

    import java.util.logging.Logger;



    /**

    *
  • @author vchevali

    */

    public class screen2 extends AbstractAppState implements ScreenController {



    //standard field

    private Nifty nifty;

    //standard field

    private Screen screen;

    //standard field

    private Main app;



    /**
  • Standard code to get logger

    /

    private static final Logger logger = Logger.getLogger(Main.class.getName());



    public void bind(Nifty nifty, Screen screen) {

    //standard code to bind controller to nifty gui elements

    this.nifty = nifty;

    this.screen = screen;

    }



    public void onStartScreen() {

    //nothing

    }



    public void onEndScreen() {

    //nothing

    }



    /
    * jME3 AppState methods /

    @Override

    public void initialize(AppStateManager stateManager, Application app) {

    //standard code to bid app state to the application

    super.initialize(stateManager, app);

    this.app=(Main)app;



    //Create nifty Gui display, load screen from the xml file and display screen, standard behavior

    //only for the main screen, no need to load from xml for other screens

    NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(app.getAssetManager(),

    app.getInputManager(),

    app.getAudioRenderer(),

    app.getGuiViewPort());

    nifty = niftyDisplay.getNifty();

    nifty.fromXml("Interface/NiftyPoc.xml", "screen2", this);

    app.getGuiViewPort().addProcessor(niftyDisplay);

    }



    @Override

    public void update(float tpf) {

    /
    * jME update loop! */

    }



    @Override

    public void stateAttached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached to the application

    }



    @Override

    public void stateDetached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached from the application

    }



    /**
  • Method called by the btnGoToScreen3

    */

    public void btnGoToScreen3Click() {

    app.getStateManager().detach(app.niftyScreen2);

    app.getStateManager().attach(app.niftyScreen3);

    nifty.gotoScreen("screen3");

    }



    /**
  • Method called by the btnGreenCube

    /

    public void btnGreenCubeClick() {

    app.MakeCubeGreen();

    }



    }[/java]



    The third screen controller (almost identical to the first two)

    [java]/

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

    */

    package niftypoc;



    import com.jme3.app.Application;

    import com.jme3.app.state.AbstractAppState;

    import com.jme3.app.state.AppStateManager;

    import com.jme3.niftygui.NiftyJmeDisplay;

    import de.lessvoid.nifty.Nifty;

    import de.lessvoid.nifty.screen.Screen;

    import de.lessvoid.nifty.screen.ScreenController;

    import java.util.logging.Logger;



    /**

    *
  • @author vchevali

    */

    public class screen3 extends AbstractAppState implements ScreenController {



    //standard field

    private Nifty nifty;

    //standard field

    private Screen screen;

    //standard field

    private Main app;



    /**
  • Standard code to get logger

    /

    private static final Logger logger = Logger.getLogger(Main.class.getName());



    public void bind(Nifty nifty, Screen screen) {

    //standard code to bind controller to nifty gui elements

    this.nifty = nifty;

    this.screen = screen;

    }



    public void onStartScreen() {

    //nothing

    }



    public void onEndScreen() {

    //nothing

    }



    /
    * jME3 AppState methods /

    @Override

    public void initialize(AppStateManager stateManager, Application app) {

    //standard code to bid app state to the application

    super.initialize(stateManager, app);

    this.app=(Main)app;



    //Create nifty Gui display, load screen from the xml file and display screen, standard behavior

    //only for the main screen, no need to load from xml for other screens

    NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(app.getAssetManager(),

    app.getInputManager(),

    app.getAudioRenderer(),

    app.getGuiViewPort());

    nifty = niftyDisplay.getNifty();

    nifty.fromXml("Interface/NiftyPoc.xml", "screen3", this);

    app.getGuiViewPort().addProcessor(niftyDisplay);

    }



    @Override

    public void update(float tpf) {

    /
    * jME update loop! */

    }



    @Override

    public void stateAttached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached to the application

    }



    @Override

    public void stateDetached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached from the application

    }



    /**
  • Method called by the btnGoToScreen1

    */

    public void btnGoToScreen1Click() {

    app.getStateManager().detach(app.niftyScreen3);

    app.getStateManager().attach(app.niftyScreen1);

    nifty.gotoScreen("start");

    }



    /**
  • Method called by the btnBlueCube

    */

    public void btnBlueCubeClick() {

    app.MakeCubeBlue();

    }



    }[/java]



    By the way, I’m learning Java along jMonkey and NiftyGui, so please bear with me.



    Thanks a lot!

Oh, maybe in case the problem wasn’t stated clearly enough, since the app field isn’t loaded properly in screen2 and screen3, any calls made to the app field crashes. (example: buttons makeCubeGreen, makeCubeBlue, switchToScreen3, etc)



Thanks! :slight_smile:

Hi guys, I took another step, but I’m not sure I’m headed in the right direction… I use my little POC to make different tests and I get to a point where if I load each screen from XML when I attach the appstate, it works, but if I do not load from xml and simply call a “gotoscreen”, nothings works. I am really lost here. I’m not sure if the problems is how I use Nifty or I use AppStates…



The xml file didn’t change in this test. Se the first post for the XML file.



In the main class, I centralized the niftyDisplay and nifty objects and screencontrollers now refer to the main class fields.

[java]package niftypoc;



import com.jme3.app.SimpleApplication;

import com.jme3.app.state.AppState;

import com.jme3.input.MouseInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.MouseButtonTrigger;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector3f;

import com.jme3.niftygui.NiftyJmeDisplay;

import com.jme3.renderer.RenderManager;

import com.jme3.scene.Geometry;

import com.jme3.scene.shape.Box;

import de.lessvoid.nifty.Nifty;

import java.util.logging.Level;

import java.util.logging.Logger;



/**

  • test
  • @author normenhansen

    */

    public class Main extends SimpleApplication {



    Geometry geom;

    /**
  • Field to hold the Gui main screen

    */

    public AppState niftyScreen1 = new screen1();

    /**
  • Field to hold the Gui mesh edit screen

    */

    public AppState niftyScreen2 = new screen2();

    /**
  • Field to hold the Gui hidden screen

    */

    public AppState niftyScreen3 = new screen3();



    /Nifty shared field/

    public NiftyJmeDisplay niftyDisplay;

    /standard field/

    public Nifty nifty;



    /**
  • Standard code to get logger

    */

    private static final Logger logger = Logger.getLogger(Main.class.getName());



    public static void main(String[] args) {

    Main app = new Main();

    app.start();

    }



    @Override

    public void simpleInitApp() {

    SetupInterface();



    Box b = new Box(Vector3f.ZERO, 1, 1, 1);

    geom = new Geometry("Box", b);



    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");

    mat.setColor("Color", ColorRGBA.Blue);

    geom.setMaterial(mat);



    rootNode.attachChild(geom);

    }



    @Override

    public void simpleUpdate(float tpf) {

    //TODO: add update code

    }



    @Override

    public void simpleRender(RenderManager rm) {

    //TODO: add render code

    }



    public void MakeCubeRed() {

    logger.log(Level.INFO, "Color was changed to Red");

    geom.getMaterial().setColor("Color", ColorRGBA.Red);

    }



    public void MakeCubeGreen() {

    logger.log(Level.INFO, "Color was changed to Green");

    geom.getMaterial().setColor("Color", ColorRGBA.Green);

    }



    public void MakeCubeBlue() {

    logger.log(Level.INFO, "Color was changed to Blue");

    geom.getMaterial().setColor("Color", ColorRGBA.Blue);

    }



    private void SetupInterface() {

    flyCam.setEnabled(false);

    //Allow mouse be visible Much easier to interact with gui

    inputManager.setCursorVisible(true);



    //Create nifty Gui display, load screen from the xml file and display screen, standard behavior

    //only for the main screen, no need to load from xml for other screens

    niftyDisplay = new NiftyJmeDisplay(assetManager,

    inputManager,

    audioRenderer,

    guiViewPort);

    nifty = niftyDisplay.getNifty();

    guiViewPort.addProcessor(niftyDisplay);



    /**
  • Attach the gui class to the application

    /

    stateManager.attach(niftyScreen1);

    }

    }

    [/java]



    The first screen controller initialize method changed to verify if the screen exists in nifty and if it doesn’t it is loaded from the XML file. The first screen works just fine.

    [java]/

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

    */

    package niftypoc;



    import com.jme3.app.Application;

    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;

    import java.util.logging.Logger;



    /**

    *
  • @author vchevali

    */

    public class screen1 extends AbstractAppState implements ScreenController {



    //standard field

    private Nifty nifty;

    //standard field

    private Screen screen;

    //standard field

    private Main app;



    /**
  • Standard code to get logger

    /

    private static final Logger logger = Logger.getLogger(Main.class.getName());



    public void bind(Nifty nifty, Screen screen) {

    //standard code to bind controller to nifty gui elements

    this.nifty = nifty;

    this.screen = screen;

    }



    public void onStartScreen() {

    //nothing

    }



    public void onEndScreen() {

    //nothing

    }



    /
    * jME3 AppState methods /

    @Override

    public void initialize(AppStateManager stateManager, Application app) {

    //standard code to bid app state to the application

    super.initialize(stateManager, app);

    this.app=(Main)app;

    nifty = this.app.nifty;

    if(!nifty.getAllScreensName().contains("start"))

    {

    nifty.fromXml("Interface/NiftyPoc.xml", "start", this);

    }

    else

    {

    nifty.gotoScreen("start");

    }

    }



    @Override

    public void update(float tpf) {

    /
    * jME update loop! */

    }



    @Override

    public void stateAttached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached to the application

    }



    @Override

    public void stateDetached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached from the application

    }



    /**
  • Method called by the btnGoToScreen2

    */

    public void btnGoToScreen2Click() {

    app.getStateManager().detach(app.niftyScreen1);

    app.getStateManager().attach(app.niftyScreen2);

    }



    /**
  • Method called by the btnRedCube

    /

    public void btnRedCubeClick() {

    app.MakeCubeRed();

    }



    }

    [/java]



    The second screen controller initialize method changed to verify if the screen exists in nifty and if it doesn’t it is loaded from the XML file. It is pretty much identical to the first screen controller… But by the time the application gets there, the XML file is loaded. So rather than reloading from the XML file, a simple Nifty gotoscreen is called. But then, when the second screen is loaded, nothing works.



    [java]/

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

    */

    package niftypoc;



    import com.jme3.app.Application;

    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;

    import java.util.logging.Logger;



    /**

    *
  • @author vchevali

    */

    public class screen2 extends AbstractAppState implements ScreenController {



    //standard field

    private Nifty nifty;

    //standard field

    private Screen screen;

    //standard field

    private Main app;



    /**
  • Standard code to get logger

    /

    private static final Logger logger = Logger.getLogger(Main.class.getName());



    public void bind(Nifty nifty, Screen screen) {

    //standard code to bind controller to nifty gui elements

    this.nifty = nifty;

    this.screen = screen;

    }



    public void onStartScreen() {

    //nothing

    }



    public void onEndScreen() {

    //nothing

    }



    /
    * jME3 AppState methods /

    @Override

    public void initialize(AppStateManager stateManager, Application app) {

    //standard code to bid app state to the application

    super.initialize(stateManager, app);

    this.app=(Main)app;

    nifty = this.app.nifty;

    if(!nifty.getAllScreensName().contains("screen2"))

    {

    nifty.fromXml("Interface/NiftyPoc.xml", "screen2", this);

    }

    else

    {

    nifty.gotoScreen("screen2");

    }

    }



    @Override

    public void update(float tpf) {

    /
    * jME update loop! */

    }



    @Override

    public void stateAttached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached to the application

    }



    @Override

    public void stateDetached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached from the application

    }



    /**
  • Method called by the btnGoToScreen3

    */

    public void btnGoToScreen3Click() {

    app.getStateManager().detach(app.niftyScreen2);

    app.getStateManager().attach(app.niftyScreen3);

    }



    /**
  • Method called by the btnGreenCube

    */

    public void btnGreenCubeClick() {

    app.MakeCubeGreen();

    }



    }

    [/java]

What I don’t understand is this: when the second screen controller is initialized, it receives two parameters: a state manager and the application. These parameters have values. I store the reference to the app in the screen controller app field. But when I click on a button of the second screen, the app field is null. How is that possible? Can someone help me please?

Well, I found 2 solutions. I am unhappy with both of them, but at least it works:



First solution: make one xml file for each screen and load it form xml each time you change screen. That makes switching form screen to screen pretty slow, but since the xml files are pretty small, it’s not decades long. All code controlling screen is separated in different screen controllers: easy to manage, not messy. But not great at runtime since it’s slow.



Second solution: make only one screen controller that manages all screens. Make only one XML file that contains all the screens. It flows really well at runtime (no intteruption to load), but it makes for one ugly screen controller. You have to make sure not to mess up which screen calls which method, etc. When I switch from one nifty screen to another, I simply call “nifty.gotoscreen()” and I don’t attach/detach screen controllers. I only attach the big screen controller when the app is initialized.



here is the code for solution #2. It’s easy to go from the code of my previous posts to solution #1. It’s not worth uploading it here, unless someone asks me for it.



nifty screen controller that manages all nifty screens:

[java]/*

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

    */

    package niftypoc;



    import com.jme3.app.Application;

    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;

    import java.util.logging.Logger;



    /**

    *
  • @author vchevali

    */

    public class NiftyScreenController extends AbstractAppState implements ScreenController {



    //standard field

    private Nifty nifty;

    //standard field

    private Screen screen;

    //standard field

    private Main app;



    /**
  • Standard code to get logger

    /

    private static final Logger logger = Logger.getLogger(Main.class.getName());



    public void bind(Nifty nifty, Screen screen) {

    //standard code to bind controller to nifty gui elements

    this.nifty = nifty;

    this.screen = screen;

    }



    public void onStartScreen() {

    //nothing

    }



    public void onEndScreen() {

    //nothing

    }



    /
    * jME3 AppState methods /

    @Override

    public void initialize(AppStateManager stateManager, Application app) {

    //standard code to bid app state to the application

    super.initialize(stateManager, app);

    this.app=(Main)app;

    nifty = this.app.nifty;

    nifty.fromXml("Interface/NiftyScreens.xml", "start", this);

    }



    @Override

    public void update(float tpf) {

    /
    * jME update loop! */

    }



    @Override

    public void stateAttached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached to the application

    }



    @Override

    public void stateDetached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached from the application

    }



    //Start Screen methods

    /**
  • Method called by the btnGoToScreen2

    */

    public void btnGoToScreen2Click() {

    nifty.gotoScreen("screen2");

    /app.getStateManager().detach(app.niftyScreen1);

    app.getStateManager().attach(app.niftyScreen2);
    /

    }



    /**
  • Method called by the btnRedCube

    */

    public void btnRedCubeClick() {

    app.MakeCubeRed();

    }



    //Screen 2 methods

    /**
  • Method called by the btnGoToScreen3

    */

    public void btnGoToScreen3Click() {

    nifty.gotoScreen("screen3");

    /app.getStateManager().detach(app.niftyScreen2);

    app.getStateManager().attach(app.niftyScreen3);
    /

    }



    /**
  • Method called by the btnGreenCube

    */

    public void btnGreenCubeClick() {

    app.MakeCubeGreen();

    }





    //Screen 3 methods

    /**
  • Method called by the btnGoToScreen1

    */

    public void btnGoToScreen1Click() {

    nifty.gotoScreen("start");

    /app.getStateManager().detach(app.niftyScreen3);

    app.getStateManager().attach(app.niftyScreen1);
    /

    }



    /**
  • Method called by the btnBlueCube

    */

    public void btnBlueCubeClick() {

    app.MakeCubeBlue();

    }



    }

    [/java]



    main class

    [java]package niftypoc;



    import com.jme3.app.SimpleApplication;

    import com.jme3.app.state.AppState;

    import com.jme3.material.Material;

    import com.jme3.math.ColorRGBA;

    import com.jme3.math.Vector3f;

    import com.jme3.niftygui.NiftyJmeDisplay;

    import com.jme3.renderer.RenderManager;

    import com.jme3.scene.Geometry;

    import com.jme3.scene.shape.Box;

    import de.lessvoid.nifty.Nifty;

    import java.util.logging.Level;

    import java.util.logging.Logger;



    /**
  • test
  • @author normenhansen

    */

    public class Main extends SimpleApplication {



    Geometry geom;

    /**
  • One Nifty Screen controller to rule them all

    */

    public AppState niftyScreen1 = new NiftyScreenController();



    /Nifty shared field/

    public NiftyJmeDisplay niftyDisplay;

    /standard field/

    public Nifty nifty;



    /**
  • Standard code to get logger

    */

    private static final Logger logger = Logger.getLogger(Main.class.getName());



    public static void main(String[] args) {

    Main app = new Main();

    app.start();

    }



    @Override

    public void simpleInitApp() {

    SetupInterface();



    Box b = new Box(Vector3f.ZERO, 1, 1, 1);

    geom = new Geometry("Box", b);



    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");

    mat.setColor("Color", ColorRGBA.Blue);

    geom.setMaterial(mat);



    rootNode.attachChild(geom);

    }



    @Override

    public void simpleUpdate(float tpf) {

    //TODO: add update code

    }



    @Override

    public void simpleRender(RenderManager rm) {

    //TODO: add render code

    }



    public void MakeCubeRed() {

    logger.log(Level.INFO, "Color was changed to Red");

    geom.getMaterial().setColor("Color", ColorRGBA.Red);

    }



    public void MakeCubeGreen() {

    logger.log(Level.INFO, "Color was changed to Green");

    geom.getMaterial().setColor("Color", ColorRGBA.Green);

    }



    public void MakeCubeBlue() {

    logger.log(Level.INFO, "Color was changed to Blue");

    geom.getMaterial().setColor("Color", ColorRGBA.Blue);

    }



    private void SetupInterface() {

    flyCam.setEnabled(false);

    //Allow mouse be visible Much easier to interact with gui

    inputManager.setCursorVisible(true);



    //Create nifty Gui display, load screen from the xml file and display screen, standard behavior

    //only for the main screen, no need to load from xml for other screens

    niftyDisplay = new NiftyJmeDisplay(assetManager,

    inputManager,

    audioRenderer,

    guiViewPort);

    nifty = niftyDisplay.getNifty();

    guiViewPort.addProcessor(niftyDisplay);



    /**
  • Attach the gui class to the application

    /

    stateManager.attach(niftyScreen1);

    }

    }

    [/java]



    nifty screen xml file

    [xml]<?xml version=“1.0” encoding=“UTF-8”?>

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

    <useControls filename=“nifty-default-controls.xml” />

    <useStyles filename=“nifty-default-styles.xml” />

    <screen id=“start” controller=“niftypoc.NiftyScreenController”>

    <layer id=“layer” backgroundColor="#0000" childLayout=“vertical”>

    <panel id=“topPanel” height="
    " width=“100%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical” />

    <panel id=“bottomPanel” height=“85px” width=“100%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“horizontal”>

    <panel id=“bottomPanel1” height=“100%” width=“33%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical” style=“nifty-panel-no-shadow”>

    <effect>

    <onStartScreen name=“move” mode=“in” direction=“bottom” length=“300” startDelay=“100” inherit=“true”/>

    <onEndScreen name=“move” mode=“out” direction=“bottom” length=“300” startDelay=“100” inherit=“true”/>

    </effect>

    <control name=“label” text=“This is screen 1” width=“100%” />

    <control name=“button” id=“btnRedCube” label=“Make cube red” width=“100%”>

    <interact onClick=“btnRedCubeClick()”/>

    </control>

    <control name=“button” id=“btnGoToScreen2” label=“Go to screen 2” width=“100%”>

    <interact onClick=“btnGoToScreen2Click()”/>

    </control>

    </panel>

    <panel id=“bottomPanel2” height=“100%” width=“33%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical”>

    </panel>

    <panel id=“bottomPanel3” height=“100%” width="" align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical”>

    </panel>

    </panel>

    </layer>

    </screen>

    <screen id=“screen2” controller=“niftypoc.NiftyScreenController”>

    <layer id=“layer” backgroundColor="#0000" childLayout=“vertical”>

    <panel id=“topPanel” height="
    " width=“100%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical” />

    <panel id=“bottomPanel” height=“85px” width=“100%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“horizontal”>

    <panel id=“bottomPanel1” height=“100%” width=“33%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical”>

    </panel>

    <panel id=“bottomPanel2” height=“100%” width=“33%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical” style=“nifty-panel-no-shadow”>

    <effect>

    <onStartScreen name=“move” mode=“in” direction=“bottom” length=“300” startDelay=“100” inherit=“true”/>

    <onEndScreen name=“move” mode=“out” direction=“bottom” length=“300” startDelay=“100” inherit=“true”/>

    </effect>

    <control name=“label” text=“This is screen 2” width=“100%” />

    <control name=“button” id=“btnGreenCube” label=“Make cube green” width=“100%”>

    <interact onClick=“btnGreenCubeClick()”/>

    </control>

    <control name=“button” id=“btnGoToScreen3” label=“Go to screen 3” width=“100%”>

    <interact onClick=“btnGoToScreen3Click()”/>

    </control>

    </panel>

    <panel id=“bottomPanel3” height=“100%” width="" align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical”>

    </panel>

    </panel>

    </layer>

    </screen>

    <screen id=“screen3” controller=“niftypoc.NiftyScreenController”>

    <layer id=“layer” backgroundColor="#0000" childLayout=“vertical”>

    <panel id=“topPanel” height="
    " width=“100%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical” />

    <panel id=“bottomPanel” height=“85px” width=“100%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“horizontal”>

    <panel id=“bottomPanel1” height=“100%” width=“33%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical”>

    </panel>

    <panel id=“bottomPanel2” height=“100%” width=“33%” align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical”>

    </panel>

    <panel id=“bottomPanel3” height=“100%” width="*" align=“left” valign=“bottom” backgroundColor="#0000" childLayout=“vertical” style=“nifty-panel-no-shadow”>

    <effect>

    <onStartScreen name=“move” mode=“in” direction=“bottom” length=“300” startDelay=“100” inherit=“true”/>

    <onEndScreen name=“move” mode=“out” direction=“bottom” length=“300” startDelay=“100” inherit=“true”/>

    </effect>

    <control name=“label” text=“This is screen 3” width=“100%” />

    <control name=“button” id=“btnBlueCube” label=“Make cube blue” width=“100%”>

    <interact onClick=“btnBlueCubeClick()”/>

    </control>

    <control name=“button” id=“btnGoToScreen1” label=“Go to screen 1” width=“100%”>

    <interact onClick=“btnGoToScreen1Click()”/>

    </control>

    </panel>

    </panel>

    </layer>

    </screen>

    </nifty>

    [/xml]



    I think I’m gonna go with solution #2. Runtime experience is more important I believe. And I’ll be careful not to mess my code up. It’s a shame though.



    If someone knows how to make this work in a better way, please tell me.

I would suggest a slightly different approach to option 2.

You could have what I call “delegate screen controllers”. What I mean by this is you still have 1 screen controller as you option 2. But isntead of dropping all the code in the same screencontroller, you have sub-controllers.which contain the actual code for the screen in question. I think you should even be able to change out the screen controller at run-time so when you switch to screen 2, you put in the screencontroller for screen2. But I’m not 100% sure about this and not currently in a position to test it for you.



But with sub screen controllers, your main screen controller would just have a bunch of place-holder methods which refer to the sub screen controller for the actual functionality. Makes it a bit cleaner code wise.

1 Like

Hi ractoc,



It’s a good idea! I’m going to try this and I’ll let you know what results I get.



Thanks a lot for taking the time to read and answer. :slight_smile:

Wow, okay, now I’ve got something that really works well. A great many thanks ractoc! :slight_smile:



I followed your suggestion. Now, my application contains 5 classes and 1 Interface:


  • The Main class (of course)
  • The NiftyScreenController (that manages all nifty stuff)
  • An interface explaining what a SubScreenController should be.
  • 3 SubScreenControllers that contain the code for each nifty screen (they implement the interface).



    Basically, the ScreenController loads from XML (all screens in the same XML file) then it creates and HashMap to contain all SubScreencontrollers with the key being the screen name, of course.) I also have a field containing the currently active SubScreenController. So the ScreenController loads up the start screen and sets the field toward the right SubScreenController. I use reflection to call the proper method of the SubScreenController and I have a method to switch from one screen to another, changing nifty display and the current active SubScreenController.



    I don’t know if I followed the most logical approach, but please bear with me as I am new to java.



    Here goes…



    Main class:



    [java]package niftypoc;



    import com.jme3.app.SimpleApplication;

    import com.jme3.app.state.AppState;

    import com.jme3.material.Material;

    import com.jme3.math.ColorRGBA;

    import com.jme3.math.Vector3f;

    import com.jme3.niftygui.NiftyJmeDisplay;

    import com.jme3.renderer.RenderManager;

    import com.jme3.scene.Geometry;

    import com.jme3.scene.shape.Box;

    import de.lessvoid.nifty.Nifty;

    import java.util.logging.Level;

    import java.util.logging.Logger;



    /**
  • test
  • @author normenhansen

    */

    public class Main extends SimpleApplication {



    Geometry geom;

    /**
  • One Nifty Screen controller to rule them all

    */

    public AppState niftyScreen1 = new NiftyScreenController();



    /Nifty shared field/

    public NiftyJmeDisplay niftyDisplay;

    /standard field/

    public Nifty nifty;



    /**
  • Standard code to get logger

    */

    private static final Logger logger = Logger.getLogger(Main.class.getName());



    public static void main(String[] args) {

    Main app = new Main();

    app.start();

    }



    @Override

    public void simpleInitApp() {

    setupInterface();



    Box b = new Box(Vector3f.ZERO, 1, 1, 1);

    geom = new Geometry("Box", b);



    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");

    mat.setColor("Color", ColorRGBA.Blue);

    geom.setMaterial(mat);



    rootNode.attachChild(geom);

    }



    @Override

    public void simpleUpdate(float tpf) {

    //TODO: add update code

    }



    @Override

    public void simpleRender(RenderManager rm) {

    //TODO: add render code

    }



    public void makeCubeRed() {

    logger.log(Level.INFO, "Color was changed to Red");

    geom.getMaterial().setColor("Color", ColorRGBA.Red);

    }



    public void makeCubeGreen() {

    logger.log(Level.INFO, "Color was changed to Green");

    geom.getMaterial().setColor("Color", ColorRGBA.Green);

    }



    public void makeCubeBlue() {

    logger.log(Level.INFO, "Color was changed to Blue");

    geom.getMaterial().setColor("Color", ColorRGBA.Blue);

    }



    private void setupInterface() {

    flyCam.setEnabled(false);

    //Allow mouse be visible Much easier to interact with gui

    inputManager.setCursorVisible(true);



    setDisplayFps(false);

    setDisplayStatView(false);



    //Create nifty Gui display, load screen from the xml file and display screen, standard behavior

    //only for the main screen, no need to load from xml for other screens

    niftyDisplay = new NiftyJmeDisplay(assetManager,

    inputManager,

    audioRenderer,

    guiViewPort);

    nifty = niftyDisplay.getNifty();

    guiViewPort.addProcessor(niftyDisplay);



    /**
  • Attach the gui class to the application

    /

    stateManager.attach(niftyScreen1);

    }

    }

    [/java]



    Nifty ScreenController

    [java]/

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

    */

    package niftypoc;



    import com.jme3.app.Application;

    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;

    import java.util.HashMap;

    import java.util.logging.Logger;

    import java.util.logging.Level;



    /**

    *
  • @author vchevali

    */

    public class NiftyScreenController extends AbstractAppState implements ScreenController {



    //standard field

    private Nifty nifty;

    //standard field

    private Screen screen;

    //standard field

    private Main app;

    //private field for screens

    private HashMap<String, ISubScreenController> subScreens = new HashMap<String, ISubScreenController>();

    private ISubScreenController currentSubScreenController;



    /**
  • Standard code to get logger

    /

    private static final Logger logger = Logger.getLogger(Main.class.getName());



    public void bind(Nifty nifty, Screen screen) {

    //standard code to bind controller to nifty gui elements

    this.nifty = nifty;

    this.screen = screen;

    }



    public void onStartScreen() {

    //nothing

    }



    public void onEndScreen() {

    //nothing

    }



    /
    * jME3 AppState methods /

    @Override

    public void initialize(AppStateManager stateManager, Application app) {

    //standard code to bid app state to the application

    super.initialize(stateManager, app);

    this.app=(Main)app;

    nifty = this.app.nifty;

    nifty.fromXml("Interface/NiftyScreens.xml", "start", this);

    subScreens.put("start", new StartSubScreenController(nifty, (Main)app));

    subScreens.put("screen2", new Screen2SubScreenController(nifty, (Main)app));

    subScreens.put("screen3", new Screen3SubScreenController(nifty, (Main)app));

    currentSubScreenController = subScreens.get("start");

    }



    @Override

    public void update(float tpf) {

    /
    * jME update loop! /

    }



    @Override

    public void stateAttached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached to the application

    }



    @Override

    public void stateDetached(AppStateManager stateManager) {

    //Method used to run code when this app state is attached from the application

    }



    public void goToScreen(String screenName) {

    nifty.gotoScreen(screenName);

    currentSubScreenController = subScreens.get(screenName);

    }



    public void callMethod(String methodName) {

    try {

    Class c = Class.forName(currentSubScreenController.getClass().getName());

    c.getMethod(methodName).invoke(currentSubScreenController);

    }

    catch (Throwable e) {

    logger.log(Level.SEVERE, e.getMessage());

    }

    }

    }

    [/java]



    SubScreenController Interface

    [java]/

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

    */

    package niftypoc;



    /**

    *
  • @author vchevali

    /

    public interface ISubScreenController {



    }

    [/java]



    Subscreen controller 1

    [java]/

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

    */

    package niftypoc;



    import de.lessvoid.nifty.Nifty;



    /**

    *
  • @author vchevali

    */

    public class StartSubScreenController implements ISubScreenController{

    private Nifty nifty;

    private Main app;



    public StartSubScreenController(Nifty nifty, Main app)

    {

    this.app = app;

    this.nifty = nifty;

    }



    /**
  • Method called by the btnRedCube in start screen

    /

    public void btnRedCubeClick() {

    app.makeCubeRed();

    }

    }

    [/java]



    sub screencontroller 2:

    [java]/

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

    */

    package niftypoc;



    import de.lessvoid.nifty.Nifty;



    /**

    *
  • @author vchevali

    */

    public class Screen2SubScreenController implements ISubScreenController{

    private Nifty nifty;

    private Main app;



    public Screen2SubScreenController(Nifty nifty, Main app)

    {

    this.app = app;

    this.nifty = nifty;

    }



    /**
  • Method called by the btnRedCube in start screen

    /

    public void btnGreenCubeClick() {

    app.makeCubeGreen();

    }

    }

    [/java]



    sub screen controller 3:

    [java]/

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

    */

    package niftypoc;



    import de.lessvoid.nifty.Nifty;



    /**

    *
  • @author vchevali

    */

    public class Screen3SubScreenController implements ISubScreenController{

    private Nifty nifty;

    private Main app;



    public Screen3SubScreenController(Nifty nifty, Main app)

    {

    this.app = app;

    this.nifty = nifty;

    }



    /**
  • Method called by the btnRedCube in start screen

    /

    public void btnBlueCubeClick() {

    app.makeCubeBlue();

    }

    }

    [/java]



    of course, nifty xml file

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

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

    <useControls filename="nifty-default-controls.xml" />

    <useStyles filename="nifty-default-styles.xml" />

    <screen id="start" controller="niftypoc.NiftyScreenController">

    <layer id="layer" backgroundColor="#0000" childLayout="vertical">

    <panel id="topPanel" height="
    " width="100%" align="left" valign="bottom" backgroundColor="#0000" childLayout="vertical" />

    <panel id="bottomPanel" height="85px" width="100%" align="left" valign="bottom" backgroundColor="#0000" childLayout="horizontal">

    <panel id="bottomPanel1" height="100%" width="33%" align="left" valign="bottom" backgroundColor="#0000" childLayout="vertical" style="nifty-panel-no-shadow">

    <effect>

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

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

    </effect>

    <control name="label" text="This is screen 1" width="100%" />

    <control name="button" id="btnRedCube" label="Make cube red" width="100%">

    <interact onClick="callMethod(btnRedCubeClick)"/>

    </control>

    <control name="button" id="btnGoToScreen2" label="Go to screen 2" width="100%">

    <interact onClick="goToScreen(screen2)"/>

    </control>

    </panel>

    <panel id="bottomPanel2" height="100%" width="33%" align="left" valign="bottom" backgroundColor="#0000" childLayout="vertical">

    </panel>

    <panel id="bottomPanel3" height="100%" width="" align="left" valign="bottom" backgroundColor="#0000" childLayout="vertical">

    </panel>

    </panel>

    </layer>

    </screen>

    <screen id="screen2" controller="niftypoc.NiftyScreenController">

    <layer id="layer" backgroundColor="#0000" childLayout="vertical">

    <panel id="topPanel" height="
    " width="100%" align="left" valign="bottom" backgroundColor="#0000" childLayout="vertical" />

    <panel id="bottomPanel" height="85px" width="100%" align="left" valign="bottom" backgroundColor="#0000" childLayout="horizontal">

    <panel id="bottomPanel1" height="100%" width="33%" align="left" valign="bottom" backgroundColor="#0000" childLayout="vertical">

    </panel>

    <panel id="bottomPanel2" height="100%" width="33%" align="left" valign="bottom" backgroundColor="#0000" childLayout="vertical" style="nifty-panel-no-shadow">

    <effect>

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

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

    </effect>

    <control name="label" text="This is screen 2" width="100%" />

    <control name="button" id="btnGreenCube" label="Make cube green" width="100%">

    <interact onClick="callMethod(btnGreenCubeClick)"/>

    </control>

    <control name="button" id="btnGoToScreen3" label="Go to screen 3" width="100%">

    <interact onClick="goToScreen(screen3)"/>

    </control>

    </panel>

    <panel id="bottomPanel3" height="100%" width="" align="left" valign="bottom" backgroundColor="#0000" childLayout="vertical">

    </panel>

    </panel>

    </layer>

    </screen>

    <screen id="screen3" controller="niftypoc.NiftyScreenController">

    <layer id="layer" backgroundColor="#0000" childLayout="vertical">

    <panel id="topPanel" height="
    " width="100%" align="left" valign="bottom" backgroundColor="#0000" childLayout="vertical" />

    <panel id="bottomPanel" height="85px" width="100%" align="left" valign="bottom" backgroundColor="#0000" childLayout="horizontal">

    <panel id="bottomPanel1" height="100%" width="33%" align="left" valign="bottom" backgroundColor="#0000" childLayout="vertical">

    </panel>

    <panel id="bottomPanel2" height="100%" width="33%" align="left" valign="bottom" backgroundColor="#0000" childLayout="vertical">

    </panel>

    <panel id="bottomPanel3" height="100%" width="*" align="left" valign="bottom" backgroundColor="#0000" childLayout="vertical" style="nifty-panel-no-shadow">

    <effect>

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

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

    </effect>

    <control name="label" text="This is screen 3" width="100%" />

    <control name="button" id="btnBlueCube" label="Make cube blue" width="100%">

    <interact onClick="callMethod(btnBlueCubeClick)"/>

    </control>

    <control name="button" id="btnGoToScreen1" label="Go to screen 1" width="100%">

    <interact onClick="goToScreen(start)"/>

    </control>

    </panel>

    </panel>

    </layer>

    </screen>

    </nifty>

    [/xml]



    The only downside I see to this method is that I lose the possibility to pass parameters in a function called from a nifty screen (since the parameter is always the name of the method to call). But I can live with that since the runtime is great and the code is neatly spread across useful classes.



    What do you think? :slight_smile:



    Do you see a way to improve this?

(Oh by the way, I gave you a +1 ractoc, thanks again!)

On second thought, I pushed the system a step further. Rather than having to hardcode the creation of each SubScreenController, once I found all the screen names in the xml file, I loop through the list of found screen names and instanciate the SubScreenControllers by reflection when I add them to the SubScreenControllers HashMap. As long as the screen names match with the subcontroller class name, it works! :slight_smile:



If anyone is interested by the final code, I can post it here.

Thanks!

Just started experimenting with jMonkeyEngine Beta1 & Nifty and got totally confused about why switching screens did not work. I had several nifty-xml-files, one for each screen.

However, reading your post I tried to put them all in one XML-file. That worked, haven’t come a long way with the controllers yet but I guess that you could simplify your approach like this.



[xml] <screen id=“id1” controller=“com.acme.nifty.Controller1”>…

<screen id=“id2” controller=“com.acme.nifty.Controller2”>…[/xml]



Then in your java-code you initialize nifty like this:

[java]nifty.fromXml(

“Interface/nifty.xml”,

“startscreen”,

new com.acme.nifty.Controller1(whatever),

new com.acme.nifty.Controller2(someother));[/java]



Seems that Nifty will match all the controllers you pass into fromXml against the classname in the actual XML and select the right controller. So now you only have to do nifty.gotScreen(“id2”)



So it mostly works as you’d want except that it seems like you have to put everything in one XML-file (or re-initialize nifty with another XML-file as you point out).



If the controllers have default, no-args-constructors you don’t even have to create them and pass them into fromXml, Nifty will do it for you it seems.



Cheers,

JM

No. It works with separated screen xml files as well. I do that all the time, and it works like a boss. It didn’t work for you, because you’ve used the fromXml() method, then it won’t work at all, because it will just read the data from only one xml file. For several xml files, that’s more elegant, you have to use addXml() and it’ll work.

1 Like

Yeah, do I feel stupid, I found that 2 minutes after I wrote my reply :slight_smile: Thanks for the tip. Must say that jMonkeyEngine & Nifty is so sweet.



https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:nifty_gui_scenarios