= hellochasecam
:author:
:revnumber:
:revdate: 2016/03/17 20:48
:relfileprefix: ../../
:imagesdir: ../..
ifdef::env-github,env-browser[:outfilesuffix: .adoc]


link:http://www.youtube.com/watch?v=dXGfecvI1Sk[Youtube video]

image:jme3/beginner/chasecamera.png[chasecamera.png,width="",height=""]

*  link:https://code.google.com/p/jmonkeyengine/source/browse/#svn%2Ftrunk%2Fengine%2Ftest-data%2FModels%2FNinja[Ninja Mesh]
*  link:http://jmonkeyengine.googlecode.com/svn/trunk/engine/town.zip[Town Scene]

*  Walk Bugs emoji:
*  Jump Bugs emoji:

Memo:

.  jMonkeyEngine calls the update() methods of all AppState objects in the order in which you attached them.
.  jMonkeyEngine calls the controlUpdate() methods of all controls in the order in which you added them.
.  jMonkeyEngine calls the simpleUpdate() method of the main SimpleApplication class.

—AbstractPhysicBodyContext

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.bullet.BulletAppState;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public abstract class AbstractPhysicBodyContext extends AbstractAppState
{

    private AppStateManager stateManager = null;
    //
    private static final BulletAppState bulletAppState;

    static
    {
        bulletAppState = new BulletAppState();
    }

    public AbstractPhysicBodyContext()
    {
    }

    /**
     @return the bulletAppState
     */
    public BulletAppState getBulletAppState()
    {
        return bulletAppState;
    }

    /**
     @param stateManager the stateManager to set
     Attaching BulletAppstate to Initialize PhysicsSpace
     */
    public void attachBulletAppstate(AppStateManager stateManager)
    {
        this.stateManager = stateManager;
        stateManager.attach(bulletAppState);
    }
}

----

—AbstractSpatialBodyContext.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.app.state.AbstractAppState;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public abstract class AbstractSpatialBodyContext extends AbstractAppState
{

    public AbstractSpatialBodyContext()
    {
    }

}

----

—ApplicationContext.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.input.FlyByCamera;
import com.jme3.input.InputManager;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import com.jme3.system.AppSettings;
import org.jmonkey.utils.Debug;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class ApplicationContext extends AbstractAppState
{

    private final Node rootNode;
//
    private final CameraContext sceneCameraContext;
    private final AvatarBodyManager avatarBodyManager;
    private final SceneBodyManager sceneBodyManager;

/**

    @param stateManager
    @param am
    @param settings
    @param inputManager
    @param rootNode
    @param cam
    @param flyByCam
    */
    public ApplicationContext(AppStateManager stateManager, AssetManager am, AppSettings settings, InputManager inputManager, Node rootNode, Camera cam, FlyByCamera flyByCam)
    {

        this.rootNode = rootNode;
        this.sceneCameraContext = new CameraContext(settings, inputManager, cam, flyByCam);
        this.sceneBodyManager = new SceneBodyManager(stateManager, am, rootNode);
        this.avatarBodyManager = new AvatarBodyManager(am, rootNode, sceneCameraContext);
    }

    @Override
    public void initialize(AppStateManager stateManager, Application app)
    {
        //super.initialize(stateManager, app);
        //TODO: initialize your AppState, e.g. attach spatials to rootNode
        //this is called on the OpenGL thread after the AppState has been attached

//
        stateManager.attach(this.sceneCameraContext);
        stateManager.attach(this.sceneBodyManager);//initialize physic spacein constructor
        stateManager.attach(this.avatarBodyManager);
        //
        Debug.showNodeAxes(app.getAssetManager(), this.rootNode, 1024.0f);
        Debug.attachWireFrameDebugGrid(app.getAssetManager(), rootNode, Vector3f.ZERO, 2048, ColorRGBA.DarkGray);
    }

    @Override
    public void update(float tpf)
    {

    }
}

----

—AvatarAnimationEventListener.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.bullet.objects.PhysicsCharacter;
import com.jme3.scene.Spatial;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class AvatarAnimationEventListener extends AbstractAppState implements AnimEventListener
{

    private final AnimChannel channel;
    private final AnimControl control;
    private final PlayerInputActionListener pial;
    private final AvatarAnimationHelper animHelper;
    private final PhysicsCharacter physicBody;
/**

    @param pial
    @param pc
    @param avatarMesh
    */
    public AvatarAnimationEventListener(PlayerInputActionListener pial, PhysicsCharacter pc, Spatial avatarMesh)
    {
        this.pial = pial;
        this.control = avatarMesh.getControl(AnimControl.class);
        assert (this.control != null);
        this.channel = this.control.createChannel();
        this.physicBody = pc;
        this.animHelper = new AvatarAnimationHelper(this.physicBody, this.channel);
    }

    @Override
    public void initialize(AppStateManager stateManager, Application app)
    {
        this.control.addListener(this);
        this.channel.setAnim("Idle1");
        this.channel.setSpeed(0.5f);
    }

    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName)
    {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    public void onAnimChange(AnimControl control, AnimChannel channel, String animName)
    {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.

    }

    /**
     @return the channel
     */
    protected AnimChannel getChannel()
    {
        return channel;
    }

    /**
     @return the control
     */
    protected AnimControl getControl()
    {
        return control;
    }

    /**
     * @return the animHelper
     */
    protected AvatarAnimationHelper getAnimHelper()
    {
        return animHelper;
    }
}

----

—AvatarAnimationHelper.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.animation.AnimChannel;
import com.jme3.animation.LoopMode;
import com.jme3.bullet.objects.PhysicsCharacter;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class AvatarAnimationHelper
{

    private final AnimChannel animChannel;
    private final PhysicsCharacter physicBody;
/**

    @param pc
    @param ac
    */
    public AvatarAnimationHelper(PhysicsCharacter pc, AnimChannel ac)
    {
        this.animChannel = ac;
        this.physicBody = pc;
    }

    protected void idle()
    {
        animChannel.setAnim("Idle1");
        animChannel.setSpeed(0.5f);
    }

    protected boolean forward(boolean pressed)
    {
        if (pressed)
        {
            if (this.physicBody.onGround())
            {
                animChannel.setAnim("Walk");
                animChannel.setSpeed(AvatarConstants.FORWARD_MOVE_SPEED * 2f);
                animChannel.setLoopMode(LoopMode.Loop);
            }
            return true;
        } else
        {
            idle();
            return false;
        }
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    protected boolean backward(boolean pressed)
    {
        if (pressed)
        {
            return true;
        } else
        {
            return false;
        }
    }

    protected boolean rightward(boolean pressed)
    {
        if (pressed)
        {
            return true;
        } else
        {
            return false;
        }
    }

    protected boolean leftward(boolean pressed)
    {
        if (pressed)
        {
            return true;
        } else
        {
            return false;
        }
    }

    protected boolean jump(boolean pressed)
    {
            if (pressed)
            {
                if (this.physicBody.onGround())
                {
                    animChannel.setAnim("HighJump");
                    animChannel.setSpeed(AvatarConstants.FORWARD_MOVE_SPEED / 1.8f);
                    animChannel.setLoopMode(LoopMode.DontLoop);
                    //
                    this.physicBody.jump();
                }
                return true;
            } else
            {
                return false;
            }
    }
}

----

—AvatarBodyManager.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.app.Application;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.objects.PhysicsCharacter;
import com.jme3.input.ChaseCamera;
import com.jme3.input.InputManager;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import org.jmonkey.utils.Debug;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class AvatarBodyManager extends AbstractPhysicBodyContext
{

    private InputManager inputManager;
    private final Node rootNode;
    //
    private final CameraContext cc;
    private final Camera cam;
    private final ChaseCamera chaseCam;
    //
    private final AvatarPhysicBodyContext apbc;
    private final AvatarSpatialBodyContext asbc;
    //
    private final PhysicsCharacter physicBody;
    private final Node avatar;
    private final BetterCharacterControl bcc;
    //

    private final PlayerInputActionListener playerInputListener;

/**

    @param am
    @param rootNode
    @param cc
    */
    public AvatarBodyManager(AssetManager am, Node rootNode, CameraContext cc)
    {

        //
        this.rootNode = rootNode;
        //
        this.asbc = new AvatarSpatialBodyContext(am, rootNode);
        this.apbc = new AvatarPhysicBodyContext();
        //
        this.physicBody = apbc.getPhysicBody();

        this.avatar = asbc.getAvatar();
        this.bcc = new BetterCharacterControl(AvatarConstants.COLLISION_SHAPE_RADIUS, AvatarConstants.COLLISION_SHAPE_RADIUS * 2, AvatarConstants.PHYSIC_BODY_MASS);
        //
        this.playerInputListener = new PlayerInputActionListener(this.physicBody, this.asbc.getAvatarMesh());
        //
        this.cc = cc;
        this.cam = cc.getCam();
        this.chaseCam = cc.getChaseCam();
    }

    @Override
    public void initialize(AppStateManager stateManager, Application app)
    {
        //TODO: initialize your AppState, e.g. attach spatials to rootNode
        //this is called on the OpenGL thread after the AppState has been attached

        stateManager.attach(this.asbc);
        stateManager.attach(this.apbc);
        stateManager.attach(this.playerInputListener);


//
        this.avatar.addControl(new AvatarBodyMoveControl(playerInputListener, physicBody, cam));
        this.avatar.addControl(chaseCam);
        this.avatar.addControl(bcc);

        //DEBUG
        Debug.showNodeAxes(app.getAssetManager(), avatar, 4);
        getBulletAppState().getPhysicsSpace().enableDebug(app.getAssetManager());
    }



    @Override
    public void update(float tpf)
    {
        //assert (sceneCameraContext != null);

        //correctDirectionVectors(cam.getDirection(), cam.getLeft());

    }
}

----

—AvatarBodyMoveControl.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.objects.PhysicsCharacter;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class AvatarBodyMoveControl extends AbstractControl
{
    private final Camera cam;
    private final PhysicsCharacter physicBody;
    private final PlayerInputActionListener pial;
/**

    @param pial
    @param physicBody
    @param cam
    */
    public AvatarBodyMoveControl(PlayerInputActionListener pial, PhysicsCharacter physicBody, Camera cam)
    {
        this.pial = pial;
        this.physicBody = physicBody;
        this.cam = cam;
    }
    private final Vector3f walkDirection = new Vector3f();

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

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


        /**

     @param camDir
     @param camLeft
     */
    public void correctDirectionVectors()
    {
//        assert (camDir != null);
//        assert (camLeft != null);
//        assert (walkDirection != null);
        //Affect forward, backward move speed 0.6f lower - 1.0f faster
        Vector3f camDirVector = cam.getDirection().clone().multLocal(AvatarConstants.FORWARD_MOVE_SPEED);
        //Affect left, right move speed 0.6f lower - 1.0f faster
        Vector3f camLeftVector = cam.getLeft().clone().multLocal(AvatarConstants.SIDEWARD_MOVE_SPEED);

        walkDirection.set(0, 0, 0);//critical
        if (pial.isLeftward())
        {
            walkDirection.addLocal(camLeftVector);
        }
        if (pial.isRightward())
        {
            walkDirection.addLocal(camLeftVector.negate());
        }
        if (pial.isForward())
        {
            walkDirection.addLocal(camDirVector);
        }
        if (pial.isBackward())
        {
            //@TODO Bug if cam direction (0, -n, 0) - character fly upwards ;)
            walkDirection.addLocal(camDirVector.negate());
        }
        physicBody.setWalkDirection(walkDirection);//Critical



        //Avoid vibration
        spatial.setLocalTranslation(physicBody.getPhysicsLocation());
        //Translate Node accordingly
        spatial.getControl(BetterCharacterControl.class).warp(physicBody.getPhysicsLocation());
        //Rotate Node accordingly to camera
        spatial.getControl(
                BetterCharacterControl.class).setViewDirection(
                cam.getDirection().negate());

    }
}


----

—AvatarConstants.java

[source,java]
----

package org.jmonkey3.chasecam;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class AvatarConstants
{
    public static final float COLLISION_SHAPE_CENTERAL_POINT = 0.0f;
    public static final float COLLISION_SHAPE_RADIUS = 4.0f;
    //
    public static final float PHYSIC_BODY_MASS = 1.0f;
    public static float FORWARD_MOVE_SPEED = 0.8f;
    public static float SIDEWARD_MOVE_SPEED = 0.6f;
}

----

—AvatarPhysicBodyContext.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.app.Application;
import com.jme3.app.state.AppStateManager;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.objects.PhysicsCharacter;
import com.jme3.math.Vector3f;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class AvatarPhysicBodyContext extends AbstractPhysicBodyContext
{



    private final PhysicsCharacter physicBody;


    public AvatarPhysicBodyContext()
    {

        this.physicBody = new PhysicsCharacter(new SphereCollisionShape(AvatarConstants.COLLISION_SHAPE_RADIUS), .01f);

    }



    @Override
    public void initialize(AppStateManager stateManager, Application app)
    {
//
        assert (getBulletAppState() != null);
        System.out.println(this.getClass().getName() + ".getBulletAppState().hashCode() = " + getBulletAppState().hashCode());

//
        this.physicBody.setJumpSpeed(32);
        this.physicBody.setFallSpeed(32);
        this.physicBody.setGravity(32);
        this.physicBody.setPhysicsLocation(new Vector3f(0, 10, 0));
        //
        getBulletAppState().getPhysicsSpace().add(this.physicBody);

    }

    @Override
    public void update(float tpf)
    {
    }

    @Override
    public void cleanup()
    {
        super.cleanup();
    }

    /**
     @return the physicBody
     */
    public PhysicsCharacter getPhysicBody()
    {
        return this.physicBody;
    }
}

----

—AvatarSpatialBodyContext.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.app.Application;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class AvatarSpatialBodyContext extends AbstractSpatialBodyContext
{

    //
    private final Node rootNode;
    //
    private final Node avatar;
    private final Spatial avatarMesh;
    private final Vector3f correction;
/**

    @param am
    @param rootNode
    */
    public AvatarSpatialBodyContext(AssetManager am, Node rootNode)
    {
        this.rootNode = rootNode;
        //
        this.avatar = new Node();
        this.avatarMesh = am.loadModel("Models/Ninja/Ninja.mesh.xml");
        this.correction = new Vector3f(
                0,
                AvatarConstants.COLLISION_SHAPE_CENTERAL_POINT - AvatarConstants.COLLISION_SHAPE_RADIUS,
                0);
    }

    @Override
    public void initialize(AppStateManager stateManager, Application app)
    {


        this.avatarMesh.setLocalScale(new Vector3f(0.05f, 0.05f, 0.05f));//Trouble with scales?
        this.avatarMesh.setLocalTranslation(this.correction);
        this.avatar.attachChild(this.avatarMesh);
        this.rootNode.attachChild(this.avatar);

        //super.initialize(stateManager, app); //To change body of generated methods, choose Tools | Templates.
    }

    /**
     @return the avatar
     */
    public Node getAvatar()
    {
        return avatar;
    }

    /**
     * @return the avatarMesh
     */
    public Spatial getAvatarMesh()
    {
        return avatarMesh;
    }
}

----

—CameraContext.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.input.ChaseCamera;
import com.jme3.input.FlyByCamera;
import com.jme3.input.InputManager;
import com.jme3.renderer.Camera;
import com.jme3.system.AppSettings;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class CameraContext extends AbstractAppState
{

    private final AppSettings settings;
    private final InputManager inputManager;
    /*
     http://hub.jmonkeyengine.org/javadoc/com/jme3/renderer/Camera.html
     public class Camera
     extends java.lang.Object
     implements Savable, java.lang.Cloneable

     Width and height are set to the current Application's settings.getWidth() and settings.getHeight() values.
     Frustum Perspective:
     Frame of view angle of 45Â° along the Y axis
     Aspect ratio of width divided by height
     Near view plane of 1 wu
     Far view plane of 1000 wu
     Start location at (0f, 0f, 10f).
     Start direction is looking at the origin.
     */
    private final Camera cam;
    /*
     http://hub.jmonkeyengine.org/javadoc/com/jme3/input/ChaseCamera.html
     public class ChaseCamera
     extends java.lang.Object
     implements ActionListener, AnalogListener, Control

     A camera that follows a spatial and can turn around it by dragging the mouse
     Constructs the chase camera, and registers inputs if you use this
     constructor you have to attach the cam later to a spatial doing
     spatial.addControl(chaseCamera);
     */
    private final ChaseCamera chaseCam;
    private final FlyByCamera flyByCam;

/**

    @param settings
    @param inputManager
    @param cam
    @param flyByCam
    */
    public CameraContext(AppSettings settings, InputManager inputManager, Camera cam, FlyByCamera flyByCam)
    {

        assert (settings != null);
        this.settings = settings;
        assert (inputManager != null);
        this.inputManager = inputManager;
        assert (cam != null);
        this.cam = cam;
        assert (flyByCam != null);
        this.flyByCam = flyByCam;
        this.chaseCam = new ChaseCamera(this.cam, this.inputManager);
    }

    @Override
    public void initialize(AppStateManager stateManager, Application app)
    {
        super.initialize(stateManager, app);
        //TODO: initialize your AppState, e.g. attach spatials to rootNode
        //this is called on the OpenGL thread after the AppState has been attached

        this.cam.setFrustumPerspective(116.0f, (settings.getWidth() / settings.getHeight()), 1.0f, 2000.0f);
        //this.flyByCam.setMoveSpeed(100);
        this.flyByCam.setEnabled(false);
    }

    /**
     @return the cam
     */
    public Camera getCam()
    {
        return cam;
    }

    /**
     @return the chaseCam
     */
    public ChaseCamera getChaseCam()
    {
        return chaseCam;
    }
}

----

—PlayerInputActionListener.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.bullet.objects.PhysicsCharacter;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.scene.Spatial;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class PlayerInputActionListener extends AbstractAppState implements ActionListener
{

    private final PhysicsCharacter physicBody;
//
    private boolean leftward = false;
    private boolean rightward = false;
    private boolean forward = false;
    private boolean backward = false;
    private boolean jump = false;
    private final AvatarAnimationEventListener aael;
/**

    @param pc
    @param avatar
    */
    public PlayerInputActionListener(PhysicsCharacter pc, Spatial avatar)
    {
        this.physicBody = pc;
        this.aael = new AvatarAnimationEventListener(this, this.physicBody, avatar);
    }

    @Override
    public void initialize(AppStateManager stateManager, Application app)
    {
        stateManager.attach(this.aael);
        //
        app.getInputManager().addMapping("LEFTWARD", new KeyTrigger(KeyInput.KEY_A));
        app.getInputManager().addMapping("RIGHTWARD", new KeyTrigger(KeyInput.KEY_D));
        app.getInputManager().addMapping("FORWARD", new KeyTrigger(KeyInput.KEY_W));
        app.getInputManager().addMapping("BACKWARD", new KeyTrigger(KeyInput.KEY_S));
        app.getInputManager().addMapping("JUMP", new KeyTrigger(KeyInput.KEY_SPACE));
        app.getInputManager().addListener(this, "LEFTWARD");
        app.getInputManager().addListener(this, "RIGHTWARD");
        app.getInputManager().addListener(this, "FORWARD");
        app.getInputManager().addListener(this, "BACKWARD");
        app.getInputManager().addListener(this, "JUMP");
        //
    }

    /**
     @param binding
     @param keyPressed
     @param tpf
     */
    public void onAction(String binding, boolean keyPressed, float tpf)
    {

        if (binding.equals("LEFTWARD"))
        {

            this.leftward = this.aael.getAnimHelper().leftward(keyPressed);

        } else if (binding.equals("RIGHTWARD"))
        {

            this.rightward = this.aael.getAnimHelper().rightward(keyPressed);

        } else if (binding.equals("FORWARD"))
        {

            this.forward = this.aael.getAnimHelper().forward(keyPressed);

        } else if (binding.equals("BACKWARD"))
        {

                this.backward = this.aael.getAnimHelper().backward(keyPressed);

        } else if (binding.equals("JUMP"))
        {

            this.jump = this.aael.getAnimHelper().jump(keyPressed);

        }
    }

    /**
     @return the leftward
     */
    public boolean isLeftward()
    {
        return this.leftward;
    }

    /**
     @return the rightward
     */
    public boolean isRightward()
    {
        return this.rightward;
    }

    /**
     @return the forward
     */
    public boolean isForward()
    {
        return this.forward;
    }

    /**
     @return the backward
     */
    public boolean isBackward()
    {
        return this.backward;
    }

    /**
     @return the jump
     */
    public boolean isJump()
    {
        return this.jump;
    }
}

----

—SceneBodyManager.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.app.Application;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.scene.Node;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class SceneBodyManager extends AbstractPhysicBodyContext
{

    private final ScenePhysicBodyContext spbc;
    private final SceneSpatialBodyContext ssbc;

/**

    @param stateManager
    @param am
    @param rootNode
    */
    public SceneBodyManager(AppStateManager stateManager, AssetManager am, Node rootNode)
    {


        this.ssbc = new SceneSpatialBodyContext(am, rootNode);
        this.spbc = new ScenePhysicBodyContext(ssbc.getScene());
    }

    @Override
    public void initialize(AppStateManager stateManager, Application app)
    {

        //PhysicsSpace Initialization
        attachBulletAppstate(stateManager);
//
        stateManager.attach(this.ssbc);
        stateManager.attach(this.spbc);

    }
}

----

—ScenePhysicBodyContext.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.app.Application;
import com.jme3.app.state.AppStateManager;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.scene.Node;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class ScenePhysicBodyContext extends AbstractPhysicBodyContext
{
    private final RigidBodyControl rigidBodyControl;
    private final Node scene;

/**

    @param scene
    */
    public ScenePhysicBodyContext(Node scene)
    {
        this.scene = scene;
        this.rigidBodyControl = new RigidBodyControl(.0f);
    }


    @Override
    public void initialize(AppStateManager stateManager, Application app)
    {
        //
        //Add scene to PhysicsSpace
        System.out.println(this.getClass().getName() + ".getBulletAppState().hashCode() = " + getBulletAppState().hashCode());
        scene.addControl(rigidBodyControl);
        getBulletAppState().getPhysicsSpace().addAll(scene);
    }

}

----

—SceneSpatialBodyContext.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.app.Application;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.asset.plugins.ZipLocator;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class SceneSpatialBodyContext extends AbstractSpatialBodyContext
{

    private final Node rootNode;
    //
    private final Node scene;
    private AmbientLight ambient;
    private DirectionalLight sun;
/**

    @param am
    @param rootNode
    */
    public SceneSpatialBodyContext(AssetManager am, Node rootNode)
    {
        this.rootNode = rootNode;
        //
        am.registerLocator("town.zip", ZipLocator.class);
        this.scene = (Node) am.loadModel("main.scene");
        this.ambient = new AmbientLight();
        this.sun = new DirectionalLight();
    }

    @Override
    public void initialize(AppStateManager stateManager, Application app)
    {
        //Main Scene loading

        this.scene.setLocalScale(0.1f);
        this.scene.scale(32.0f);
        //
        this.sun.setDirection(new Vector3f(1.4f, -1.4f, -1.4f));
        this.scene.setLocalTranslation(Vector3f.ZERO);
        //

        rootNode.attachChild(this.scene);
        rootNode.addLight(this.ambient);
        rootNode.addLight(this.sun);
    }

    /**
     @return the scene
     */
    public Node getScene()
    {
        return scene;
    }
}

----

—TheGame.java

[source,java]
----

package org.jmonkey3.chasecam;

import com.jme3.app.SimpleApplication;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class TheGame extends SimpleApplication
{

    private ApplicationContext applicationContext;

    public TheGame()
    {
    }

    //
    public static void main(String[] args)
    {
        TheGame game = new TheGame();
        game.setShowSettings(false);
        game.start();
    }

    @Override
    public void simpleInitApp()
    {
        this.applicationContext = new ApplicationContext(stateManager, assetManager, settings, inputManager, rootNode, cam, flyCam);
        //
        stateManager.attach(applicationContext);
    }
}

----

—Debug.java

[source,java]
----


package org.jmonkey.utils;

import com.jme3.animation.AnimControl;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.debug.Arrow;
import com.jme3.scene.debug.Grid;
import com.jme3.scene.debug.SkeletonDebugger;
import com.jme3.scene.shape.Line;
import static org.jmonkey.utils.SpatialUtils.makeGeometry;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class Debug
{

    public static void showNodeAxes(AssetManager am, Node n, float axisLen)
    {
        Vector3f v = new Vector3f(axisLen, 0, 0);
        Arrow a = new Arrow(v);
        Material mat = new Material(am, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Red);
        Geometry geom = new Geometry(n.getName() + "XAxis", a);
        geom.setMaterial(mat);
        n.attachChild(geom);


        //
        v = new Vector3f(0, axisLen, 0);
        a = new Arrow(v);
        mat = new Material(am, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Green);
        geom = new Geometry(n.getName() + "YAxis", a);
        geom.setMaterial(mat);
        n.attachChild(geom);


        //
        v = new Vector3f(0, 0, axisLen);
        a = new Arrow(v);
        mat = new Material(am, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Blue);
        geom = new Geometry(n.getName() + "ZAxis", a);
        geom.setMaterial(mat);
        n.attachChild(geom);
    }

    //
    public static void showVector3fArrow(AssetManager am, Node n, Vector3f v, ColorRGBA color, String name)
    {
        Arrow a = new Arrow(v);
        Material mat = MaterialUtils.makeMaterial(am, "Common/MatDefs/Misc/Unshaded.j3md", color);
        Geometry geom = makeGeometry(a, mat, name);
        n.attachChild(geom);
    }

    public static void showVector3fLine(AssetManager am, Node n, Vector3f v, ColorRGBA color, String name)
    {
        Line l = new Line(v.subtract(v), v);
        Material mat = MaterialUtils.makeMaterial(am, "Common/MatDefs/Misc/Unshaded.j3md", color);
        Geometry geom = makeGeometry(l, mat, name);
        n.attachChild(geom);
    }

//Skeleton Debugger
    public static void attachSkeleton(AssetManager am, Node player, AnimControl control)
    {
        SkeletonDebugger skeletonDebug = new SkeletonDebugger("skeleton", control.getSkeleton());
        Material mat2 = new Material(am, "Common/MatDefs/Misc/Unshaded.j3md");
        mat2.setColor("Color", ColorRGBA.Yellow);
        mat2.getAdditionalRenderState().setDepthTest(false);
        skeletonDebug.setMaterial(mat2);
        player.attachChild(skeletonDebug);
    }

    ///
    public static void attachWireFrameDebugGrid(AssetManager assetManager, Node n, Vector3f pos, Integer size, ColorRGBA color)
    {
        Geometry g = new Geometry("wireFrameDebugGrid", new Grid(size, size, 1.0f));//1WU
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.getAdditionalRenderState().setWireframe(true);
        mat.setColor("Color", color);
        g.setMaterial(mat);
        g.center().move(pos);
        n.attachChild(g);
    }
}


----

—MaterialUtils.java

[source,java]
----

package org.jmonkey.utils;

import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;

/*
 Chase camera (aka 3rd person camera) example
 Based on official TestQ3.java

 @author Alex Cham aka Jcrypto
 */
public class MaterialUtils
{

    public MaterialUtils()
    {
    }


    //"Common/MatDefs/Misc/Unshaded.j3md"
    public static Material makeMaterial(AssetManager am, String name, ColorRGBA color)
    {
        Material mat = new Material(am, name);
        mat.setColor("Color", color);
        return mat;
    }
}

----

—SpatialUtils.java

[source,java]
----


package org.jmonkey.utils;

import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;

/**

 @author java
 */
public class SpatialUtils
{
    //
    public static Node makeNode(String name)
    {
        Node n = new Node(name);
        return n;
    }

//
    public static Geometry makeGeometry(Mesh mesh, Material mat, String name)
    {
        Geometry geom = new Geometry(name, mesh);
        geom.setMaterial(mat);
        return geom;
    }

    //
    public static Geometry makeGeometry(Vector3f loc, Vector3f scl, Mesh mesh, Material mat, String name)
    {
        Geometry geom = new Geometry(name, mesh);
        geom.setMaterial(mat);
        geom.setLocalTranslation(loc);
        geom.setLocalScale(scl);
        return geom;
    }
}


----
