Switching States, screen is null (bind never called)

Okay, so, it’s pretty simple (I think). There is a green button on the previous screen, and when click, switches between 2 states. The previous state’s screen is not null. However, when switching the new state, an NPE is thrown because screen is null. And I know the bind for this new state isn’t getting called because nothing prints to screen if I try and put something in there.

Not sure what code to give you, but here:

File of new state being transitioned to.

[java]@Override
public void initialize(AppStateManager stateManager, Application app)
{
super.initialize(stateManager, app);
this.app = (SimpleApplication)app;
this.rootNode = this.app.getRootNode();
this.guiNode = this.app.getGuiNode();
this.assetManager = this.app.getAssetManager();
this.settings = this.app.getContext().getSettings();
this.viewPort = this.app.getViewPort();
this.inputManager = this.app.getInputManager();
this.compoundManager = Main.getCompoundManager();
this.audioRenderer = this.app.getAudioRenderer();
this.guiViewPort = this.app.getViewPort();
this.cam = this.app.getCamera();

    time = new LwjglTimer();  

// gameTimer = new LwjglTimer();

    bulletAppState = new BulletAppState();
    stateManager.attach(bulletAppState);

    rootNode.attachChild(localRootNode);    //Creates rootNode 
    guiNode.attachChild(localGuiNode);      //Creates guiNode

     // Can use same music node across all menus. Not sure if ideal, but it is functional, and seems to work well.
    Main.changeMusic("Sounds/Music/Super Smash Bionicle Main Theme 2.wav"); 

    //Initialize enviornment
    createEnviron();

    //Attach stage
    localRootNode.attachChild(loadStage.getStageNode());
    players[1].getPlayer().getControl(AIController.class).findTarget(); //TO CHANGE AFTER AI TESTING
    bulletAppState.getPhysicsSpace().enableDebug(assetManager);

    //Get Main's nifty
    nifty = Main.getNifty();
    nifty.registerScreenController(this);
    nifty.addXml("Interface/GUIS/StandardMatchHUD.xml");
    nifty.gotoScreen("standardMatchHud");
    
    //Add the InGameHUD xml file, and go this screen
    nifty.setDebugOptionPanelColors(false); // Added this so the true on the Menu won't make the screen look weird.

    //Initialize GUI
    System.out.println(nifty + " Nifty and Screen " + screen);
    guiInitiate();
    playerStockInitialize();
    balanceSetup();

// adjustStock(initialStock, 1);

    //Rotate the camera to start position
    cam.setLocation(new Vector3f(0, 10, 70));
    cam.setRotation(new Quaternion(0f, -1f, 0f, 0f));
}

/*Create players algo
 * First, check the length,
 * and with each increment in length
 * create the next player appropriately
 */
public void createEnviron()         /**STILL NEEDS ADJUSTING **/
{
    
    //players are all null at start
    
    players[0] = null; 
    players[1] = null;
    players[2] = null;
    players[3] = null;
    
    
    players[0] = new PlayerPhysics(localRootNode,currentMatch.getPlayer1(), bulletAppState, inputManager, compoundManager, cam, this, false);
    for(int i = 1; i < 4; i++)
    {
        switch(i){
            case 1:
                if(!currentMatch.getPlayer2().canPlay()){
                    break;
                } else if(currentMatch.getPlayer2().sameCharacter(currentMatch.getPlayer1())){
                    players[1]= new PlayerPhysics(localRootNode,currentMatch.getPlayer2(), bulletAppState, inputManager, compoundManager, cam, this, true);
                } else {
                    players[1]= new PlayerPhysics(localRootNode,currentMatch.getPlayer2(), bulletAppState, inputManager, compoundManager, cam, this, false);
                }
                break;
            case 2:
                if(!currentMatch.getPlayer3().canPlay()){
                    break;
                }else if(currentMatch.getPlayer3().sameCharacter(currentMatch.getPlayer2()) || currentMatch.getPlayer3().sameCharacter(currentMatch.getPlayer1())){
                   // players[2]= new PlayerPhysics(currentMatch.getPlayer3(), bulletAppState, inputManager, cam,true);
                } else {
                   // players[2]= new PlayerPhysics(currentMatch.getPlayer3(), bulletAppState, inputManager, cam,false);
                }
                break;
            case 3:
                if(!currentMatch.getPlayer4().canPlay()){
                    break;
                }else if(currentMatch.getPlayer4().sameCharacter(currentMatch.getPlayer3()) || currentMatch.getPlayer4().sameCharacter(currentMatch.getPlayer2()) || currentMatch.getPlayer4().sameCharacter(currentMatch.getPlayer1())){
                  //  players[3]= new PlayerPhysics(currentMatch.getPlayer3(), bulletAppState, inputManager, cam,true);
                } else {
                  //  players[3]= new PlayerPhysics(currentMatch.getPlayer3(), bulletAppState, inputManager, cam,false);
                }
                break;
                
        }
    }
    
    loadStage = new Stage(assetManager.loadModel("Scenes/StageScenes/ShowdownScene.j3o"), bulletAppState);  
    ledges = loadStage.getLedges();
    loadStage.setInGameState(this);
    
    for(int i = 0; i < 4; i++){
        switch (i){
            case 0:
                if(players[0] != null){
                    loadStage.getp1Spawn().attachChild(players[i].getPlayer());
                    //players[i].getModelRoot().move(0,-5f,0);
                    Vector3f pos = ((Spatial) loadStage.getp1Spawn()).getWorldTranslation();
                    players[i].getCharacterControl().setPhysicsLocation(pos);
                }
                break;
            case 1:
                if(players[1] != null){
                    loadStage.getp2Spawn().attachChild(players[i].getPlayer());
                    players[i].getCharacterControl().setPhysicsLocation((((Spatial) loadStage.getp2Spawn()).getWorldTranslation()));
                }
                break;
            case 2:
                if(players[2] != null){
                    loadStage.getp3Spawn().attachChild(players[i].getModelRoot());
                    players[i].getCharacterControl().setPhysicsLocation((((Spatial) loadStage.getp1Spawn()).getWorldTranslation()));
                }
                break;
            case 3:
                if(players[3] != null){
                    loadStage.getp4Spawn().attachChild(players[i].getModelRoot());
                    players[i].getCharacterControl().setPhysicsLocation((((Spatial) loadStage.getp1Spawn()).getWorldTranslation()));
                }
                break;
        }
        
    }
        
    //loadStage.getp2Spawn().attachChild(two.getPlayer());
    //loadStage.getp3Spawn().attachChild(three.getPlayer());

    
    //two.getCharacterControl().setPhysicsLocation((((Spatial) loadStage.getp2Spawn()).getWorldTranslation()));
    //three.getCharacterControl().setPhysicsLocation((((Spatial) loadStage.getp3Spawn()).getWorldTranslation()));   
}

@Override
public void update(float tpf) 
{
    for(int i =0; i < ledges.size(); i++){
        if(((Spatial)ledges.get(i)).getControl(GhostControl.class).getOverlappingCount() == 0 && (Boolean)((Spatial)ledges.get(i)).getUserData("ledgeGrabbed")){
            ((Spatial)ledges.get(i)).setUserData("ledgeGrabbed",false);
            players[(Integer)((Spatial)ledges.get(i)).getUserData("playerGrabbing")-1].getPlayer().getControl(PlayerControl.class).resetGravity();
            
        }
    }
    
    //JME says looping streamed music isn't possible. So instead
    //look for when the music is stopped, create a new
    //instance of that music, and play it again.

// if(music.getStatus() == Status.Stopped)
// {
// music = new AudioNode(assetManager, “Sounds/Music/Super Smash Bionicle Main Theme 2.wav”, true);
// music.play();
// }

    time.update();
    currentTime = calculateTime(time.getTimeInSeconds());
    screen.findElementByName("CurrentTime").getRenderer(TextRenderer.class).setText("" + currentTime + "");
    screen.findElementByName("Player2Damage").getRenderer(TextRenderer.class).setText(Integer.toString(players[1].getPercent()) + "%");

    
}

@Override
public void cleanup() 
{
    rootNode.detachChild(localRootNode);
    guiNode.detachChild(localRootNode);

}

@Override
public void setEnabled(boolean enabled) 
{
    super.setEnabled(enabled);

    if (enabled) {
        System.out.println("enable");
    } else {
        System.out.println("disabled");
    }

}

@Override
public void bind(Nifty nifty, Screen screen) 
{
    System.out.println("Bind called");
    this.nifty = nifty;
    this.screen = screen;
}

public void onStartScreen() 
{
    //throw new UnsupportedOperationException("Not supported yet.");
}

public void onEndScreen() 
{
    //throw new UnsupportedOperationException("Not supported yet.");
}

public void guiInitiate()
{
    setStatusPanelLocations();
    setStockImageHeight();
    setPlayerAvatars();
    setPlayerEmblems();
    // Temporary
    screen.findElementByName("Player1Damage").getRenderer(TextRenderer.class).setText("10%");
}

public void setStockImageHeight()
{
    String width = "" + screen.findElementByName("Player1ProgressItem1").getWidth() + "px";
    String image = "";
    
    for (int i = 1; i <= 4; i++) 
    {
        for (int j = 1; j <= 5; j++) 
        {
            image = "Player" + i + "StockIcon" + j;
            screen.findElementByName(image).setConstraintHeight(new SizeValue(width));
            screen.findElementByName(image).getParent().layoutElements();
            
            if (players[i - 1] != null)
            {
                NiftyImage icon = nifty.getRenderEngine().createImage(screen,"Interface/CharacterInGame/StockIcon/" + players[i - 1].getMenuPlayer().costume + ".png", false);
                screen.findElementByName(image).getRenderer(ImageRenderer.class).setImage(icon);
            }
            
        }  
    }
    
}

public void setPlayerAvatars()
{
    String image = "";
    
    for (int i = 1; i <= 4; i++) 
    {
        image = "Player" + i + "Avatar";
        
        if (players[i - 1] != null)
        {
            NiftyImage emblem = nifty.getRenderEngine().createImage(screen,"Interface/CharacterInGame/Avatar/" + players[i - 1].getMenuPlayer().costume + ".png", false);
            screen.findElementByName(image).getRenderer(ImageRenderer.class).setImage(emblem);
        }
        
    }
}

public void setPlayerEmblems()
{
    String image = "";
    
    for (int i = 1; i <= 4; i++) 
    {
        image = "Player" + i + "Emblem";
        
        if (players[i - 1] != null)
        {
            NiftyImage emblem = nifty.getRenderEngine().createImage(screen,"Interface/CharacterInGame/Emblem/" + players[i - 1].getMenuPlayer().costume + ".png", false);
            screen.findElementByName(image).getRenderer(ImageRenderer.class).setImage(emblem);
        }
        
    }
}

public void setStatusPanelLocations()
{
    if (numberOfPlayers == 2)
    {
        screen.findElementByName("Player1StatusConstantPanel").setConstraintX(new SizeValue("24.0583%"));
        [/java]

The last line is where the NPE is called. The trace up to that is initialize, guiInitiate, setStatusPanelLocations, and then BAM.

I’m sure you guys have had issues like this crop up before, so hopefully this isn’t too hard to solve. I can always give more code if this isn’t enough. Thanks.

Also, a friend alterted me of this issue:

When he comments out everything relating to the screen, it sort of works, and then crashes. What I mean is, you can see the 3d stage being rendered behind the current GUI (which, mind you, isn’t right still), but then this occurs:

A fatal error has been detected by the Java Runtime Environment:

EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000000fdf30a0, pid=2316, tid=7232

JRE version: 7.0_11-b21

Java VM: Java HotSpot™ 64-Bit Server VM (23.6-b04 mixed mode windows-amd64 compressed oops)

Problematic frame:

C [ig7icd64.dll+0x30a0]

Failed to write core dump. Minidumps are not enabled by default on client versions of Windows

An error report file with more information is saved as:

C:\Users\Dell Notebook\Documents\JMonkey\BionicleShowdown\hs_err_pid2316.log

If you would like to submit a bug report, please visit:

http://bugreport.sun.com/bugreport/crash.jsp

The crash happened outside the Java Virtual Machine in native code.

See problematic frame for where to report the bug.

Any thoughts as to why?

ig7icd64.dll…

intel “graphics” card?

Well, yes. So there is a problem in the driver for the graphics card?

Even so, the original problem should occur due to this, because I’m running a nvidia GeForce500 on it. And this error occured because he tried to bypass the screen being null with a boolean somewhere.

Somehow your screen controller isn’t really being used by nifty. I stopped using nifty a long time ago so I’m not really inclined or motivated to help debug the issue but there are many others who can probably spot problem. It’s a pretty common one that comes up on the forum every couple weeks or so.

Whoever it is that finally helps will need to see how you are setting up the app state, how you are telling nifty about it, etc… Bottom line: if bind() isn’t getting called then nifty has no idea about this instance of the controller.

Hah. If you stopped using nifty, may I ask what you use instead? Nifty can be, how do I put it, a real pain my butt sometimes.

You say it has no idea of the instance of the controller, hmm? Then it would probably help if I post the code that creates the controller, right?

Well, here is the function that creates the state and detaches the previous one

@NiftyEventSubscriber(id=“StartMatch”)
[java] public void startMatch(String id, NiftyMousePrimaryClickedEvent event)
{
System.out.println(“Working?”);
player1.getCharacter().initializeCharacter(app,player1);
player2.getCharacter().initializeCharacter(app,player2);
//player3.getCharacter().initializeCharacter(app,player3);
//player4.getCharacter().initializeCharacter(app,player4);
if (currentMatch == null)
{
currentMatch = new Match(player1, player2, player3, player4);
currentMatch.getMatchSettings().setStock(7);
currentMatch.getMatchSettings().setTime(65);
}
else
{
currentMatch.reloadPlayers(player1, player2, player3, player4);
}

    if (player1.playerType.equals("CPU"))
    {
        currentMatch.setBalance(true);
    }
    else
    {
        currentMatch.setBalance(false);
    }
    
    System.out.println("Screen here is also " + screen);
    standardMatchState = new StandardMatchState(currentMatch);
    stateManager.attach(standardMatchState);
    stateManager.detach(mainMenu);
    
}[/java]

I really don’t want to post the full file. That’s just a lot of lines of code. But I will if necessary…

I wrote Lemur and use that for my game projects.

I don’t see anywhere in your code where you’ve given the controller to nifty.

Well, in the initialize of the second state I register the screen controller using [java]nifty.registerScreenController(this)[/java]

Are you telling me to delete that, and put :
[java]
nifty.registerScreenController(StandardMatchState)
[/java]

in the file that creates the instance of it?

When do you load the screen? Are you using XML or building the screen manually with builders? The controller has to be registered before you load the XML.

I’m using XML. And you can see in this code:
[java]
nifty = Main.getNifty();
nifty.registerScreenController(this);
nifty.addXml(“Interface/GUIS/StandardMatchHUD.xml”);
nifty.gotoScreen(“standardMatchHud”);[/java]

that I load the screen after I regesiter the controller for the new state.

This is all in the initialize of the new ScreenController.

If bind is never called then your controller is not being used. That’s all I can tell you. Others will have to help you sort out why that is.

Hmm, I should have mentioned this earlier. The code I posted in the original post worked before fine. It is when I update my SDK to the latest stable version released in September (I think) that this broke. Actually, a bunch of nifty things broke, and all of them we could fix except for this.

Does that give insight into my problem?

small help here, not a big one :

i had problem with a program with a network part where, when the server wasn’t sending data fast, the bug didn’t happened.

I found that the root of that is the bind is not called directly, synchronously

Yes, i have a 0.3 to 0.5 s interval between the moment i create the screen and the moment where “bind” is called.

I solved this by giving to the controller a reference to the screen, myself (i used “bind” to get this reference before).

1 Like

So what you are saying is I should manually get a refecrence to screen so that, in the event that actually bind is being called to late, due to async reasons (dunno if that holds for me) then screen won’t be null.

How did you give reference to screen that worked for you (hoping you’ll continue to give me small help :slight_smile: )?

hmmm like this :

[java]
Screen sc = nifty.getScreen(“ingame”);
InGame inGame = (InGame) sc.getScreenController();
inGame.setScreen(sc);
[/java]

“InGame” is the screencontroller (and also an appstate, but it’s an other topic) and the “setScreen” method is write by me. It’s pretty just … like all the setThing : assign to “thing”.

But this is the new method (from this evening). Before, i made the “new InGame()” myself, then use the “fromXml” method with the ingame as a parameter.

The problem with the server was : the server was sending a “setHealth” message (this is not the name of the message but it’s to explain) and when i was trying to get the element to display the difference i ended with a nullpointerexception, because the screen was null.

After this little assignement, everything was fine. Why i say this ? To say that even BEFORE the bind, it seems that you can access the element of the screen - which is a good news. But i don’t know how “safe” it is to do that.

2 Likes

Okay, is there any way to give gold, like in reddit? Or maybe there should be an upvote system (where you can only upvote. No need to create hostile feelings on a place of creation).

BECAUSE YOU DESERVE A MILLION UPVOTES.

No, in all seriousness, thank you. All I did was add your first line and it worked. Wow.

I want to say this is still an issue. Why did this happen? I don’t know. Perhaps because the load time for all the GUI elements was more than the load time for the 3d elements? This would match your initial prognosis. I know for a fact that the steps I used for the other states, all of which were GUI up till now, didn’t have this issue.

But I will tag as solved. Again, thanks.

1 Like

There is a voting thing already, click the little “thumbs up” and they get an upvote for their post and the count near their name goes up one (its the number before the p).

Never knew that…Thanks zarch.

Oh, and I can’t seem to figure out how to mark this as solved. Where’s that supposed to happen?

I had similar issues with nifty. Bind was not called on my screens sometimes. Hopefully they are going to fix that, but I agree, Nifty can really be hard to use.

actually, i am not sure that there is anything to fix about it. You DON’T want to have your app freeze when you switch screen, so you have to do it asynchronously. Yes, a method to oblige the screen to bind NOW (and freeze until it did) would be possible to add, but then people would come and complain that their apps freeze when they switch screen :roll:

My main question is more : why 0.3 second, it’s HUGE and i really don’t have a lot of things on my screen. I think that the root of the problem is that nifty is really under-optimized, espacially when you compare it to html renderer which have tens of years of optimization behind them.

It’s also sad that despite the fact that most plateforms (even mobiles) has a way to render html, we still need to embed our own renderer. But this is because of the lack of transparency in html renderers (so we can’t really have reliable ways to access them).