Sep 4, 2014

44. Physics


Using AppState and controls, we can use the built-in Physics Engine in jMonkeyEngine.




The floor is a Box mesh of 25 square meter dimensions.


        // *** 1. Start (Create Floor)
        Box b = new Box(5,1,5);
        Geometry floor = new Geometry("floor", b);
        Material mat = new Material(assetManager, 
                "Common/MatDefs/Misc/ShowNormals.j3md");
        floor.setMaterial(mat);
        rootNode.attachChild(floor);
        // *** 1. End



We have to start the BulletAppState to use Bullet physics.


        // *** 2. Start (Physics - 3rd line debug)
        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);
        bulletAppState.getPhysicsSpace().enableDebug(assetManager);
        // *** 2. End



We set a RigidBodyControl object to the floor, so it is immovable.


        // *** 3. Start (floor is rigid)
        scenePhy = new RigidBodyControl(0f);
        floor.addControl(scenePhy);
        bulletAppState.getPhysicsSpace().add(floor);
        // *** 3. End



The player is a BetterCharacterControl object to use the Physics.


        // *** 4. Start (invisible player)
        playerNode = new Node("the player");
        playerNode.setLocalTranslation(
                new Vector3f(0, 1, 0));
        rootNode.attachChild(playerNode);
        playerControl = new BetterCharacterControl(
                1.5f, 4, 30f);
        playerControl.setJumpForce(new Vector3f(0, 300, 0));
        playerControl.setGravity(new Vector3f(0, -10, 0));
        playerNode.addControl(playerControl);
        bulletAppState.getPhysicsSpace().add(playerControl);
        // *** 4. End



A camera is created, fixed to our player.


        // *** 5. Start (Camera attached to Player)
        camNode = new CameraNode("CamNode", cam);
        camNode.setControlDir(CameraControl.
                              ControlDirection.SpatialToCamera);
        camNode.setLocalTranslation(new Vector3f(0, 4, -6));
        Quaternion quat = new Quaternion();
        quat.lookAt(Vector3f.UNIT_Z, Vector3f.UNIT_Y);
        camNode.setLocalRotation(quat);
        playerNode.attachChild(camNode);
        camNode.setEnabled(true);
        flyCam.setEnabled(false);
        // *** 5. End



The four arrow keys are registered.


        // *** 6. Start (Arrow Keys)
        inputManager.addMapping("Forward",
                new KeyTrigger(KeyInput.KEY_UP));
        inputManager.addMapping("Back",
                new KeyTrigger(KeyInput.KEY_DOWN));
        inputManager.addMapping("Left",
                new KeyTrigger(KeyInput.KEY_LEFT));
        inputManager.addMapping("Right",
                new KeyTrigger(KeyInput.KEY_RIGHT));
        inputManager.addListener(this, "Left", 
                "Right","Forward", "Back");
        // *** 6. End



In simpleUpdate(), the booleans are checked to update player position, and the attached camera.


    // *** 7. Start (Walk character)
    @Override
    public void simpleUpdate(float tpf) {
        Vector3f ForwardDir = Vector3f.UNIT_Z;
        Vector3f LeftDir = Vector3f.UNIT_X;
        walkDirection.set(0, 0, 0); 
        if (forward) {
            walkDirection = walkDirection.add(ForwardDir);
        } else if (backward) {
            walkDirection = walkDirection.subtract(ForwardDir);
        }
        if (Left) {
            walkDirection = walkDirection.add(LeftDir);
        } else if (Right) {
            walkDirection = walkDirection.subtract(LeftDir);
        }
        playerControl.setWalkDirection(walkDirection);
        vy = playerControl.getVelocity().y;
        if (vy<-10f) {
            System.out.println("Game over");
        }
    }
    // *** 7. End



When an action, arrow key press or release, occurs, the booleans will be set true or false.


    // *** 8. Start (Which key pressed)
    public void onAction(String binding, boolean isPressed, float tpf) {
        if (binding.equals("Left")) {
            Left = isPressed;
        }
        else if (binding.equals("Right")) {
            Right = isPressed;
        }
        else if (binding.equals("Forward")) {
            forward = isPressed;
        }
        else if (binding.equals("Back")) {
            backward = isPressed;
        }
    }
    // *** 8. End



// JMonkey44.java

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.control.CameraControl;
import com.jme3.scene.shape.Box;

public class JMonkey44 extends SimpleApplication
        implements ActionListener  {
    public static void main(String[] args) {
        JMonkey44 app = new JMonkey44();
        app.start();
    }
    
    private BulletAppState bulletAppState; // physics
    private RigidBodyControl scenePhy; // rigid floor
    private Node playerNode;
    private BetterCharacterControl playerControl; // player
    private CameraNode camNode; // physics camera
    // Walking vars
    private Vector3f walkDirection = new Vector3f(0,0,0);
    private boolean Left = false, Right = false,
                    forward = false,    backward = false;
    private float vy; // Player downwad velocity    
    @Override
    public void simpleInitApp() {
        viewPort.setBackgroundColor(ColorRGBA.White);
        setDisplayFps(false);
        setDisplayStatView(false);

        // *** 1. Start (Create Floor)
        Box b = new Box(5,1,5);
        Geometry floor = new Geometry("floor", b);
        Material mat = new Material(assetManager, 
                "Common/MatDefs/Misc/ShowNormals.j3md");
        floor.setMaterial(mat);
        rootNode.attachChild(floor);
        // *** 1. End
        
        // *** 2. Start (Physics - 3rd line debug)
        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);
        bulletAppState.getPhysicsSpace().enableDebug(assetManager);
        // *** 2. End
        
        // *** 3. Start (floor is rigid)
        scenePhy = new RigidBodyControl(0f);
        floor.addControl(scenePhy);
        bulletAppState.getPhysicsSpace().add(floor);
        // *** 3. End
        
        // *** 4. Start (invisible player)
        playerNode = new Node("the player");
        playerNode.setLocalTranslation(
                new Vector3f(0, 1, 0));
        rootNode.attachChild(playerNode);
        playerControl = new BetterCharacterControl(
                1.5f, 4, 30f);
        playerControl.setJumpForce(new Vector3f(0, 300, 0));
        playerControl.setGravity(new Vector3f(0, -10, 0));
        playerNode.addControl(playerControl);
        bulletAppState.getPhysicsSpace().add(playerControl);
        // *** 4. End
        
        // *** 5. Start (Camera attached to Player)
        camNode = new CameraNode("CamNode", cam);
        camNode.setControlDir(CameraControl.
                              ControlDirection.SpatialToCamera);
        camNode.setLocalTranslation(new Vector3f(0, 4, -6));
        Quaternion quat = new Quaternion();
        quat.lookAt(Vector3f.UNIT_Z, Vector3f.UNIT_Y);
        camNode.setLocalRotation(quat);
        playerNode.attachChild(camNode);
        camNode.setEnabled(true);
        flyCam.setEnabled(false);
        // *** 5. End
        
        // *** 6. Start (Arrow Keys)
        inputManager.addMapping("Forward",
                new KeyTrigger(KeyInput.KEY_UP));
        inputManager.addMapping("Back",
                new KeyTrigger(KeyInput.KEY_DOWN));
        inputManager.addMapping("Left",
                new KeyTrigger(KeyInput.KEY_LEFT));
        inputManager.addMapping("Right",
                new KeyTrigger(KeyInput.KEY_RIGHT));
        inputManager.addListener(this, "Left", 
                "Right","Forward", "Back");
        // *** 6. End
    }

    // *** 7. Start (Walk character)
    @Override
    public void simpleUpdate(float tpf) {
        Vector3f ForwardDir = Vector3f.UNIT_Z;
        Vector3f LeftDir = Vector3f.UNIT_X;
        walkDirection.set(0, 0, 0); 
        if (forward) {
            walkDirection = walkDirection.add(ForwardDir);
        } else if (backward) {
            walkDirection = walkDirection.subtract(ForwardDir);
        }
        if (Left) {
            walkDirection = walkDirection.add(LeftDir);
        } else if (Right) {
            walkDirection = walkDirection.subtract(LeftDir);
        }
        playerControl.setWalkDirection(walkDirection);
        vy = playerControl.getVelocity().y;
        if (vy<-10f) {
            System.out.println("Game over");
        }
    }
    // *** 7. End

    @Override
    public void simpleRender(RenderManager rm) {
    }

    // *** 8. Start (Which key pressed)
    public void onAction(String binding, boolean isPressed, float tpf) {
        if (binding.equals("Left")) {
            Left = isPressed;
        }
        else if (binding.equals("Right")) {
            Right = isPressed;
        }
        else if (binding.equals("Forward")) {
            forward = isPressed;
        }
        else if (binding.equals("Back")) {
            backward = isPressed;
        }
    }
    // *** 8. End
}


Output:


Sep 3, 2014

43. States and Controls

Here AppStates State1, State2, and State3 are used to control Control1, Control2, and Control3, respectively. In the main application, at different times we have different states active, resulting in different actions on a geometry object.




Control1 rotates a spatial.


// Control1.java

package mygame;

import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;

class Control1 extends AbstractControl{
    @Override
    protected void controlUpdate(float tpf) {
        spatial.rotate(tpf,tpf,tpf);
    }
    @Override
    protected void controlRender(RenderManager rm, ViewPort vp) {
    }
}



Control2 moves a spatial in +x and -x.


// Control2.java

package mygame;

import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;

public class Control2 extends AbstractControl{

    private float current;
    private int sign = -1;
        
    @Override
    protected void controlUpdate(float tpf) {
        current = spatial.getLocalTranslation().x;
        if (current<-2f) {
            sign = 1;
        }
        if (current>2f) {
            sign = -1;
        }
        spatial.move(5*sign*tpf,0,0);
    }
    
    @Override
    protected void controlRender(RenderManager rm, ViewPort vp) {
    }
}



Control 3 moves a spatial in +y and -y.


// Control3.java

package mygame;

import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;

class Control3 extends AbstractControl {
    
    private float current;
    private int sign = -1;
    
    @Override
    protected void controlUpdate(float tpf) {
        current = spatial.getLocalTranslation().y;
        if (current<-2f) {
            sign = 1;
        }
        if (current>2f) {
            sign = -1;
        }
        spatial.move(0,5*sign*tpf,0);
    }

    @Override
    protected void controlRender(RenderManager rm, ViewPort vp) {
    }
}



In State1, Geometry "Box" is set to Control1, during the time state is active.


// State1.java

package mygame;

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.scene.Geometry;

public class State1 extends AbstractAppState{
    
    private SimpleApplication app;
    private Geometry geom;
    private Control1 control1 = new Control1();
        
    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        super.initialize(stateManager, app);
        this.app = (SimpleApplication)app;
        geom = (Geometry)this.app.getRootNode().getChild("Box");
        geom.addControl(control1);
    }
    
      
    @Override
    public void stateAttached(AppStateManager stateManager) {
        super.stateAttached(stateManager);
        System.out.println("Attached - 1");
    }
 
    @Override
    public void update(float tpf) {
        super.update(tpf);
    }

    @Override
    public void stateDetached(AppStateManager stateManager) {
        super.stateDetached(stateManager);
        System.out.println("Detached - 1");
    }
 
    @Override
    public void cleanup() {
        super.cleanup();
        geom.removeControl(control1);
    }
}



In State1, Geometry "Box" is set to Control2, during the time state is active.


// State2.java

package mygame;

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.scene.Geometry;

public class State2 extends AbstractAppState{
    
    private SimpleApplication app;
    private Geometry geom;
    private Control2 control2 = new Control2();
        
    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        super.initialize(stateManager, app);
        this.app = (SimpleApplication)app;
        geom = (Geometry)this.app.getRootNode().getChild("Box");
        geom.addControl(control2);
    }
    
    @Override
    public void stateAttached(AppStateManager stateManager) {
        super.stateAttached(stateManager);
        System.out.println("Attached - 2");
    }
 
    @Override
    public void update(float tpf) {
        super.update(tpf);
    }

    @Override
    public void stateDetached(AppStateManager stateManager) {
        super.stateDetached(stateManager);
        System.out.println("Detached - 2");
    }
 
    @Override
    public void cleanup() {
        super.cleanup();
        this.geom.removeControl(control2);
    }
}



In State1, Geometry "Box" is set to Control3, during the time state is active.


// State3.java

package mygame;

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.scene.Geometry;

public class State3 extends AbstractAppState{
    
    private SimpleApplication app;
    private Geometry geom;
    private Control3 control3 = new Control3();

    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        super.initialize(stateManager, app);
        this.app = (SimpleApplication)app;
        geom = (Geometry)this.app.getRootNode().getChild("Box");
        geom.addControl(control3);
    }
    
    @Override
    public void stateAttached(AppStateManager stateManager) {
        super.stateAttached(stateManager);
        System.out.println("Attached - 3");
    }
 
    @Override
    public void update(float tpf) {
        super.update(tpf);
    }

    @Override
    public void stateDetached(AppStateManager stateManager) {
        super.stateDetached(stateManager);
        System.out.println("Detached - 3");
    }
 
    @Override
    public void cleanup() {
        super.cleanup();
        this.geom.removeControl(control3);
    }
}



// JMonkey43.java

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.system.AppSettings;

public class JMonkey43 extends SimpleApplication {
    public static void main(String[] args) {
        AppSettings settings = new AppSettings(true);
        settings.setFrameRate(60);
        settings.setTitle(
                "Box under different controls at different times!");
        JMonkey43 app = new JMonkey43();
        app.setSettings(settings);
        app.start();
    }
    
    private int counter = 0;
    Geometry geom;

    @Override
    public void simpleInitApp() {
        viewPort.setBackgroundColor(ColorRGBA.White);
        setDisplayFps(false);
        setDisplayStatView(false);
        flyCam.setEnabled(false);
        Box b = new Box(.5f, .5f, .5f);
        geom = new Geometry("Box", b);
        Material mat = new Material( 
            assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
        geom.setMaterial(mat);
        geom.setLocalTranslation(2,2,2);
        rootNode.attachChild(geom);
    }

    @Override
    public void simpleUpdate(float tpf) {
        counter++;
        if (counter==50) {
            stateManager.attach(new State1());
        }
        if (counter==200) {
            stateManager.detach(stateManager.getState(State1.class));
            stateManager.attach(new State2());
        }
        if (counter==350) {
            stateManager.detach(stateManager.getState(State2.class));
            stateManager.attach(new State3());
        }
        if (counter==500) {
            stateManager.detach(stateManager.getState(State3.class));
        }
        if (counter==600) {
            stateManager.attach(new State1());
            stateManager.attach(new State2());
            stateManager.attach(new State3());
        }
        if (counter==750) {
            stateManager.detach(stateManager.getState(State3.class));
            stateManager.detach(stateManager.getState(State2.class));
            stateManager.detach(stateManager.getState(State1.class));
        }
    }
    @Override
    public void simpleRender(RenderManager rm) {
    }
}


Output: