Spatials can contain Nodes, Geometries, and user data (player score, health, inventory, etc). You can save and load individual Nodes or Geometries, as well as whole scenes using com.jme3.export.binary.BinaryExporter and com.jme3.export.binary.BinaryImporter. The jMonkeyEngine's serialization system is the com.jme3.export.Savable interface; the jMonkeyEngine's binary file format is called .j3o. You can open, view, and edit .j3o files in the jMonkeyEngine SDK.
The following example overrides destroy() to save the rootNode to a file when the user quits the application. The saved rootNode is a normal .j3o binary file that you open in the SDK.
/* This is called when the user quits the app. */ @Override public void destroy() { String userHome = System.getProperty("user.home"); BinaryExporter exporter = BinaryExporter.getInstance(); File file = new File(userHome+"/MySuperGame/"+"LastSavedGame.j3o"); try { exporter.save(rootNode, file); } catch (IOException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Error: Failed to save game!", ex); } super.destroy(); // continue quitting the game }
The following example uses simpleInitApp() to load the last saved game when the game is initialized.
@Override public void simpleInitApp() { String userHome = System.getProperty("user.home"); BinaryImporter importer = BinaryImporter.getInstance(); importer.setAssetManager(assetManager); File file = new File(userHome+"/MySuperGame/"+"LastSavedGame.j3o"); if(file.exists()){ try { Node loadedNode = (Node)importer.load(file); loadedNode.setName("loaded node"); rootNode.attachChild(loadedNode); } catch (IOException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Could not load saved game.", ex); } else { // No previous game. Create new game, or let user browse to file, etc... }
JME's BinaryExporter/BinaryImporter can read and write standard Java objects (String, ArrayList, buffers, etc), JME objects (Savables, such as Material), and primitive data types (int, float, etc). If you are using any custom class together with a Spatial, then the custom class must implement the com.jme3.export.Savable interface. There are two common cases where this is relevant:
mySpatial.addControl(myControl);mySpatial.setUserData("inventory", myInventory);If your custom classes (the user data or the Controls) do not implement Savable, then the BinaryImporter/BinaryExporter cannot save the Spatial!
So every time you create a custom Control or custom user data class, remember to implement Savable:
import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.export.Savable; import com.jme3.material.Material; import java.io.IOException; public class MyCustomClass implements Savable { private int someIntValue; // some custom user data private float someFloatValue; // some custom user data private Material someJmeObject; // some custom user data ... // your other code... ... public void write(JmeExporter ex) throws IOException { OutputCapsule capsule = ex.getCapsule(this); capsule.write(someIntValue, "someIntValue", 1); capsule.write(someFloatValue, "someFloatValue", 0f); capsule.write(someJmeObject, "someJmeObject", new Material()); } public void read(JmeImporter im) throws IOException { InputCapsule capsule = im.getCapsule(this); someIntValue = capsule.readInt( "someIntValue", 1); someFloatValue = capsule.readFloat( "someFloatValue", 0f); someJmeObject = capsule.readSavable("someJmeObject", new Material()); } }
To make a custom class savable:
Savable and add the write() and read() methods as shown in the example above.write()s the data to the JmeExport output capsule. read…()s the data to the JmeImport input capsule. capsule.read…() method for the data type. Specify the String name of the variable (must be the same as you used in the write() method), and again specify a default value.