These are questions I have as a beginner, and the (current) answers I found. Status: I'm updating this to JME 2.0.
The jMonkeyEngine project is for people who want to write 3-D computer games of their own. As an example, watch this movie of a 3-D game created with jme. You can learn how to write games like that! :) It's tough but worth it.
What you need (in this order)
| You need | What for? | From Where? |
|---|---|---|
| Java Development Kit (JDK) | Tools to develop your project. Includes Java compiler, runtime, and more. | sun.com |
| Build Tools: Ant or Maven, JUnit | Tools to automate clean/build/test/run cycle | apache.org, junit.org |
| File version control: Subversion | Utility to download latest version of JME sources | tigris.org |
| Any 3-D mesh editor | Tool to create 3-D models | See below… |
| (LWJGL Java Game Library) | OpenGL for 3D graphics, OpenAL for 3D sound | (Included in JME) |
| (Jogg, Jorbis) | Libraries for Ogg Vorbis sound | (Included in JME) |
| jME sources and libraries | The 3-D Game Engine | jmonkeyengine.com |
| Optional: The jME physics engine | 3-D game physics | jmephysics |
Tip: Using an integrated development (IDE) environment is optional, but recommended. An IDE is a developer utility that integrates all your development tools (compiler, build tools, file version control, debugger, profiler, editor, documentation, etc) into one graphical interface and spares you some of the complications of the commandline. See also: All Netbeans Tutorials.
Download jME_2.0_Complete and jME_2.0_Distribution
The jMonkeyEngine (jME_2.0_Distribution) These are all bundled in jME_2.0_Complete/jME_2.0.jar !
jme.jar jme-effects.jar jme-swt.jar jme-audio.jar jme-font.jar jme-terrain.jar jme-awt.jar jme-gamestates.jar jme-xml.jar jme-collada.jar jme-model.jar jme-editors.jar jme-scene.jar
Third-party JAR libraries (jME_2.0_Complete/lib/)
gluegen-rt.jar junit_4 swt.jar jinput.jar lwjgl.jar lwjgl_util.jar lwjgl_util_applet.jar jogl.jar jorbis-0.0.17.jar
Third-party native libraries (jME_2.0_Complete/lib/natives/)
OpenAL32.dll openal.dylib libopenal.so libopenal64.so lwjgl.dll liblwjgl.jnilib liblwjgl.so liblwjgl64.so odejava.dll libodejava.jnilib libodejava.so libodejava64.so jinput-dx8.dll libjinput-osx.jnilib libjinput-linux.so libjinput-linux64.so jinput-raw.dll
MyGame, MyGame/lib/ and MyGame/lib/natives/.MyGame. MyGame/lib/. (Otherwise you get a NoClassDefFoundError for jME classes.)MyGame/lib/natives/. The directory of native libraries needs to be on your project's java.library.path! (Otherwise you'll get UnsatisfiedLinkError: no lwjgl in java.library.path)
This allows the Java Virtual Machine to find libraries when running the game like this:
java -jar -Xmx256m -Djava.library.path=“lib/natives” MyGame.jar
See also: getting_started, jme_maven_setup, packaging_and_deploying_jme_applications.
What is the Java Classpath? For platform-independent Java Classes
What is the Java Library Path? For platform-dependent native dynamic libraries
jME won't work without those files in place, period.
Some troubleshooting tips for very common errors and warnings: (Don't worry, we all get these!)
What does it mean? You are missing important native dynamic libraries (files like lwjgl.dll and many more) on the java.library.path.
First determine where the problem starts, is it in the IDE, in your app, the location of the libraries, your path settings?
Library Paths?
lib directory in the same directory as your application's JAR? lib directory contain all the necessary JARs?natives directory inside the lib directory? native directory? If yes, then:
java -jar -Xmx256m -Djava.library.path=“lib/natives” MyGame.jar Typos? If your game can be started from the command line, you at least know that your Java game works, and your libraries are correctly set up. If this does not work, first make sure you have the libraries and paths right. (This HAS to work.)
Do you use an IDE?
If the game starts, but not from your IDE, configure the java library path in the project's Run settings as -Djava.library.path=“lib/natives” . (See individual IDE documentation for details.)
Another workaround…
If the above doesn't work, move all native libraries from the lib/natives/ directory right next to your game's JAR. This may work because the java library path is set by default to search the libraries in the current working directory.
For Linuxers You can also add all the native libraries to the environment variable LD_LIBRARY_PATH. This is possible, but not generally recommended.
Which Java class does it say is missing?
Clearly missing or mislinked native libraries necessary for this operating system.
Just for fun: Some debugging code to check paths. PS: You cannot use this to set the environment variables, you can only read them after the fact!
System.out.println("java.library.path = " + System.getProperty("java.library.path"));
System.out.println("java.ext.dirs = " + System.getProperty("java.ext.dirs"));
System.out.println("java.class.path = " + System.getProperty("java.class.path"));
cd jME_2.0_Complete_(r4093); java -jar -Xmx256m -Djava.library.path="lib/natives" jME_2.0.jar
Tip: Build the javadoc from the sources and open it in your webbrowser: file:/YOUR_PATH_TO_JME/jme/data/doc/index.html and file:/YOUR_PATH_TO_JME/jME-Physics_2/dist/javadoc/index.html
First of all follow the great tutorials in jme/src/jmetest/ (and com/jmetest/physicstut respectively) to get started quickly: You begin with a SimpleGame with object nodes (and you can proceed to learn to create a SimplePhysicsGame with dynamic nodes). This way you can play and experiment, and learn the basics without bothering about implementation details at first.
It's fine to use SimpleGame for quick demos. When you got acquainted with the API, and want to start coding your actual game, you will want to be able to control all the implementation details yourself. Take some time to understand how SimpleGame and BaseSimpleGame are implemented to learn how to create your custom subclass of BaseGame.
Instance variables and methods, and where they were inherited from:
One example for how you can proceed:
Scene graph: – A data structure to store geometrical objects in 3-D space.
States: – Rendering settings used by the 3-D engine for each node
The scenegraph is a tree structure of nodes, with a root, branches and leaves. Each element in your scene (root nodes, branch nodes, leaf nodes) is referred to as spatial. Only leaf nodes are visible to your players, the rest is game internal.
Geometry or a Node. | Root Node |
|---|
| scene graph |
| A Spatial | |
|---|---|
| Rotation, Translation, Scale | |
| World Bounding Volume | |
| Render States | |
| Geometry | Node |
| All vertices (corner points) | Bounding Volume of all child nodes |
| Local Bounding volume | Links to its child spatials (Node and Geometries) |
| Optional: Solid color | |
See also: graphic
While a geometry defines a objects's shape, a render state describes how this object should look, how it is rendered. Each Node has render states, if you don't define any, it will have the same as its parent node. You can group nodes that should be rendered the same under one node that carries a specific render state (e.g. all UI elements, all transparent things, etc).
Usage:
All are optional, but I sorted them by how commonly I guess they are used.
ZBufferState buf = display.getRenderer().createZBufferState(); buf.setEnabled(true); buf.setFunction(ZBufferState.CF_LEQUAL); rootNode.setRenderState(buf); LightState ls = display.getRenderer().createLightState(); ls.setEnabled(true); rootNode.setRenderState( lightState ); ls.attach(light5);
For a simple test world, you attach the built-in spheres and boxes of the game engine to the rootNode, as demonstrated in the tutorials.
For more complex objects like houses, characters and vehicles, you need to create models in a 3-D mesh editor. See next:
These 3-D mesh editors are free, and platform-independent for Mac/Win/Linux:
Which formats does jME import? Have a look at com.jmex.model.XMLparser.Converters.*
Add-Ons:
Most 3-D editors can export and import at least 3D Studio Max (.3ds) and Wavefront Object (.obj/.mtl) well, both of which can be read by the jmonkeyengine. They have advantages (3ds supports animation) and disadvantages (obj does not support transparency. 3ds loses transparency after import?). I haven't tested all of them yet…
Currently my favorite format for static models is Collada (.dae). There is a free plugin for Blender that lets you export a static model as Collada including transparency. For animated objects, try md5Reader.
Tip: How to export Collada files from Sketchup? Export model as .kmz file, rename suffix to .zip, unzip file, rename suffix to .dae!
Open Question: Will Blender's Collada exporter ever export textures, animations? Will jMonkeyEngine import collada+textures?
public Node loadMy3DSModel(){ Node gamemap = new Node(); URL modelURL=MyGame.class.getClassLoader().getResource("data/model/mymodel.3ds"); URL textureURL=MyGame.class.getClassLoader().getResource("data/model/"); BinaryImporter importer = BinaryImporter.getInstance(); FormatConverter converter=new MaxToJme(); // com.jmex.model.XMLparser.Converters.* ByteArrayOutputStream BO=new ByteArrayOutputStream(); try { converter.convert( modelURL.openStream(), BO ); gamemap=(Node)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray())); gamemap.setCullMode( SceneElement.CULL_NEVER ); gamemap.setModelBound( new BoundingBox() ); gamemap.updateModelBound(); } catch (IOException e) { e.printStackTrace(); System.exit(0); } return gamemap; }
public Node loadMyColladaModel() { // your path to the model's textures inside YourGame URL url = YourGame.class.getClassLoader().getResource("data/model/"); //you path to the model itself inside YourGame. InputStream gamemap = YourGame.class.getClassLoader() .getResourceAsStream("data/model/mycoolmodel.dae"); if (gamemap == null) { System.out.println("Collada data file not found"); System.exit(0); } ColladaImporter.load(gamemap, url, "model"); Node n = ColladaImporter.getModel(); n.setModelBound(new BoundingBox()); // collision detection n.updateModelBound(); // collision detection return n; }
Attach all transparent objects to a node with a transparent BlendState, or set the object's individual BlendState.
When blending, what is Alpha Test, Reference Value, Source and Destintion? See: http://www.opengl.org/resources/faq/technical/transparency.htm
What about objects that are part opaque, part transparent? (e.g. house with windows.)
Idea: Split these things apart and load them half and half (first all opaque walls and floors, then all window panes)?
Optimal sizes for textures are 16*16, 32*32, 64*64, 128*128, 256*256, 512*256 (Is this a graphic card requirement?) Note: For TerrainPages it's that number +1 for some reason.
A sky box (com.jme.scene.Skybox;) or sky dome is an artificial horizon and sky for your 3D world. It is a quick and efficient way to make the environment look more realistic.
Also look at the Terrains samples to use things such as com.jmex.terrain.TerrainPage. Terrains can be generated from a 2-D grayscale bitmap file (darker is lower, lighter is more elevated).
light.setEnabled(true)? (Or setAttenuate(true) if you use that)? Did you set the root node's renderState to this LightState? Did you attach a light to the lightState?
See also: Light JavaDoc
If your dynamic objects fall through the floor:
SyntheticButton stuff in Lesson 8 of the physics tutorial?setModelBound(new BoundingBox()) (or BoundingSphere), updateModelBound(), setCenterOfMass(), and computeMass() on the dynamic node? In this order?For non-physical games, have a look at the TestObjectWalking class and “how to implement walking” below.
You keep bumping into invisible barriers in door frames, your cubes roll downhill, and your spheres sit on slopes like blocks… What the heck is going on?
myCrate.setModelBound(new BoundingBox()). myRock.setModelBound(BoundingSphere()).
Tip 1: Bounds are either Box or Sphere. You can only set the Bound shape once per model, but it affects each part of the model individually. E.g., if you have a building with BoundingBox set as Bound, it will not be one big block, but each wall and each floor is treated as one box, the way you would expect it.
Tip 2: If there are grouped objects inside an imported model, the whole group will get one BoundingBox (or BoundingSphere)! This can be an effect that you want – or it may accidentally fill up corners and doorframes with invisible barriers.
Tip 3: Bounding Spheres cannot be elliptical in jMonkeyEngine, but you can put two spheres on top or next to each other for a similar bounding effect.
Very common question regarding updating the scenegraph state.
thing.getLocalTranslation().set() to move the thing to a position, thing.getLocalScale().set() to change its size, and thing.getLocalRotation().set() to turn or tilt it. Do not use getWorldTranslation().set() nor setLocalTranslation() etc.updateGeometricState() (low-cost).updateRenderState() (expensive, use sparingly).updateCollisionTree() once.setModelBound(), always call updateModelBound().See also updatefuncs.
Use player.lookAt(v1,Vector3f.UNIT_Y) (UNIT_Y just means he should stand upright).
/* player looks in same direction as camera */ input.addAction( new PlayerMouseLookAt() , InputHandler.DEVICE_MOUSE, InputHandler.BUTTON_NONE, InputHandler.AXIS_ALL, false ); ... private class PlayerMouseLookAt extends InputAction { DynamicPhysicsNode player; public PlayerMouseLookAt() { } public void performAction(InputActionEvent e) { if ( e.getTriggerDelta() != 0f ) { Vector3f v = new Vector3f( player.getWorldTranslation().x, player.getWorldTranslation().y, player.getWorldTranslation().z ); v.addLocal( cam.getDirection().x, 0f ,cam.getDirection().z ); player.lookAt( v , Vector3f.UNIT_Y ); } } }
This happens if your 3D editor uses different coordinate system axes than the jmonkeyengine. Rotate your object back after you load it, for example:
model.setLocalRotation( new Quaternion().fromAngleAxis( - FastMath.PI/2, new Vector3f(1,0,0)) );
Open question: I imported an existing model into Blender and edited it. After export to Collada, the pre-existing parts of the model are shown correctly in jME, but all the edits and additions I did using Blender are distorted (wrong scale, tilted)?!
Work-around: Use the following settings in Blender's Collada exporter: Triangles YES, Bake Matrices NO, disable physics NO, only current scene NO, use relative path YES, use UV image NO.
Keep these rotation constants handy so you don't have to type the whole quaternion every time.
public static final Quaternion ROLL045 = new Quaternion().fromAngleAxis(FastMath.PI/4, new Vector3f(0,0,1)); public static final Quaternion ROLL090 = new Quaternion().fromAngleAxis(FastMath.PI/2, new Vector3f(0,0,1)); public static final Quaternion ROLL180 = new Quaternion().fromAngleAxis(FastMath.PI , new Vector3f(0,0,1)); public static final Quaternion ROLL270 = new Quaternion().fromAngleAxis(FastMath.PI*3/2, new Vector3f(0,0,1)); public static final Quaternion YAW045n = new Quaternion().fromAngleAxis(- FastMath.PI/4, new Vector3f(0,1,0)); public static final Quaternion YAW045 = new Quaternion().fromAngleAxis(FastMath.PI/4, new Vector3f(0,1,0)); public static final Quaternion YAW090 = new Quaternion().fromAngleAxis(FastMath.PI/2, new Vector3f(0,1,0)); public static final Quaternion YAW180 = new Quaternion().fromAngleAxis(FastMath.PI , new Vector3f(0,1,0)); public static final Quaternion YAW270 = new Quaternion().fromAngleAxis(FastMath.PI*3/2, new Vector3f(0,1,0)); public static final Quaternion PITCH045 = new Quaternion().fromAngleAxis(FastMath.PI/4, new Vector3f(1,0,0)); public static final Quaternion PITCH090 = new Quaternion().fromAngleAxis(FastMath.PI/2, new Vector3f(1,0,0)); public static final Quaternion PITCH180 = new Quaternion().fromAngleAxis(FastMath.PI , new Vector3f(1,0,0)); public static final Quaternion PITCH270 = new Quaternion().fromAngleAxis(FastMath.PI*3/2, new Vector3f(1,0,0)); public static final Quaternion UPRIGHT = new Quaternion().fromAngleAxis(- FastMath.PI/2, new Vector3f(1,0,0));
If you want more than walking, consider introducing a roaming_mode variable for the player. It keeps track of mutually exclusive maneuvering states such as standing/walking/running, floating/flying/rising, hanging/climbing/ascending, treading_water/swimming/diving, jumping. (Use an enum.)
All these states typically go along with different speeds and different animations.
The point of view can be a first-person or third-person character (the difference is where the camera is attached).
Have a look at the TestObjectWalking sample. In short:
Concerning non-physical games based on the method proposed in TestObjectWalking:
When using the idea proposed in the TestObjectWalking class, the avatar will be able to walk on floors, but it still walks right through walls. This is because the algorithm only tests for triangles directly under the player's fleet. Walls and other vertical objects are not covered by this algorithm. It needs to be extended to detect walls.
Idea: Attach all wall objects to one wallsRootNode. Use hasCollison on this wallsRootNode and the player. This way you only test collision with potential walls and not waste time testing everything in the scenegraph (no need to test the floor nodes too, for instance). If Collision, stop player from walking on by setting its velocity to e.g. a negation of its current speed (bounces off obstacle) or to zero.
See colliding with the walls tutorial and sample code for the Flag Rush tutorial lesson 10.
See how player velocity is implemented in Vehicle.java:
public void update(float time) {
this.localTranslation.addLocal(this.localRotation.getRotationColumn(2, tempVa).multLocal(velocity * time));
...
First implement walking and the roaming_mode enum. The player is stuck to the floor…
Idea:
flying roaming_mode and presses arrow keys: flap wings constantly to stay up, does it stay afloat, does it constantly rise like a hot air balloon?)…?
First implement walking and the roaming_mode enum. The player will walk to the bottom of any body of water…
Idea: (I assume here the water surface is a Quad.)
First implement walking and the roaming_mode enum. The player will run uphill too easily…
General idea – Just a guess: Determine the slope of the triangle the character stands on (in calculus terms: the 1st derivative of a line tangent to the terrain at this point). Let's assume that your characters can handle anything up to a 100% = 45° slope.
Consider slowing down walking speed according to steepness and skill!
Mouse Picking is the keyword here. Have a look at the com.jme.intersection.* package.
Demos such as MousePick + TestPick show you how to determine what (which object, which mesh) the user clicked.
Results accuracy: You can retrieve information about which mesh was clicked (com.jme.intersection.BoundingPickResults). You can even set it to determine which triangle on the mesh was clicked (com.jme.intersection.TrianglePickResults).
You can respond to clicks (com.jme.input.action.mouseInputAction) or keyboard input (com.jme.input.action.KeyInputAction) in a similar way.
/* as part of game initialization: */ MousePick pick = new MousePick(cam, clickableNode, ...); input.addAction(pick);
public class MousePick extends MouseInputAction { public MousePick(Camera camera, Node scene, ...) { /* init variables */ } public void performAction(InputActionEvent evt) { clickTime += evt.getTime(); if( MouseInput.get().isButtonDown(0) && clickTime > 0.1f) { clickTime = 0; Ray ray = new Ray(camera.getLocation(), camera.getDirection()); /* Line of sight */ PickResults results = new BoundingPickResults(); /* Set accuracy */ results.setCheckDistance(true); /* Order results closest first */ scene.findPick(ray,results); /* List everything in line of sight */ if(results.getNumber() > 0) { for(int i = 0; i < results.getNumber(); i++) { /* Now analyse what was clicked... */ System.out.println( results.getPickData(i).getTargetMesh().getName() ); System.out.println( results.getPickData(i).getDistance() ); /* ... and respond by informing the controller. */ } } results.clear(); } } }
This is a jME physics-related question.
Sliding: Your objects slide unstoppably up and down hills as if on ice?
contactDetails.setMu( Float.POSITIVE_INFINITY ) which does not slide.
Shivering: Does the Physics engine constantly recalculate the object's rotation on uneven ground, so it starts shivering or dancing?
thing.lockBounds()). This method isn't good if you need collision detection to consider the character's real shape in widely different stages of animation.
Toppling over: Even little obstacles make your object fall over?
player.setCenterOfMass( new Vector3f( 0, -0.5f, 0 ) );
Tip: Physics generally only make sense for objects with physcial behavior, typically things such as cars driving with a shock-absorbing suspension, bouncing/rolling balls, and falling/shifting boxes. Do not use physics for tall characters walking on the ground, they will not behave as expected. A walking person's motion does not correspond to a physical object being pushed around by a mechanical forward force. For characters see: TestObjectWalking sample.
Learn about Java Serialization. In short:
Serializable interface. And all the sub objects it depends on, too! Start at the bottom.If you change the read/write order, or any data type in your object, the serialization will break–and you will not be able to load any previously saved files! So do this after finalizing your datatypes.
The folowing IDE related tutorial contains a section about Generating a Distribution that is also useful if you do not use an IDE.
The following article shows you in general how to package and deploy your jME game: packaging_and_deploying_jme_applications
Tip: You can create a JAR launcher file (.bat, .sh) for your operating system that calls for example java -jar -Xmx256m -Djava.library.path=“lib/natives” MyGame.jar . Keep it in the same directory next to the libraries (in lib) and natives (in lib/natives). Execute the launcher to start the JAR.