getApplication returns null

I’m trying to make a GUI screen with an exit button. When I click the button, I get a null pointer exception from getApplication().stop() because getApplication() is returning null. (The Nifty GUI tutorial didn’t check for that so I assume it’s not supposed to happen.) This is my first attempt at using an AppState so I might be doing something wrong there.

StartScreen.java:

package mygame;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.BaseAppState;
import com.jme3.niftygui.NiftyJmeDisplay;
import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.builder.LayerBuilder;
import de.lessvoid.nifty.builder.PanelBuilder;
import de.lessvoid.nifty.builder.ScreenBuilder;
import de.lessvoid.nifty.builder.TextBuilder;
import de.lessvoid.nifty.controls.button.builder.ButtonBuilder;
import de.lessvoid.nifty.screen.Screen;
import de.lessvoid.nifty.screen.ScreenController;

public class StartScreen extends BaseAppState implements ScreenController {

    @Override
    protected void initialize(Application app) {
        //It is technically safe to do all initialization and cleanup in the
        //onEnable()/onDisable() methods. Choosing to use initialize() and
        //cleanup() for this is a matter of performance specifics for the
        //implementor.
        //TODO: initialize your AppState, e.g. attach spatials to rootNode
    }

    @Override
    protected void cleanup(Application app) {
        //TODO: clean up what you initialized in the initialize method,
        //e.g. remove all spatials from rootNode
    }

    //onEnable()/onDisable() can be used for managing things that should
    //only exist while the state is enabled. Prime examples would be scene
    //graph attachment or input listener attachment.
    @Override
    protected void onEnable() {
        NiftyJmeDisplay niftyDisplay = NiftyJmeDisplay.newNiftyJmeDisplay(
                getApplication().getAssetManager(),
                getApplication().getInputManager(),
                getApplication().getAudioRenderer(),
                getApplication().getGuiViewPort());

        Nifty nifty = niftyDisplay.getNifty();
        getApplication().getGuiViewPort().addProcessor(niftyDisplay);
        ((SimpleApplication) getApplication()).getFlyByCamera().setDragToRotate(true);

        nifty.loadStyleFile("nifty-default-styles.xml");
        nifty.loadControlFile("nifty-default-controls.xml");

        // <screen>
        nifty.addScreen("Screen_ID", new ScreenBuilder("Hello Nifty Screen"){{
            controller(new mygame.StartScreen()); // Screen properties

            // <layer>
            layer(new LayerBuilder("Menu_Layer") {{
                childLayoutVertical(); // layer properties, add more...

                
                panel(new PanelBuilder("Title_Panel") {{
                childLayoutCenter();
                alignCenter();
                height("25%");
                width("75%");

                    text(new TextBuilder() {{
                        text("Game with a Name");
                        font("Interface/Fonts/Default.fnt");
                        height("100%");
                        width("100%");
                    }});
                }});
                                
                // <panel>
                panel(new PanelBuilder("Menu_Panel") {{
                   childLayoutCenter(); // panel properties, add more...
                   alignCenter();
                   valignCenter();
                   height("50%");
                   width("50%");

                    control(new ButtonBuilder("ExitButton", "Exit"){{
                        alignCenter();
                        valignBottom();
                        height("10%");
                        width("30%");
                        visibleToMouse(true);
                        interactOnClick("quitGame()");
                    }});

                    //.. add more GUI elements here

                }});
                // </panel>
              }});
            // </layer>
          }}.build(nifty));
        // </screen>

        nifty.gotoScreen("Screen_ID"); // start the screen
    }

    @Override
    protected void onDisable() {
        //Called when the state was previously enabled but is now disabled
        //either because setEnabled(false) was called or the state is being
        //cleaned up.
    }

    @Override
    public void update(float tpf) {
        //TODO: implement behavior during runtime
    }

    @Override
    public void bind(Nifty nifty, Screen screen) {
        System.out.println("Bind not supported yet.");
    }

    @Override
    public void onStartScreen() {
        System.out.println("onStartScreen not supported yet.");
    }

    @Override
    public void onEndScreen() {
        System.out.println("onEndScreen not supported yet.");
    }

    public void quitGame() {
        getApplication().stop();
    }

}

Main.java:

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.renderer.RenderManager;

public class Main extends SimpleApplication {
    StartScreen startScreen = new StartScreen();

    public static void main(String[] args) {
        Main app = new Main();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        stateManager.attach(startScreen);
    }

    @Override
    public void simpleUpdate(float tpf) {
        // This prints a non-null value
        //System.out.println(startScreen.getApplication());
    }

    @Override
    public void simpleRender(RenderManager rm) {
        //TODO: add render code
    }
}

Any idea what could be the problem? (Thanks in advance)

1 Like

i havent really used nifty but you seem to create a new instance of your StartScreen class that you pass as controller to your nifty screen. so you got that one StartScreen that you create in your Main class, that you attach to the stateManager and which will not return null for getApplication() because it gets initialized properly, and then you have that freshly created StartScreen that you pass to controller(), which won’t ever be initialized and thus not know about any application.
havent tried it, but maybe this would work:

nifty.addScreen("Screen_ID", new ScreenBuilder("Hello Nifty Screen"){{
            controller(this); // Screen properties

edit: just realized you’re new, so welcome and a little more information here:
you didnt actually use the appState in a wrong way, the only problem was that you created 2, the one you used wasnt really initialized.
the thing with appStates and the stateManager is, a state wont be initialized as soon as it is attached or cleaned up as soon as it is removed, instead it will first be initialized / cleaned up in the frame after your call.
one example:
when you have code like:

SomeAppState state = new SomeAppState();
stateManager.attach(state);
Application app = state.getApplication();

then ‘app’ will be null, because the stateManager didnt have time to initialize the state yet.
one more thing is that stateManager is threadSafe, that is you can attach / detach states from any thread and stateManager will properly manage their lifecycles.
another thing worth noting is, that a state that is removed in the same frame it was added will never be initialized / terminated. hope it helps

3 Likes

Initialization bugs are easily solved using a debugger.

The app field of BaseAppState is initialized in BaseAppState.initialize(). StartScreen overrides the initialize() method but does not perform the initialization.

A simple solution would be to have StartScreen.initialize() invoke super.initialize().

thats what i thought first, too.
however, he or she overrides

protected void initialize( Application app )

which is the abstract method provided by BaseAppState (which implements

public void initialize( AppStateManager stateManager, Application app )

which does the actual initialization and is the method declared in the AppState interface, thus the one called by the stateManager), probably was made like that exactly to overcome this issue

Thanks! That fixed it. And thanks for the extra advice as well.