What is the best way to share a specific field in JME?

Hi!

I’m trying to learn code Java in a more structural way. Normaly I have only code Java in form of classes in the same folder and not using java packages at all. But that will change from now.

So I have decided to rotate a cube so I split up the listener for keyboard in one class and model design in another class and of course Main class is the start up class.

The main class attach the two classes Models and KeyBoard. My question is simple. What is the best way to let KeyBoard.java class use the geometry geom from Models.java?

Notice that every class in each separate package.

public class Main extends SimpleApplication {

    public static void main(String[] args) {
        Main app = new Main();
        app.start();
    }

    @Override
    public void simpleInitApp() {
       stateManager.attach(new Models(this));
       stateManager.attach(new KeyBoard(this));
    }

}

KeyBoard class

public class KeyBoard extends AbstractAppState {

private final Node rootNode;
private final Node localRootNode;
private final InputManager inputManager;

public KeyBoard(SimpleApplication app){
    rootNode = app.getRootNode();
    inputManager = app.getInputManager();
    localRootNode = new Node();
    rootNode.attachChild(localRootNode);
}

@Override
public void initialize(AppStateManager stateManager, Application app){
    // You can map one or several inputs to one named action
    inputManager.addMapping("Pause",  new KeyTrigger(KeyInput.KEY_P));
    inputManager.addMapping("Left",   new KeyTrigger(KeyInput.KEY_J));
    inputManager.addMapping("Right",  new KeyTrigger(KeyInput.KEY_K));
    inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE));
    
    // Add the names to the action listener.
    inputManager.addListener(actionListener, "Pause");
    inputManager.addListener(analogListener, "Left", "Right", "Rotate");
}

private final ActionListener actionListener = new ActionListener() {
    @Override
    public void onAction(String name, boolean keyPressed, float tpf) {

    }
};

private final AnalogListener analogListener = new AnalogListener() {
    @Override
    public void onAnalog(String name, float value, float tpf) {
        
    }
};

}

And Models class

public class Models extends AbstractAppState  {

private final Node rootNode;
private final AssetManager assetManager;
private final Node localRootNode;

// Model
private static Geometry geom;


public Models(SimpleApplication app){
    rootNode = app.getRootNode();
    assetManager = app.getAssetManager();
    localRootNode = new Node();
    rootNode.attachChild(localRootNode);
}

@Override
public void initialize(AppStateManager stateManager, Application app) {
    
    Box b = new Box(1, 1, 1);
    geom = new Geometry("Box", b);

    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mat.setColor("Color", ColorRGBA.Blue);
    geom.setMaterial(mat);
    
    localRootNode.attachChild(geom);
}

@Override
public void cleanup(){
}

}

Use a mediator class that incapsulates Keyboard and Models and let them communicate their shared fields, even main may do that passing them to constructors.

1 Like

Isin’t there an easier way to do that? Mediator could be very complexe.

As i said if you application is really simple you can do that in your Main, if you plan to extend it more not using a specific mediating class could result in future troubles. Think about using an InGame app state which incapsulates Models and Keyboard :slight_smile:

So you really recommend to learn to create Mediators? Is that a new thing in Java? If the answer is yes, then I have to learn it.

By the way. I set a field to public static in a class and then import it to another class. Look at the KeyBoard class. This didn’t work. Null pointer exception.

public class KeyBoard extends AbstractAppState {

private final Node rootNode;
private final Node localRootNode;
private final InputManager inputManager;


public static Geometry geom;

public KeyBoard(SimpleApplication app){
    rootNode = app.getRootNode();
    inputManager = app.getInputManager();
    localRootNode = new Node();
    rootNode.attachChild(localRootNode);
    geom = Models.geom; // <-------- geom is static in Models.java.
}

@Override
public void initialize(AppStateManager stateManager, Application app){
    // You can map one or several inputs to one named action
    inputManager.addMapping("Pause",  new KeyTrigger(KeyInput.KEY_P));
    inputManager.addMapping("Left",   new KeyTrigger(KeyInput.KEY_J));
    inputManager.addMapping("Right",  new KeyTrigger(KeyInput.KEY_K));
    inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE));
    
    // Add the names to the action listener.
    inputManager.addListener(actionListener, "Pause");
    inputManager.addListener(analogListener, "Left", "Right", "Rotate");
}

private final ActionListener actionListener = new ActionListener() {
    @Override
    public void onAction(String name, boolean keyPressed, float tpf) {
        if(name.equals("Left") && keyPressed){
            geom.rotate(0,0,0.1f);
        }
    }
};

private final AnalogListener analogListener = new AnalogListener() {
    @Override
    public void onAnalog(String name, float value, float tpf) {
        
    }
};

}

Don’t use the static keyword.

In your model appstate write an accessor for the geometry you create (a get method).

In your keyboard state use getState(ModelState.class).getMyAccessor();

You access other states using the getState(AppState) method from within other states.

Also, most times I extend BaseAppState and not AbstractAppState.

In this case, order of addition to the stateManager is important.
You will want to add the modelstate first, so the constructor and initialise parts of an appstate are called first, then the keyboard state will have something to use when it calls it.

1 Like

NEVER do that in Java. Non-final attributes are always private/protected, then create a getter and a setter method to get its value or modify it.

You should study some OOP before digging into videogames development, you must be confident with java basic programming to understand jME’s data structures.

What do you mean with “model appstate”? Is that a code word for “Models.java”? I assume that you mean something like this?

private Geometry geom;

public Geometry getGeometry(){
         return geom;
 }

Then in my KeyBoard.java file, I will write.

import GameDesign.Models;
// Add geometry
geom = (Models.class).getGeometry();

But that will give my an error because the method is not avaiable.

getState(ModelState.class).getGeometry();

Sorry yes models.class. Usually a state is named xxxState.class for brevity.

Yes. I know, but I want to learn Java through a way to develop games.

getState is not avaiable here.

Extend BaseAppState and not AbstractAppState

Not it’s working. But it seems that the geometry is null. Due to the

public class KeyBoard extends BaseAppState  {

    private final Node rootNode;
    private final Node localRootNode;
    private final InputManager inputManager;
    
   //private Geometry geom;
    
    public KeyBoard(SimpleApplication app){
        rootNode = app.getRootNode();
        inputManager = app.getInputManager();
        localRootNode = new Node();
        rootNode.attachChild(localRootNode);
    }
    
    @Override
    public void initialize(Application app){
        // You can map one or several inputs to one named action
        inputManager.addMapping("Pause",  new KeyTrigger(KeyInput.KEY_P));
        inputManager.addMapping("Left",   new KeyTrigger(KeyInput.KEY_J));
        inputManager.addMapping("Right",  new KeyTrigger(KeyInput.KEY_K));
        inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE));

And

public class Main extends SimpleApplication {

    public static void main(String[] args) {
        Main app = new Main();
        app.start();
    }

    @Override
    public void simpleInitApp() {
       stateManager.attach(new Models(this));
       stateManager.attach(new KeyBoard(this));
    }

}

In your models class you will need to set the geometry object to something.

But I already have done that. Now everythings seems to confuse me.

Show the full models code. The geometry object that your accessor refers to should not be null.

Well, here it is.

Main.java

package Start;

import GameDesign.Models;
import GameLogic.KeyBoard;
import com.jme3.app.SimpleApplication;

/**
 * This is the Main Class of your Game. You should only do initialization here.
 * Move your Logic into AppStates or Controls
 * @author normenhansen
 */
public class Main extends SimpleApplication {

    public static void main(String[] args) {
        Main app = new Main();
        app.start();
    }

    @Override
    public void simpleInitApp() {
       stateManager.attach(new Models(this));
       stateManager.attach(new KeyBoard());
    }

}

Models.java

package GameDesign;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;

/**
 *
 * @author spektrakon
 */
public class Models extends AbstractAppState  {
    
    private final Node rootNode;
    private final AssetManager assetManager;
    private final Node localRootNode;
    
    private Geometry geom;
    
    public Models(SimpleApplication app){
        rootNode = app.getRootNode();
        assetManager = app.getAssetManager();
        localRootNode = new Node();
        rootNode.attachChild(localRootNode);
    }

    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        
        Box b = new Box(1, 1, 1);
        geom = new Geometry("Box", b);

        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Blue);
        geom.setMaterial(mat);
        
        localRootNode.attachChild(geom);
    }
    
    @Override
    public void cleanup(){
    }
    
    public Geometry getGeometry(){
        return geom;
    }
    
    
}

KeyBoard.java

package GameLogic;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import GameDesign.Models;
import com.jme3.app.state.BaseAppState;

/**
 *
 * @author spektrakon
 */
public class KeyBoard extends BaseAppState  {

    private Node rootNode;
    private Node localRootNode;
    private InputManager inputManager;
   
   private Geometry geom;

    
    @Override
    public void initialize(Application app){
        // You can map one or several inputs to one named action
        inputManager.addMapping("Pause",  new KeyTrigger(KeyInput.KEY_P));
        inputManager.addMapping("Left",   new KeyTrigger(KeyInput.KEY_J));
        inputManager.addMapping("Right",  new KeyTrigger(KeyInput.KEY_K));
        inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE));
        
        // Add the names to the action listener.
        inputManager.addListener(actionListener, "Pause");
        inputManager.addListener(analogListener, "Left", "Right", "Rotate");
        
        // Add geometry
        geom = getState(Models.class).getGeometry();
    }
    
    private final ActionListener actionListener = new ActionListener() {
        @Override
        public void onAction(String name, boolean keyPressed, float tpf) {
            
        }
    };

    private final AnalogListener analogListener = new AnalogListener() {
        @Override
        public void onAnalog(String name, float value, float tpf) {
            if(name.equals("Left")){
                System.out.println("dsfsd");
                geom.rotate(0,0,0.1f);

            }
        }
    };

    @Override
    protected void cleanup(Application app) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    protected void onEnable() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    protected void onDisable() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
    
}

You need to remove the unsupportedoperationexception lines.

You should also extend BaseAppState on both classes. It makes life (and calling super on everything) simpler.