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!
If you are a beginner, you should first read some articles about game development. We cannot cover all general tips here.
As a quick overview, answer yourself the following questions:
Use an issue and bug tracker to outline what features you want and what components are needed.
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.
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.
| DO | DON'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.
Every jME3 game is centered around one main class that (directly or indirectly) extends com.jme3.app.SimpleApplication.
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.
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?
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.
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 );
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.
initialize() and cleanup() methods when it is attached to or detached from the game. update() loop that hooks into the main simpleUpdate() loop. controlUpdate() loop that hooks into the main simpleUpdate() loop.
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.
Read all about Custom Controls and Application States here.
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.
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.
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.
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.
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.
| Distribution | Pros | Cons |
|---|---|---|
| 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.