Difficulty with AppStates... Attach & Detach

Hello,

We are trying to implement AppStates to our game. We have a Nifty Title Screen, and a scene based off of TestPostWater.java

We can successfully load the TestPostWater.java AppState from the title screen! Yay! However switching back is quite difficult.

Even the tutorials on AppStates do not help. (https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:appstatesdemo)
They say they are out of date. They compile, however if you toggle between the scenes you will notice the cubes do not detach properly.

What is the correct way to Detach and Attach an AppState? Because nifty things are their own beast, we have plugged in the StartScreenTest.java as our title screen to toggle between that and our water scene. As previously stated we can succesfully switch the initial state from StartScreenState -> TestPostWater. However when going back, we have difficulties. In this instance, the grey background and yellow cube will not load, but what will load is the TextBox.

Thinking about it now I guess the GuiNode is loading successfully, however the rootNode is not.

What are the correct methods to load and detach a state? Thank you.

For regular states, you just detach them or attach them by calling AppStateManager.atttach() or detach(). You haven’t really explained what you’ve tried and what the problem is.

If you’ve implemented stateAttached() or stateDetached() methods then that’s wrong. It’s the huge problem with the demo/tutorial. Generally, don’t ever implement these methods.

If you are implementing intiailize() and cleanup() as appropriate then the problem may be nifty specific. Keeping the screen controller side happy can be tricky but I can’t see anything about how you’ve done it.

I call stateManager.detach(otherState) from this state’s stateAttached(); as far as I know that’s supposed to be safe.
The nice thing about this approach is that AppStates need to know nothing about each other except which ones to attach/detach on stateAttached/stateDetached. (The tutorial code pollutes AppStates with knowledge about the inner workings of other Appstates.)

@toolforger said: I call stateManager.detach(otherState) from this state's stateAttached(); as far as I know that's supposed to be safe.

You assume wrong :slight_smile: I made a thread a while back, its something to do with the update loop executing 1 more time after you detach it depending on the order you attached them in the first place. You can prevent this happening by calling setenabled(false) in the detach method No idea if this has anything to do with ur issue, I’m just saying.

You should use initialize/cleanup as Paul suggests,if possible

Surprising. I’d have expected the exact opposite: use stateAttached/detached to make sure that initialize/cleanup is still called within the same cycle.
But then I purposefully constructed my AppStates so that they never access each other other than by attaching/detaching them with the AppStateManager, that makes them quite robust against any sequencing problems.

@toolforger said: Surprising. I'd have expected the exact opposite: use stateAttached/detached to make sure that initialize/cleanup is still called within the same cycle. But then I purposefully constructed my AppStates so that they never access each other other than by attaching/detaching them with the AppStateManager, that makes them quite robust against any sequencing problems.

stateAttached/stateDetached() are called inline. So, yeah, if you really want them within the same cycle then you can detach another app state there. They’ll be called before attach()/detach() returns, even.

…now, the actual cleanup() won’t be called until the next update.

@wezrule said: You assume wrong :) I made a thread a while back, its something to do with the update loop executing 1 more time after you detach it depending on the order you attached them in the first place. You can prevent this happening by calling setenabled(false) in the detach method No idea if this has anything to do with ur issue, I'm just saying.

You should use initialize/cleanup as Paul suggests,if possible

All of my advice is caveated with “except for the people who know better”. I can’t say that because then everyone will think they know better.

It is also not dependent on the order your attached them. All initialize(), cleanup(), and update() are run in blocks together. Ah, I see… you detached a state later in the list and it still had its update() called. Yep, that can happen.

…doesn’t matter whether you call it from stateAttached or just do it directly, though.

1 Like

Thank you very much for the help.

I have removed the stateDetached() and stateAttached() methods. Now I am using the initialize(), cleanup(), and some setEnabled(bool).

Now, I can successfully switch from Nifty Title -> Game -> Nifty Title!!! Yay! However, when I switch back to the Game, I have lots of issues. My Game is an ocean based off of TestPostWater.java that has ships. When switching back to the game, the ships are loaded upside down and the sky is not loaded. However, what is cool, is that the camera stays the same, so it does mimic a “pause” button.

When I go from Game -> Nifty Title I execute this code which is in my Main.java (extends SimpleApp) and is used next to the code that can detachApps from stateManager.

[java]
stateManager.detach(gameRunningState);
game.cleanup(); [/java]

[java]
public void cleanup() {
super.cleanup();
// unregister all my listeners, detach all my nodes, etc…
this.app.getRootNode().detachAllChildren();
this.app.getGuiNode().detachAllChildren();
this.app.getViewPort().clearScenes();
this.app.getViewPort().clearProcessors();
this.app.getGuiViewPort().clearScenes();
} [/java]

I have reason to believe my initialize methods are not being called successfully when I switch AppStates back to the Game, most likely because I am using processing effects in the ViewPort. As I said, the sky is not loading correctly, and my ships are all upside down. All of my code to initialize my Game is in the initialize method. So I modularized all of the code in intialize into 5 methods, then called these 5 methods from setEnabled(true). This does not work, infact it seems to load the game twice with doubles of ships, and encourages crashing.

Thanks for the help.

T&D

This looks a bit suspicious:
game.cleanup();

Is game an app state? You should never call initialize() and cleanup() yourself as the state manager will do it for you.

Ok. Thanks I will remove this code. Still hasn’t solved issue. It is totally issue with the ViewPort or something, because the water loads on the scene, but the sky doesn’t. So basically The update() method is working (to have the water ripple) but the initial loading of the sky and the ships is all skewed. This is all in initialize.

These issues arrise when I load the appstate back.

[java]
stateManager.add(titleMenu);
titleMenu.setEnabled(true); // calls initializing
stateManager.detach(titleMenu);
titleMenu.setEnabled(false);
stateManager.add(gameRunningState);
stateManager.detach(gameRunningState);
stateManager.add(titleMenu);
titleMenu.setEnabled(true); // calls initializing
stateManager.detach(titleMenu);
titleMenu.setEnabled(false);
stateManager.add(gameRunningState); //<-- this is where the errors happen. Everything is perfect until this point. [/java]

Of course this is not the exact code, but merely sequence of adding and detaching that results in error.

So when you load the appstate back, do you have to reinitialize? Shouldn’t it do this for me? That is essentially how I have the titleMenu working.

My nifty Title Menu setEnabled(bool) looks like this:

[java] @Override
public void setEnabled(boolean enabled){
// Pause and unpause
super.setEnabled(enabled);

  if(enabled){ begin();}           // call custom methods...
   else {
    // take away everything not needed while this state is PAUSED
   nifty.removeScreen("start");
  }
} [/java]

Thank you.

Yes, initialize will be called again when you attach it.

I presume that begin() is also called in initialize() if isEnabled() is true? Or do you always make sure to setEnabled(true). Anyway, I guess that app state isn’t the problem since you say that gameRunningState is the issue.

Maybe something is not cleaned up properly in cleanup() or initialized like you think in initialize(). Since we can’t see the app state we can only guess.

By the way, here is a useful app state base class that handles attachment/detachment/enablement/etc. in a nice standard way:

http://code.google.com/p/jmonkeyplatform-contributions/source/browse/trunk/Lemur/src/com/simsilica/lemur/event/BaseAppState.java

1 Like

Problem solved.

The biggest issue is the idea of localRootNode & localGuiNode vs. rootNode & guiNode. I have only worked with SimpleApplication before, where rootNode is a super global variable that does not need to be initalized and can always be called.

With AppStates, each appstate must have their own localRootNode and localGuiNode that you use to attach your objects to the scene. Then after, you have added everything to your localRootNodes, add the localRootNode to the rootNode. Finally, during cleanup, remove the children from ONLY the localRootNode using:

[java] public void cleanup(){
localRootNode.detachAllChildren();
localGuiNode.detachAllChildren(); } [/java]

or

[java]
rootNode.detachChild(localRootNode);
guiNode.detachChild(localGuiNode); [/java]

I’m not sure if either is better than the other. Personally, I prefer the first.

So after changing all of my variables from rootNode -> localRootNode, I still had some issues. My scene utilizes the ViewPort. ViewPort has 2 clean up commands, clearProcessors() and clearScenes().

For the solution to my problem I only needed to viewPort.clearProcessors() ONLY, I did NOT need to clearScenes(). If I clearScenes() AT ALL (as in, just clearScenes, or both clearProcessors() AND clearScenes()) it results in improper removal of my scene.

At the moment, I only need to setEnable(true/false) the nifty titleMenu. I do not need to setEnable() my gameplay scene for the game to work correctly. I will continue to optimize the code throughout the week, possibly I can remove the code for setEnable for the nifty titleMenu.

Thank you for your help, I will gladly change the (https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:appstatesdemo) for any admin. I have changed the code and updated it on my machine so it runs 100% correctly.

Thank you for the help on understand AppStates, specifically how cleanup()/initialize() is used and how not to use stateAttach/Detach()

The second way is a little easier if you wish to support enable/disable that attaches and removes the local stuff.

I’m still using the same global rootNode and guiNode, but I have my AppStates add and remove their own roots below them.
That way, I don’t need to worry about properly initializing the superglobal nodes with buckets and camera and whatnot. Also, my example code doesn’t raise eyebrows; it is a factor because having to defend nonstandard architecture choices tends to distract from the issue that one wants the answers for.

Oh. And my StdAppState class overrides all the functions to provide logging, something like this:
[java]public class StdAppState extends AbstractAppState {
private static Logger logger = LogTools.getLogger();
…
@Override
public void cleanup() {
logger.debug(“cleanup {}”, this);
super.cleanup();
}
…
}
[/java]
(I’m using SLF4J so the logger calls look a bit different than what one would use for the standard Java logging that the JME engine uses.)