From the perspective of someone trying to write a game it makes sense to expose the bits and pieces of the Jme3 engine (the assetManager, rootNode, guiNode, etc) that are needed for easy accesses in the same way System.out.println is easily accessible. I say so because then I can concentrate on structuring the game according to either its storyline, or its scenegraph, and no longer have to organize and run the lines of spagetti necessary allow classes that end up over yonder to access the bits and pieces they need. From the perspective of Jme3 developers, however, it might be best were I lined up against a wall and shot… I don’t know, but that is what I am asking… have I missed some information about the game engine that would see the following code lead nowhere, or to insurmountable troubles?
There are two classes. Jme is taken from SimpleApplication. It performs the same initialization as SimpleApplication. It is booted from within by main(). Once initialized it calls the init method of the second class called Game.
package mygame;
import com.jme3.app.Application;
import com.jme3.asset.AssetManager;
import com.jme3.input.InputManager;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial.CullHint;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeContext.Type;
/**
- The purpose of the class is to kickstart the Jme3 engine and expose
- bits and pieces such as assetManager, rootNode, etc as static fields
- that can be easily accesed throughout the game’s classes. The code is
- based on SimpleApplication…SimpleBulletApplication bits and pieces could be added
-
@author ste3e
*/
public class Jme extends Application {
private Game game=new Game();//your game starts in here
//static fields accessible via Jme.fieldName
public static Node rootNode = new Node(“Root Node”);
public static Node guiNode = new Node(“Gui Node”);
//will be initialized with super’s managers
public static AssetManager assetManager;
public static InputManager inputManager;
//universally available debug text
public static BitmapText sysout;
protected float secondCounter = 0.0f;
protected BitmapFont guiFont;
//enable esc to quit
private AppActionListener actionListener = new AppActionListener();
private class AppActionListener implements ActionListener {
public void onAction(String name, boolean value, float tpf) {
if (!value)
return;
if (name.equals(“SIMPLEAPP_Exit”)){
stop();
}
}
}
public Jme(){
super();
this.start();
}
//Main
public static void main(String[] args){
Jme jme=new Jme();
}
@Override
public void start(){
//hand the settings you want to super
settings=new AppSettings(true);
settings.put(“Width”, 1024);
settings.put(“Height”, 768);
settings.put(“Title”, “My Game”);
settings.put(“VSync”, true);
settings.put(“Samples”, 4);
super.setSettings(settings);
super.start();
}
@Override
public void initialize(){
super.initialize();
guiNode.setQueueBucket(Bucket.Gui);
guiNode.setCullHint(CullHint.Never);
viewPort.attachScene(rootNode);
guiViewPort.attachScene(guiNode);
//initialize managers
Jme.assetManager=super.getAssetManager();
Jme.inputManager=super.getInputManager();
if (Jme.inputManager != null){
if (context.getType() == Type.Display)
Jme.inputManager.addMapping(“SIMPLEAPP_Exit”, new KeyTrigger(KeyInput.KEY_ESCAPE));
Jme.inputManager.addListener(actionListener, “SIMPLEAPP_Exit”);
}
guiFont = assetManager.loadFont(“Interface/Fonts/Default.fnt”);
sysout = new BitmapText(guiFont, false);
sysout.setSize(guiFont.getCharSet().getRenderedSize());
sysout.setLocalTranslation(0, sysout.getLineHeight(), 0);
sysout.setText(“Frames per second”);
guiNode.attachChild(sysout);
Jme.debug(“This”);//won’t show as debug is called later in game
//Jme3 is all initialized so we can start the game
game.init();
}
@Override
public void update() {
if (speed == 0 || paused)
return;
super.update();
float tpf = timer.getTimePerFrame() * speed;
secondCounter += timer.getTimePerFrame();
int fps = (int) this.timer.getFrameRate();
if (secondCounter >= 1.0f){
//sysout.setText("Frames per second: "+secondCounter);
secondCounter = 0.0f;
}
// update states
stateManager.update(tpf);
// simple update and root node
rootNode.updateLogicalState(tpf);
guiNode.updateLogicalState(tpf);
rootNode.updateGeometricState();
guiNode.updateGeometricState();
// render states
stateManager.render(renderManager);
renderManager.render(tpf);
//simpleRender(renderManager);
}
public static void debug(String str){
sysout.setText(str);
}
}
Game illustrates how the static fields are accessed.
package mygame;
import com.jme3.light.DirectionalLight;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.ActionListener;
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.animation.LoopMode;
/** - This is where the game begins. Note there are no references to a Jme
object other than the static calls to Jme.fieldName
*@author ste3e
*/
public class Game implements AnimEventListener{
private Node root, placeholder, player;
private AnimChannel channel;
private AnimControl control;
private boolean play=false;//play or stop the animation
public Game(){}
//init is called from Jme after the Jme initializes the game engine.
public void init() {
//the node “root” reorients the world space to coincide with Blender’s
reorient();
placeholder=new Node(“placeholder”);
root.attachChild(placeholder);
//use static access to the assetManager
player=(Node)Jme.assetManager.loadModel(“Models/ogre/boxMesh.mesh.j3o”);
placeholder.attachChild(player);
initKeys();
//setup animation and set to Start animation as named in Ogre exporter
control=player.getControl(AnimControl.class);
control.addListener(this);
channel=control.createChannel();
channel.setAnim(“Start”);
DirectionalLight sun=new DirectionalLight();
sun.setDirection(new Vector3f(-0.1f,-0.7f,-1.0f));
//static access to the rootNode
Jme.rootNode.addLight(sun);
//static access to debug method
Jme.debug(“Fred is nigh”);
}
private void initKeys(){
//static access to the inputManager
Jme.inputManager.addMapping(“ccw”, new KeyTrigger(KeyInput.KEY_LEFT));
Jme.inputManager.addMapping(“cw”, new KeyTrigger(KeyInput.KEY_RIGHT));
Jme.inputManager.addMapping(“play”, new KeyTrigger(KeyInput.KEY_P));
Jme.inputManager.addListener(actionListener, “ccw”);
Jme.inputManager.addListener(actionListener, “cw”);
Jme.inputManager.addListener(actionListener, “play”);
}
private ActionListener actionListener=new ActionListener(){
public void onAction(String name, boolean keyPressed, float tpf){
if(name.equals(“ccw”)){
placeholder.rotate(0.0f,0.0f,-0.05f);
}
if(name.equals(“cw”)){
placeholder.rotate(0.0f,0.0f,0.05f);
}
if(name.equals(“play”) && !keyPressed){
if(!play){
channel.setAnim(“Action”, 0.5f);
channel.setLoopMode(LoopMode.Loop);
play=true;
}else{
channel.setAnim(“Start”, 0.5f);
play=false;
}
}
}
};
//need to implement listener
public void onAnimCycleDone(AnimControl control, AnimChannel channel, String name){}
public void onAnimChange(AnimControl control, AnimChannel channel, String name){}
private void reorient(){
root=new Node(“root”);
root.rotate((float)Math.toRadians(90.0f),(float)Math.toRadians(270.0f),0.0f);
root.setLocalTranslation(new Vector3f(0.0f,-2.5f,0.0f));
Jme.rootNode.attachChild(root);
}
}