Save Scene when switching AppStates

Hello. I’m using JME 3.0 Stable Edition. I’m trying to make a project with scene and menu. My current project consists of two AppStates: MainGameAppState and MenuGameAppState. Switching to menu app state is done by pressing ESC key, this behavior is defined in main module. When I switch from MainGameAppState to MenuGameAppState, I remove all entities from rootNode and after switching from menu state, I’m adding them back.
I’m terrible sorry to ask such question, what is the best and the most efficient way to save all the parameters (rotation, position, color…) of items in scenegraph (concerning rootNode of MainGameAppState)?

In order to further clarify the question, I’m posting post the setEnabled routine of MainGameAppState (made by example from https://jmonkeyengine.github.io/wiki/jme3/advanced/application_states.html#pausing-and-unpausing)

@Override
public void setEnabled(boolean enabled) {
    // Pause and unpause
    super.setEnabled(enabled);
    if(enabled){
        // init stuff that is in use while this state is RUNNING
        //System.out.println("++++++Enabling scene++++++");
        if (justInitialized == false) {
            this.basicSetLight();
            this.setScene();
            this.setCamera();
            this.registerInput();
            
        } else {
            justInitialized = false;
        }
      } else {
        //System.out.println("------Shuting down scene------");
        // take away everything not needed while this state is PAUSED
        //lights are removed separately
        rootNode.removeLight(al); rootNode.removeLight(sun);
        //unregistering events. They're handled differently in menu
        app.getInputManager().deleteMapping("moveForward");
        app.getInputManager().deleteMapping("moveBackward");
        app.getInputManager().deleteMapping("moveLeft");
        app.getInputManager().deleteMapping("moveRight");
        app.getInputManager().deleteMapping("rotateRight");
        app.getInputManager().deleteMapping("rotateLeft");
        app.getInputManager().deleteMapping("rotateUp");
        app.getInputManager().deleteMapping("rotateDown");
        
        rootNode.detachAllChildren();
      }
}

As you might see, the scene is being recreated from scratch every time after activation of AppState. I’m looking for a way to store the scene state and elements’ data (preferrably in RAM, not on drive, if possible) which might be changed on interaction. I’ve referred to manual on saving data to .j3o file, but this does not look suitable for this task.

J3o is suitable for that Task but how about having a proxy node (virtual rootNode) which you save as variable and only remove that from the rootNode and attach it again, Later on.

Thank you. I have tried an offered solution and it worked. The state of nodes is being saved properly.
To sum it up:

  1. One should define inside the AppState subclass:
    private Node shadowNode;
  2. Add everything to shadowNode (except of light)
    2a. Add ShadowNode as the child of rootNode, with lights being children of rootNode too
  3. Child nodes would be kept upon removing/adding shadowNode on app state switching (hope that GC would not eat the stored and unused shadowNode when Menu app state is active)
public void setEnabled(boolean enabled) {
    // Pause and unpause
    super.setEnabled(enabled);
    if(enabled){
        // init stuff that is in use while this state is RUNNING
        if (justInitialized == false) {
            this.registerInput();
            this.rootNode.attachChild(shadowNode);
            System.out.println("Attaching sun");
            rootNode.addLight(this.sun); rootNode.addLight(this.al);
        } else {
            
        }
      } else {
        // take away everything not needed while this state is PAUSED
        justInitialized = false;        
        rootNode.removeLight(al); rootNode.removeLight(sun);        
        app.getInputManager().deleteMapping("moveForward");
        app.getInputManager().deleteMapping("moveBackward");
        app.getInputManager().deleteMapping("moveLeft");
        app.getInputManager().deleteMapping("moveRight");
        app.getInputManager().deleteMapping("rotateRight");
        app.getInputManager().deleteMapping("rotateLeft");
        app.getInputManager().deleteMapping("rotateUp");
        app.getInputManager().deleteMapping("rotateDown");        
        //rootNode.detachAllChildren();
        rootNode.detachChild(shadowNode);
      }
}

As long as the shadowNode is still in an appState which is still active (i.e. attached but disabled !!) then it won’t be GCed. You can also grab a List for the lights if you wanted it to be clean, but I guess it works.

Edit: I should have read DarkChaos’s response more completely before posting. Oh, well… now you have two people telling you the same thing. :slight_smile:

Then that’s easy… just keep a reference around to the node(s) you removed… then add them back again. Instead of calling “detachAllChildren” remove specific children. This is easiest if you create your own local root node and just attach/detach that… and add all of your other spatials to that. But you can pretty easily grab the children and remove them if your attach code is scattered all over.

List<Spatial> mySavedScene;

....to clean up
List<Spatial> children = rootNode.getChildren();
mySavedScene = new ArrayList<>(children);
rootNode.detachAllChildren();

....to add them back
for( Spatial s : mySavedScene ) {
    rootNode.attachChild(s);
}

Again, really easier if you just keep your own scene root that you attach to rootNode.

Node myScene = new Node("My Scene");
rootNode.attachChild(myScene);

...setting the scene up
myScene.attachChild(someModel); // instead of attaching to rootNode

...to clean up
myScene.removeFromParent();

...to add the scene back
rootNode.attachChild(myScene);

You also might benefit from upgrading JME at some point so you can use BaseAppState which has nicer extension points for enable/disable. It makes a clear distinction between init/enable/disable/cleanup… but provides consistent behavior that enable is always called, etc…

You can see how this principle is used in the MainMenuStates of pretty much every game example I’ve posted. Those states attach/detach some single root to the guiNode but the principle is the same.

In this case it’s a “mainWindow” but essentially the same:

Or the ModelViewState also does it with real models and the scene:

At some point you might want to consider having real game objects and properly separating your “view” from your “data model”. Treating spatials like game objects is fine for small quick-and-dirty apps (heavily on the dirty) but they fall over pretty quickly as they grow.