How do I pass an instance of the Nifty class to Screen Controller?


#1

I tried to use the code from the documentation, but encountered a problem: an instance of the Nifty class is not available from the screen controller. Tried to call it like so:

Main.java
public class Main extends SimpleApplication {
    ...
    public static Nifty nifty;
    public static Client myClient;
    ...
}
LoginScreen.java
public class LoginScreen extends BaseAppState implements ScreenController {
...
    public void GoToSignUp() {
       NiftyEMailText String = Main.nifty.getCurrentScreen().findElementById("EMail").getRenderer(TextRenderer.class).getWrappedText();
       NiftyPassWordText String = Main.nifty.getCurrentScreen().findElementById("PassWord").getRenderer(TextRenderer.class).getWrappedText();
       Main.nifty.gotoScreen("SignUpScreen");
       Element niftyEMail = Main.nifty.getCurrentScreen().findElementById("EMail");
       niftyEMail.getRenderer(TextRenderer.class).setText(niftyEMailText);
       Element niftyPassWord = Main.nifty.getCurrentScreen().findElementById("PassWord");
       niftyPassWord.getRenderer(TextRenderer.class).setText(niftyPassWordText);
    }
...
}
screen.xml
<?xml version="1.0" encoding="UTF-8"?>
<nifty xmlns="http://nifty-gui.lessvoid.com/nifty-gui" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://raw.githubusercontent.com/void256/nifty-gui/1.4/nifty-core/src/main/resources/nifty.xsd 
https://raw.githubusercontent.com/void256/nifty-gui/1.4/nifty-core/src/main/resources/nifty.xsd">
    <useStyles filename="nifty-default-styles.xml"/>
    <useControls filename="nifty-default-controls.xml"/>
    <screen id="start" controller="com.yasen.client.LoginScreen">
        <layer id="background" childLayout="center">
            <image filename="Interface/start-background.jpg"></image>
        </layer>
        <layer id="foreground" childLayout="center">
            <panel id="panel" height="25%" width="75%" align="center" childLayout="horizontal">
                <panel id="panel_left" height="100%" width="25%" align="left" childLayout="vertical">
                    <text text="E-mail:" font="Interface/Fonts/Default.fnt"/>
                    <text text="Password:" font="Interface/Fonts/Default.fnt"/>
                </panel>
                <panel id="panel_center" height="100%" width="50%" align="center" childLayout="vertical">
                    <control id="EMail" name="textfield" maxLength="50" font="Interface/Fonts/Default.fnt"/>
                    <control id="PassWord" name="textfield" maxLength="100" passwordChar="*" font="Interface/Fonts/Default.fnt"/>
                </panel>
                <panel id="panel_right" height="100%" width="25%" align="right" childLayout="vertical">
                    <control id="SignUp" name="button" label="Register" font="Interface/Fonts/Default.fnt" visibleToMouse="true">
                        <interact onClick="GoToSignUp()"/>
                    </control>
                    <control id="LogIn" name="button" label="Login" font="Interface/Fonts/Default.fnt" visibleToMouse="true">
                            <interact onClick="AuthReq()"/>
                    </control>
                </panel>
            </panel>
        </layer>
    </screen>
    <screen id="SignUpScreen" controller="com.yasen.client.LoginScreen">
        <layer id="background" childLayout="center">
            <image filename="Interface/start-background.jpg"></image>
        </layer>
        <layer id="foreground" childLayout="center">
            <panel id="panel" height="25%" width="75%" align="center" childLayout="horizontal">
                <panel id="panel_left" height="100%" width="25%" align="left" childLayout="vertical">
                    <text text="E-mail:" font="Interface/Fonts/Default.fnt"/>
                    <text text="Password:" font="Interface/Fonts/Default.fnt"/>
                </panel>
                <panel id="panel_center" height="100%" width="50%" align="center" childLayout="vertical">
                    <control id="EMail" name="textfield" maxLength="50" font="Interface/Fonts/Default.fnt"/>
                    <control id="PassWord" name="textfield" maxLength="100" passwordChar="*" font="Interface/Fonts/Default.fnt"/>
                    <control id="PassWord2" name="textfield" maxLength="100" passwordChar="*" font="Interface/Fonts/Default.fnt"/>
                </panel>
                <panel id="panel_right" height="100%" width="25%" align="right" childLayout="vertical">
                    <control id="SignUp" name="button" label="Sign Up" font="Interface/Fonts/Default.fnt" visibleToMouse="true">
                        <interact onClick="SignUpReq()"/>
                    </control>
                </panel>
            </panel>
        </layer>
    </screen>
</nifty>

But got an error:

Caused by: java.lang.NullPointerException
at com.yasen.client.LoginScreen.GoToSignUp(LoginScreen.java:53)

As I understand it, meaning that the program is trying to access a non-existent GUI node.

How to correctly pass an instance of the Nifty class to the screen controller to work?


#2

When you use gotoScreen it will first call the ScreenControllers bind method.

    @Override
    public void bind(Nifty nifty, Screen screen) {
        this.nifty = nifty;
        this.screen = screen;
    }

#3

What line is this?


#4
NiftyEMailText String = Main.nifty.getCurrentScreen().findElementById("EMail").getRenderer(TextRenderer.class).getWrappedText();

#5

Then it could be a bunch of different things. You will need to add debugging to see.

Possibilities:
Main.nifty is null
Main.nifter.getCurrentScreen() returns null.
Main.nifter.getCurrentScreen().finrElementById(“EMail”) returns null
Main.nifty.getCurrentScreen().findElementById(“EMail”).getRenderer(TextRenderer.class) null

…any of those things would cause an NPE on that line and you can’t tell just by the evidence provided.


#6

Added:

public class LoginScreen extends BaseAppState implements ScreenController {

    private Nifty nifty;
    private Screen screen;
    ...
    @Override
    public void bind(Nifty nifty, Screen screen) {
        this.nifty = nifty;
        this.screen = screen;
    }
    ...
}

The mistake, however, remained. bind() is necessary where-that to call, or missing calls another method? Just in case, I bring Main.simpleInitApp:

@Override
public void simpleInitApp() {
    try {
        myClient = Network.connectToServer("localhost", 6143);
        MessageSystem ms = new MessageSystem(myClient);
        myClient.start();
    }
    catch (IOException exc) { exc.printStackTrace(); }
    NiftyJmeDisplay niftyDisplay = NiftyJmeDisplay.newNiftyJmeDisplay(assetManager, inputManager, audioRenderer, guiViewPort);
    nifty = niftyDisplay.getNifty();
    nifty.fromXml("Interface/screen.xml", "start", new LoginScreen());
    guiViewPort.addProcessor(niftyDisplay);
    flyCam.setDragToRotate(true);
}

#7

Eventually you’ll address my comment and figure out what’s wrong.

In the mean time, I will point out that you never attach your LoginScreen() app state so there is no reason for it to extend BaseAppState. …unless you wanted that functionality in which case you will have to attach it.

Given the code posted, it doesn’t have anything to do with your NPE.

For that, you will either have to run in a debugger and figure out what is actually null… or put some System.out.println() in the code to see.

Do note that it’s Java convention to lower case the first letter of method and field names. It makes the code more readable over the Visual Basic style capitalization that .NET adopted. The “String” field names really took me by surprise at first glance.


#8

Do you add Loginscreen() to StateManager?

Edit: See Important Admonition at end of this topic.
https://wiki.jmonkeyengine.org/jme3/advanced/nifty_gui_scenarios.html#get-access-to-application-and-update-loop


#9

It’s a problem… but not the cause of his NPE. Unless he’s lying about the code being run.


#10

Yes.

Moving the nifty initialization to the LoginScreen controller so you can initialize and register any other controllers would probably clear it up. Then use bind in the other controllers.

Makes things simpler.