Extend com.jme3.app.SimpleApplication.
Learn more: Hello SimpleApplication, com.jme3.app.SimpleApplication.
viewPort.setBackgroundColor(ColorRGBA.Blue);
Yes! For your own games, you create a custom base class that extends com.jme3.app.SimpleApplication class, so it's no longer a "simple" application. Configure your application settings, and customize away.
Learn more: SimpleApplication, AppSettings.
You should break app your application logic into components by spreading it out over individual AppStates. AppStates can be attached to and detached from the game. AppStates have access to all objects (rootNode, PhysicsSpace, inputManager, etc) and methods in your main application. So each AppState can bring its own subset of input handlers, GUI nodes, spatial nodes, and even its own subset of game mechanics in the update() loop.
Learn more: Application States.
You split up your application into several AppStates and implement the setEnabled() methods for each state. Then you create, for example, a GameRunningAppState and a GamePausedAppState. GamePausedAppState's job is to attach all your AppStates that contain the logic and GUI of the pause screen, and to detach all the AppStates that contain logic and GUI of the running game. GameRunningAppState does the opposite. By attaching one or the other to the game, you switch between the paused and unpaused states.
Learn more: Application States.
During development, you can switch the severity level of the default logger to no longer print FINE warnings, but only WARNINGs.
java.util.logging.Logger.getLogger("").setLevel(Level.WARNING);
For the release, switch the severity level of the default logger to print only SEVERE errors.
java.util.logging.Logger.getLogger("").setLevel(Level.SEVERE);
Learn more: Logging.
Make sure to only load() models converted to .j3o binary format, not the original Ogre or Wavefront formats. If you load assets from zip files, make sure to ammend the build script to copy them ito the build.
Learn more: Asset Manager
To make a spatial appear in the scene, you attach it to the rootNode, To remove a spatial, you detach it.
rootNode.attachChild(spatial); // appear
rootNode.detachChild(spatial); // remove
Optionally, you can control whether the engine culls an object always or never.
spatial.setCullHint(CullHint.Never); // always drawn
spatial.setCullHint(CullHint.Always); // never drawn
Learn more: The Scene Graph, Hello Node, Hello Asset, Spatial, com.jme3.scene.Node and com.jme3.scene.Geometry.
First check whether the file path of the asset is correct. By default it is relative to your project's assets directory:
// To load .../jMonkeyProjects/MyGame/assets/Models/Ninja/Ninja.j3o Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.j3o");
If you are not using the default assets directory, verify that you have registered a locator to the AssetManager. Different Locator types are available.
this.assetManager.registerLocator("assets/", FileLocator.class); // default this.assetManager.registerLocator("c:/jme3User/JMEisSoCool/myAwesomeFolder/", FileLocator.class); this.assetManager.registerLocator("town.zip", ZipLocator.class);
Learn more: Asset Manager
Follow our best practices for the multi-media asset pipeline.
You create 3-D models in a 3-D mesh editor, for example Blender, and export it in Ogre Mesh XML or Wavefront OBJ format.
You create textures in a graphic editor, for exmaple Gimp, and export them as PNG or JPG.
You create sounds in an audio editor, for example, Audacity, and export them as WAVE or OGG.
Learn more: 3D Models, Download Blender, Blender tutorial, Blender-to-Ogre plugin, Multi-Media Asset Pipeline.
Use the jMonkeyEngine SDK to convert models from Ogre XML or Wavefront OBJ formats to .j3o binary format. Load the .j3o file using the AssetManager.
// To load .../jMonkeyProjects/MyGame/assets/Models/Ninja/Ninja.j3o Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.j3o");
Learn more: Hello Asset, Asset Manager, come.jme3.assets.AssetManager, com.jme3.scene.plugins.ogre], [[http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/core/com/jme3/scene/Geometry.java|com.jme3.scene.Geometry, jMonkeyEngine SDK j3o converter,
Code sample: TestOgreLoading.java, TestOgreConvert.java.
Use the simpleInitApp() method in SimpleApplication (or initApp() in Application).
Learn more: Hello SimpleApplication, com.jme3.app.SimpleApplication.
To move or turn or resize a spatial you use transformations. You can concatenate transformations (e.g. perform rotations around several axes in one step using a Quaternion with slerp() or a com.jme3.math.Transform with interpolateTransforms().
spatial.setLocalTranslation(1,-3,2.5f); spatial.rotate(0,3.14f,0); spatial.scale(2,2,2);
Learn more: Hello Node, Spatial, rotate, rotate_about_a_point, quaternion, math_for_dummies.
Change the geometry's translation (position) live in the update loop using setLocalTranslation() for non-physical and applyForce() or setWalkDirection() for physical objects. You can also define and remote-control a spatial's motion using Cinematics, e.g. to record cutscenes, or to implement mobile platforms, elevators, airships, etc.
Learn more: Hello Loop, Update Loop, Custom Controls, Cinematics
Code sample: TestBumpModel.java, TestOgreLoading.java
Geometry result = spatial.getName().startsWith(name);
Learn more: Spatial
You can programmatically create com.jme3.scene.Mesh'es.
Learn more: Custom Meshes
The most likely reason is the flipping of textures. You may be using the following default method:
material.setTexture("ColorMap", assetManager.loadTexture("myTexture.jpg"));
You can set the boolean value in the constructor of TextureKey to flipped or not flipped. Toggle the boolean to see if it fixes your UV wrapping/texture problem:
material.setTexture("ColorMap", this.assetManager.loadTexture(new TextureKey("myTexture.jpg", false)));
You cannot scale a texture, but you scale the texture coordinates of the mesh the texture is applied to:
mesh.scaleTextureCoordinates(new Vector2f(2,2));
You can choose among various com.jme3.texture.Texture.WrapModes for individual texture maps of a material: BorderClamp, EdgeClamp, Clamp; MirrorBorderClamp, MirrorEdgeClamp, MirrorClamp; Repeat, MirroredRepeat.
material.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat);
Use the AssetManager to load Materials, and change material settings.
Learn more: Hello Material, How To Use Materials, Materials Overview, Asset Manager.
Code sample: TestNormalMapping.java, TestSphere.java.
Create Textures as image files. Use the AssetManager to load a Material and use texture mapping for improved looks.
Learn more: Hello Material, How To Use Materials, Materials Overview, Asset Manager, come.jme3.assets.AssetManager, Blender: Creating Bump Maps and Normal Maps
Code sample: TestSimpleBumps.java
If you use a lit material (based on Lighting.j3md) then you must attach a light source to the rootNode, otherwise you see nothing. If you use lit material colors, make sure you have specified an Ambient color (can be the same as the Diffuse color) if you use an AmbientLight. If you see objects, but they are gray or too dark, set the light color to white, or make it brighter (you can multiply the color value with a scalar), or add a global white light source (AmbientLight). Similarly, if everything is too white, tune down the lights. If materials flicker under a directional light, change the light direction vector. Change the background color (which is independent of light sources) to get a better contrast while debugging a light problem.
Use com.jme3.shadow.BasicShadowRenderer together with com.jme3.light.DirectionalLight, and setShadowMode().
Learn more: Light and Shadow
Code sample: TestEverything.java, TestShadow.java
Assign a texture with an alpha channel to a Material and set the Material's blend mode to alpha. Use this to create transparent or translucent materials such as glass, window panes, water, tree leaves, etc.
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
Learn more: Hello Material, How To Use Materials,
You can switch the com.jme3.material.RenderState.FaceCullMode to Back, Front, FrontAndBack, or Off. This influences whether the front or backside of an object is being drawn. By default, backsides are culled (not drawn) because they are usually not visible anyway.
material.getAdditionalRenderState().setFaceCullMode(FaceCullMode.FrontAndBack);
Create a material and switch its renders state to wireframe.
material.getAdditionalRenderState().setWireframe(true);
Learn more: Debugging.
The default camera is the cam object. Learn more: com.jme3.renderer.Camera
flyCam.setEnabled(true);
flyCam.setEnabled(false); chaseCam = new ChaseCamera(cam, spatial, inputManager);
flyCam.setMoveSpeed(50f);
Use Controls to define the behaviour of types of Spatials. Use Application States to implement global behaviour, to group subsets of input handlers or GUI screens, etc. Use the simpleUpdate() and update() loops for tests and interactions. Use Cinematics to remote-control objects in scenes.
Learn more: Hello Loop, Update Loop, Custom Controls, Application States, Cinematics
Use com.jme3.input.KeyInput and a Input Listener.
Learn more: Hello Input, Input Handling
Players typically click the mouse to pick up objects, to open doors, to shoot a weapon, etc. Use an Input Listener to respond to mouse clicks, then cast a ray from the player; if it intersects with the bounding volume of a spatial, this is the selected target. The links below contain code samples for both "fixed crosshair" picking and "free mouse pointer" picking.
Learn more: Hello Picking, Mouse Picking, Collision and Intersection, Input Handling, com.jme3.bounding.*, com.jme3.math.Ray, com.jme3.collision.CollisionResults.
Code sample: TestRayCollision.java
Create an animated OgreMesh model with bones in a 3-D mesh editor (e.g. Blender).
Learn more: com.jme3.animation.*, Hello Animation, Animation, Blender animation tutorial
Code sample: animation
Use collision detection. The most common solution is to use jme's physics integration, jBullet.
Learn more: Hello Collision, Physics, com.jme3.bullet.*, CapsuleCollisionShape versus CompoundCollisionShape, CharacterControl versus RigidBodyControl.
Add physics controls to Spatials and give them spherical or cylindrical bounding volumes.
Learn more: Hello Physics, Physics, com.jme3.bounding.*, com.jme3.bullet.collisions, com.jme3.bullet.controls.RigidBodyControl,
Code sample: TestSimplePhysics.java, more physics samples
Maybe your collision shapes overlap – or they are not where you think they are. Make the collision shapes visible by adding the following line after the bulletAppState initialization:
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
You can use jBullet's CharacterControl that locks a physical object upright, so it does not tip over when moving/walking (as tall physical objects are wont to do).
Learn more: CharacterControl
Code samples: TestQ3.java (first-person), TestPhysicsCharacter.java (third-person)
Use a VehicleControl that supports suspension behavior.
Learn more: Vehicles, com.jme3.bullet.*, VehicleControl
Code samples: TestFancyCar.java, (Press HUJK keys to steer, spacebar to jump.)
Use a PhysicsControl's hinges and joints.
Learn more: Hinges and Joints, com.jme3.bullet.joints.PhysicsHingeJoint,
TestPhysicsHingeJoint.java (Press HK keys to turn, spacebar to swing.)
At the bottom left of every default SimpleGame, you see the StatsView and the FPS (frames per seconds) view. These views provide you with extra information during the development phase. For example, if you notice the object count is increasing and the FPS is decreasing, then you know that your code attaches too many objects and does not detach enough of them again (maybe a loop gone wild?).
Learn more: StatsView
In the application's simpleInitApp() method, call:
setDisplayFps(false); // to hide the FPS setDisplayStatView(false); // to hide the statistics
Learn more: StatsView
Attach text and pictures to the orthogonal guiNode to create a heads-up display (HUD).
Learn more: HUD, com.jme3.font.*, com.jme3.ui.Picture, guiNode.attachChild()
Code sample: TestOrtho.java, TestBitmapFont.java |
You may want to display buttons to let the player switch between the game, settings screen, and score screens. For buttons and other more advanced UI controls, jME supports the Nifty GUI library.
Learn more: Nifty GUI
Sample Code: TestNiftyGui.java
Instead of having a frozen frame while your games loads, you can have a loading screen while it loads.
Learn more: Loading screen
Verify that you include a controls definition file link in your XML: This is the default:
<useControls filename="nifty-default-controls.xml"/>
Use AudioRenderer, Listener, and AudioNode from com.jme3.audio.*.
Learn more: Hello Audio, Audio
Code sample: audio
For swarm like effects you use particle emitters.
Learn more: Hello Effects, Particle Emitters, Bloom and Glow, Effects Overview, com.jme3.effect.EmitterSphereShape, com.jme3.effect.ParticleEmitter
Code sample: TestExplosionEffect.java, TestParticleEmitter.java
Use a special post-processor renderer from com.jme3.water.*.
Learn more: Water, Post-Processor Water
Code sample: TestSimpleWater.java, TestSceneWater.java, TestPostWaterLake.java, TestPostWater.java
Use special post-processor renderers from com.jme3.post.*.
Learn more: effects_overview
Use com.jme3.terrain.*. The JMonkeyEngine also provides you with a Terrain Editor plugin.
Learn more: Hello Terrain, Terrain, Terrain Editor
Code sample: TestTerrain.java
Code sample: TestCubeMap.java
rootNode.attachChild(SkyFactory.createSky( assetManager, "Textures/Sky/Bright/BrightSky.dds", false)); skyGeo.setQueueBucket(Bucket.Sky)
Learn more: Sky
If your game is heavily using features that older cards do not support, you can add a check of the JME3 Renderer Caps.
Collection<com.jme3.renderer.Caps> caps = renderer.getCaps(); Logger.getLogger(HelloJME3.class.getName()).log(Level.INFO, "Capabilities: {0}" + caps.toString());
The following shortened example shows the capabilities of an older graphic card. In this case you decide whether to branch to a low-quality rendering of the unsupported features (if you still want to support this card), or print an error message explaining the user what capabilities the card is missing to play the game.
Here is an example of the capabilities of an older graphic card:
INFO: Running on jMonkey Engine 3 INFO: Using LWJGL 2.7.1 INFO: Selected display mode: 1024 x 768 x 0 @0Hz INFO: Adapter: null INFO: Driver Version: null INFO: Vendor: ATI Technologies Inc. INFO: OpenGL Version: 2.0 ATI-1.6.36 INFO: Renderer: ATI Radeon X1600 OpenGL Engine INFO: GLSL Ver: 1.20 INFO: Timer resolution: 1.000 ticks per second INFO: Capabilities: [FrameBuffer, FrameBufferMRT, FrameBufferMultisample, OpenGL20, ARBprogram, GLSL100, GLSL110, GLSL120, VertexTextureFetch, FloatTexture, TextureCompressionLATC, NonPowerOfTwoTextures]
A newer graphic card has better capabilities, for example:
INFO: Running on jMonkeyEngine 3.0.0 INFO: Using LWJGL 2.8.2 INFO: Selected display mode: 1280 x 720 x 0 @0Hz INFO: Adapter: null INFO: Driver Version: null INFO: Vendor: ATI Technologies Inc. INFO: OpenGL Version: 2.1 ATI-7.14.5 INFO: Renderer: AMD Radeon HD 6770M OpenGL Engine INFO: GLSL Ver: 1.20 INFO: Timer resolution: 1.000 ticks per second INFO: Capabilities: [FrameBuffer, FrameBufferMRT, FrameBufferMultisample, OpenGL20, OpenGL21, ARBprogram, GLSL100, GLSL110, GLSL120, VertexTextureFetch, TextureArray, FloatTexture, FloatColorBuffer, FloatDepthBuffer, PackedFloatTexture, SharedExponentTexture, PackedFloatColorBuffer, TextureCompressionLATC, NonPowerOfTwoTextures, MeshInstancing]
You can batch all Geometries in a scene (or a subnode) that remains static.
jme3tools.optimize.GeometryBatchFactory.optimize(rootNode);
Batching means that all Geometries with the same Material are combined into one mesh. This optimization only has an effect if you use only few (roughly up to 32) Materials total. The pay-off is that batching takes extra time when the game is initialized.
Add an obfuscator to the Ant script. The SDK comes with a basic obfuscation script that you can enable in the project settings.
Many maths functions (mult(), add(), subtract(), etc) come as local and a non-local variant (multLocal(), addLocal(), subtractLocal(), etc).
Quaternion q1 = q2.mult(q3);v.mult(b).add(b); q2.multLocal(q3)v.multLocal(a).addLocal(b);World coordinates of a Spatial are its absolute coordinates in the 3D scene (this is like giving GPS coordinates). Local coordinates are relative to the Spatial's parent Spatial (this is like saying, "I'm ten meters left of the entrance").
Multiply degree value by FastMath.DEG_TO_RAD to convert it to radians.