Best Practices For jME3 Developers

Every milestone of a game development project is made up of phases: Planning, development, testing, and release. Every milestone involves updates to multi-media assets and to code.

This "best practices" page is a collection of recommendations and expert tips. Feel free to add your own!

Requirements and Planning

If you are a beginner, you should first read some articles about game development. We cannot cover all general tips here.

Requirements Gathering

As a quick overview, answer yourself the following questions:

  • Motivation
    • Sum up your game idea in one sentence. If you can't, it's too complicated.
    • Who's the target group? Why would they choose your game over the million others that exist?
  • Game type
    • Point of view (camera)? What character(s) does the player control? (if applicable)
    • Time- or turn-based?
    • Genre, setting, background story? (If applicable)
  • Gameplay
    • What is the start state, what is the end state?
    • What resources does the player manage? How are resources gained, transformed, spent?
      E.g. speed, gold, health, "points".
    • How does the player interact? Define rules, challenges, game mechanics.
    • What state is considered winning, and what losing, or is it an open world?
  • Multi-media assets
    • Which media will you need? How will you get this content?
      E.g. models, terrains; materials, textures; noises, music, voices; video, cutscenes; spoken/written dialog; level maps, quests, story; AI scripts.
  • Interface
    • Can you achieve a high degree of input control? Even minor navigation and interaction glitches make the game unsolvable.
    • Decide how to reflect current status, and changes in game states. E.g. health/damage.
    • Decide how to reward good moves and discourage bad ones.

Planning Development Milestones

Use an issue and bug tracker to outline what features you want and what components are needed.

  1. Pre-Alpha
    • Artwork: Test asset loading and saving with mock-ups and stock art.
    • Lay out the overall application flow, i.e. switching between intro / options / game screen, etc.
    • Get one typical level working. E.g. if the game is a "Jump'n'Run", jumping and running must work before you can announce Alpha.
  2. Alpha Release
    • Artwork: Replace all mock-ups with first drafts of real media and level maps.
    • Run alpha tests, track bugs, debug, optimize.
    • Declare Feature Freeze before you announce Beta. This prevents a bottomless pit of new bugs.
  3. Beta Release
    • Artwork: Fill in the final media and level maps.
    • Have external people review and "beta test" it (track bugs).
    • Even out the kinks in code and gameplay – don't add any more new features.
  4. Gamma, Delta = Release Candidates
    • Test the heck out of it. Last chance to find a horrible bug.
  5. Omega = Final Release

How you name or number these stages is fully up to your team. Development teams use numbered milestones (m1, m2, m3), Greek letters (e.g. alpha, beta, gamma, delta), "major.minor.patch-build" version numbering (e.g. "2.7.23-1328"), or combinations thereof.

Use File Version Control

Whether you work in a team or alone, keeping a version controlled repository of your code will help you roll-back buggy changes, or recover old code that someone deleted and that is now needed again.

  • Treat commit messages as messages to your future self. "Made some changes" is not a commit message.
  • The jMonkeyEngine SDK supports Subversion, Mercurial, and CVS.
    If you don't know which to choose, Subversion is a good choice for starters.
  • Set up your own local server, or get free remote hosting space from various open-source dev portals like Sourceforge, Github, bitbucket (supports private projects), Java.net, Google Code

Multi-Media Asset Pipeline

DODON'T
Save original models+textures into assets/Textures. Don't reference textures or models outside your JME project.
Save sounds into assets/Sounds. Don't reference audio files outside your JME project.
Create simple, low-polygon models. Don't create high-polygon models, they render too slow to be useful in games.
Only use Diffuse Map, Normal Map, Glow Map, Specular Map. Don't use unsupported material properties that are not listed in the Materials Overview.
Use UV texture / texture atlases / baking for each texture map. Don't create models based on multiple separate textures, it will break the model into separate meshes.
Convert Models to j3o format. Move j3o files into assets/Models. Don't reference Blender/Ogre/OBJ files in your load() code, because these unoptimized files are not packaged into the JAR.

Learn details about the Multi-Media Asset Pipeline here.

Development Phase

Many game developers dream of creating their very own "MMORPG with full-physics, AI, post-rendering effects, multi-player networking, procedurally generated maps, and customizable characters". So why aren't there tons of MMORPGs out there?
Even for large experienced game producers, the creation of such a complex game is time-intensive and failure-prone. How familiar are you with multi-threading, persistance, optimization, client-server synchonization, …? Unless your answer is "very!", then start with a single-player desktop game, and work your way up – just as the pros did when they started.

Extend SimpleApplication

Every jME3 game is centered around one main class that (directly or indirectly) extends com.jme3.app.SimpleApplication.

Note that although the "SimpleApplication" name might be misleading, all jME3 applications, including very large projects, are based on this class. The name only implies that this class is a simple application already. You make it "non-simple" by extending it! ;)

For your future game releases, you will want to rely on your own base framework: Your framework extends SimpleApplication, and includes your custom methods for loading, saving, and arranging your scenes, your custom navigation, your inputs for pausing and switching your custom screens, your custom user interface (options screen, HUD, etc), your custom NPC factory, your custom physics properties, your custom networking synchronization, etc. Reusing (extending) your own base framework saves you time. When you update your generic base classes, all your games that extend them benefit from the improvements. Also it will give your games a common look and feel.

Where to Start?

You have a list of features that you want in game, but which one do you implement first? You will keep adding features to a project that grows more and more complex, how can you minimize the amount of rewriting required?

  1. Make sure the game's high-level frame (screen switching, networking, loading/saving) is sound and solid.
  2. Start with implementing the most complex game feature first – the one that imposes most constraints on the structure of your project (for example: networking or physics.)
  3. Add only one larger feature at a time. If there are complex interactions (such as "networking + physics"), start with a small test case ("one cube") and work your way up, don't start with a whole scene.
  4. Implement low-complexity decorations (audio and visual effects) last.
  5. Test for side-effects on existing code after you add a feature.

Acknowledge whether you want a feature because it is necessary for gameplay, or simply because "everyone else has it". Your goal should be to bring out the essence of your game idea. Don't water down gameplay but attempting to make it "do everything, but better". Successful high-performance games are the ones where someone made smart decisions what to keep and what to drop.

The Smart Way to Add Custom Methods and Fields

Game entities (Nodes and Geometrys) often carry custom fields and custom methods with them. For example, players and NPCs have fields such as health, gold coins, inventory, equipment, etc. We call these fields user data. Game entities also have methods that modify user data. Examples include attack(), addGold(), getHealth(), pickUpItem(), dropItem(), useItem(), walk(), etc.

Avoid the Anti-Pattern: You might be tempted to create character classes using Java inheritance: For example, MyMobileNode extends Node and then MyNPC extends MyMobileNode. Then you might create subclasses such as MyFighterNPC extends MyNPC and MyShopKeeperNPC extends MyNPC. But what if you need a group of MyShopKeeperNPCs that fight back? Do you write a MyFightingShopKeeperNPC class, and copy and paste fighting methods from MyFighterNPC? The inheritance approach will soon turn out to be an unmaintainable mess – this is not what you want.

Follow the Best Practice: You can add user data, accessors, and methods directly to each Spatial.

  • Use ''setUserData()'' to add custom fields with user data to Spatials.
  • Use Controls to add custom methods (including getters and setters) to Spatials.

The Smart Way to Access Game Features

SimpleApplication gives you access to game features such as a the rootNode, assetManager, guiNode, inputManager, audioManager, physicsSpace, viewPort, and the camera. But what if you need access to the assetManager or rootNode etc from another class? Don't pass around lots of object references in constructors. This is a sign that this class should be designed as an AppState (read details there).

An AppState has access to all game features in the SimpleApplication via the this.app and this.stateManager objects.

Examples:

Spatial sky = SkyFactory.createSky(this.app.getAssetManager(), "sky.dds", false);
this.app.getRootNode().attachChild( sky );

The Smart Way to Implement Game Logic

As your SimpleApplication-based game grows more advanced, you find yourself putting more and more interactions in the simpleUpdate() loop, and your simpleInitApp() methods grows longer and longer. It's a best practice to move blocks of game mechanics into reusable component classes of their own. In jME3, these resuable classes are called Controls and AppStates.

  • Use AppStates to implement global game mechanics.
    • Each AppState calls its own initialize() and cleanup() methods when it is attached to or detached from the game.
    • Each AppState runs its own update() loop that hooks into the main simpleUpdate() loop.
    • You can specify what happens if an AppState is paused/unpaused.
    • You can use an AppState to switch between sets of AppStates.
    • An AppState has access to everything in the SimpleApplication (rootNode, AssetManager, StateManager, InputListener, ViewPort, etc).
  • Use Controls to implement the behaviour of game entities.
    • Controls add a type of behaviour (methods and fields) to an individual Spatial (a player, an NPC).
    • Each Control runs its own controlUpdate() loop that hooks into the main simpleUpdate() loop.
    • One Spatial can be influenced by several Controls. (!)
    • Each Spatial needs its own instance of the Control.
    • A Control only has control over and access to the spatial that it is attached to (and its sub-spatials).

Controls and AppStates often work together: An AppState can reach up to the application and get all Spatials from the rootNode that carry a specific Control, and perform a global action on them. Example: In BulletPhysics, all physical Spatials that carry RigidBodyControls are steered by the overall BulletAppState.

AppStates and Controls are extensions to a SimpleApplication. They are your modular building blocks to build a more complex game. In the ideal case, you move all init/update code into Controls and AppStates, and your simpleInitApp() and simpleUpdate() could end up virtually empty. This powerful and modular approach cleans up your code considerably.

Read all about Custom Controls and Application States here.

Optimize Application Performance

  • Optimization – how to avoid wasting cycles
  • Multithreading – use concurrency, and don't change the scene graph from outside the update loop.
  • You can add a Java Profiler to the jMonkeyEngine SDK via Tools → Plugins → Available. The profiler presents statistics on the lifecycle of methods and objects. Performance problems may be caused by just a few methods that take long, or are called too often (try to cache values to avoid this). If object creation and garbage collection counts keep increasing, you are looking at a memory leak.

Don't Mess With Geometric State

These tips are especially for users who already know jME2. Automatic handling of the Geometric State has improved in jME3, and it is now a best practice to not mess with it.

  • Do not call updateGeometricState() on anything but the root node!
  • Do not override or mess with updateGeometricState() at all.
  • Do not use getLocalTranslation().set() to move a spatial, always use setLocalTranslation().

Maintain Internal Documentation

It's unlikely you will fully document every class you write, we hear you. However, you should at least write meaningful javadoc to provide context for your most crucial methods/parameters.

  • What is this? How does it solve its task (input, algorithm used, output, side-effects)?
  • Write down limits (e.g. min/max values) and defaults while you still remember.
  • In which situation do I want to use this, is this part of a larger process? Is this step required, or what are the alternatives?

Treat javadoc as messages to your future self. "genNextVal() generates the next value" and "@param float factor A factor influencing the result" do not count as documentation.

Debugging and Test Phase

A Java Debugger is included in the jMonkeyEngine SDK. It allows you to set a break point in your code near the point where an exception happens. Then you step through the execution line by line and watch object and variable states to detect where the bug starts.

Use the Logger to print status messages during the development and debugging phase, instead of System.out.println(). The logger can be switched off with one line of code, whereas commenting out all your println()s takes a while.

Unit Testing (Java Assertions) has a different status in 3D graphics development than in other types of software. You cannot write assertions that automatically test whether the rendered image looks correct, or whether interactions are intuitive. Still you should create simple test cases for individual game features such as loaders, content generators, effects. Run the test cases now and then to see whether they still work as intended – or whether they are suffering from side-effects. Keep the test classes in a test directory in the project, don't include them in the distribution.

Quality Assurance (QA) means repeatedly checking a certain set of features that must work, but that might be unexpectedly broken as a side-effect. Every game has some crazy bugs somewhere – but basic tasks must work, no excuse. This includes installing and de-installing; saving and loading; changing options; starting, pausing, quitting; basic actions such as walking, fighting, etc. After every milestone, you go through your QA list again and systematically look for regressions or newly introduced bugs. Check the application on every supported operating system and hardware (!) because not all graphic cards support the same features. If you don't find the obvious bugs, your users will, and carelessness will put them off.

Alpha and Beta Testing means that you ask someone to try to install and run your game. It should be a real user situation, where they are left to figure the gameplay out by themselves (you only can include the usual read-me and help docs). Provide the testers with an easy method to report back problems, or why they gave up. Evaluate whether these problems are exceptions or must be fixed for the game to be playable.

Release Phase

Pre-Release To-Do List

  • Prepare a web page, advertise, etc
  • Verify that all assets are up-to-date and converted to .j3o.
  • Verify that your code loads the optimized .j3o files, and not the original model formats.
  • Prepare licenses of assets that you use for inclusion. (You did obtain permission to use them, right…?)
  • Switch off fine logging output.
  • Prepare promotional art: Cool screenshots (in thumbnail, square, vertical, horizontal, and fullscreen formats) and video clips. Include name, contact info, slogan, etc., so future customers can find you.
  • Prepare a readme.txt file, or installation guide, or handbook – if applicable.
  • Get a certificate if it is required for your distribution method (see below).
  • Specify a classification rating.

Distributing the Executables

The jMonkeyEngine SDK helps you with deployment: You specify your branding and deployment options in the Project Properties dialog, and then choose Clean and Build from the context menu. If you use another IDE, then consult the IDE's documentation.

Decide whether you want to release your game as WebStart, Desktop JAR, or Applet – Each has its pros and cons.

DistributionProsCons
Desktop Launcher
(.EXE, .app, .jar+.sh)
This is the standard way of distributing desktop applications. The jMonkeyEngine SDK can be configured to automatically create zipped launchers for each operating system. You need to offer three separate, platform-dependent downloads.
Desktop Application
(.JAR)
Platform independent desktop application. User must have Java configured to run JARs when they are opened; or user must know how to run JARs from command line; or you must provide a custom JAR wrapper.
Web Start
(.JNLP)
The user accesses a URL, saves the game as one executable file. Easy process, no installer required. You can allow the game to be played offline.Users need network connection to install the game. Downloading bigger games takes a while as opposed to running them from a CD.
Browser Applet
(.HTML+.JAR)
Easy to access and play game via most web browsers. Userfriendly solution for quick small games.Game only runs in the browser. Game or settings cannot be saved to disk. Some restrictions in default camera navigation (jME cannot capture mouse.)
Android
(.APK)
Game runs on Android devices.Android devices do not support post-procesor effects.

Which ever method you choose, a Java-Application works on the main operating systems: Windows, Mac OS, Linux, Android.

The distribution appears in a newly generated dist directory inside your project directory. These are the files that you upload or burn to CD to distribute to your customers.

 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution 3.0 Unported