InputHandler for Physics First-Person-Shooters

I've read several separate threads from people looking for the same thing: a good InputHandler for Physics first-person-shooters.



The problem with just using a NodeHandler on a CameraNode with the model attached is that if you look up or down, the character will rotate so it's body is looking up.



Has there been a solution for this problem, so that the character's body will rotate right/left, but only the head (or just the camera, even) will rotate up/down?



None of the people who had the same problem posted their solutions, and the posts are so old I figure it'd be better to start a new one (at least that's what the forum recommended ;))



Help is appreciated.

Also the Handler will need to control a DynamicPhysicsNode, so collision detection works!

I've attempted to suppress the up/down controls by creating a few new classes. They are the same to what NodeHandler uses in every way, except that the up/down references have been completely removed. Somehow, though, it manages to still move the robot up and down!



Any help is appreciated:

package physicsblaster;


import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.RelativeMouse;
import com.jme.input.action.KeyNodeBackwardAction;
import com.jme.input.action.KeyNodeForwardAction;
import com.jme.input.action.KeyNodeLookDownAction;
import com.jme.input.action.KeyNodeLookUpAction;
import com.jme.input.action.KeyNodeRotateLeftAction;
import com.jme.input.action.KeyNodeRotateRightAction;
import com.jme.input.action.KeyNodeStrafeLeftAction;
import com.jme.input.action.KeyNodeStrafeRightAction;
import com.jme.math.Vector3f;
import com.jme.scene.Spatial;

/**
 * <code>NodeHandler</code> defines an InputHandler that sets
 * a node that can be controlled via keyboard and mouse inputs. By default the
 * commands are, WSAD moves the node forward, backward and strafes. The
 * arrow keys rotate and tilt the node and the mouse also rotates and tilts
 * the node.
 * @author Mark Powell
 * @version $Id: NodeHandler.java,v 1.15 2006/03/05 10:43:57 irrisor Exp $
 */
public class PhysicsFPSHandler extends InputHandler {

    /**
     * Constructor instantiates a new <code>NodeHandler</code> object. The
     * application is set for the use of the exit action. The node is set to
     * control, while the api defines which input api is to be used.
     * @param node the node to control.
     * @param api not used
     */
    public PhysicsFPSHandler(Spatial node) { //todo: remove parameter api

        setKeyBindings();
        setUpMouse(node, 1 );
        setActions(node, 0.5f, 0.01f );
    }

    /**
     * Constructor instantiates a new <code>NodeHandler</code> object. The
     * application is set for the use of the exit action. The node is set to
     * control, while the api defines which input api is to be used.
     * @param node the node to control.
     * @param keySpeed action speed for key actions (move)
     * @param mouseSpeed action speed for mouse actions (rotate)
     */
    public PhysicsFPSHandler(Spatial node, float keySpeed, float mouseSpeed) {

        setKeyBindings();
        setUpMouse(node, mouseSpeed );
        setActions(node, keySpeed, mouseSpeed );
    }

    /**
     *
     * <code>setKeyBindings</code> binds the keys to use for the actions.
     */
    private void setKeyBindings() {
        KeyBindingManager keyboard = KeyBindingManager.getKeyBindingManager();

        keyboard.set("forward", KeyInput.KEY_W);
        keyboard.set("backward", KeyInput.KEY_S);
        keyboard.set("strafeLeft", KeyInput.KEY_A);
        keyboard.set("strafeRight", KeyInput.KEY_D);
        keyboard.set("turnRight", KeyInput.KEY_RIGHT);
        keyboard.set("turnLeft", KeyInput.KEY_LEFT);
    }

    /**
     *
     * <code>setUpMouse</code> sets the mouse look object.
     * @param node the node to use for rotations.
     * @param mouseSpeed
     */
    private void setUpMouse( Spatial node, float mouseSpeed ) {
        mouse = new RelativeMouse("Mouse Input");
        mouse.registerWithInputHandler( this );

        PhysicsNodeMouseLook mouseLook = new PhysicsNodeMouseLook(mouse, node, 0.1f);
        mouseLook.setSpeed( mouseSpeed );
        mouseLook.setLockAxis(new Vector3f(node.getLocalRotation().getRotationColumn(1).x,
                node.getLocalRotation().getRotationColumn(1).y,
                node.getLocalRotation().getRotationColumn(1).z));
        addAction(mouseLook);
    }

    /**
     *
     * <code>setActions</code> sets the keyboard actions with the corresponding
     * key command.
     * @param node the node to control.
     * @param moveSpeed
     * @param turnSpeed
     */
    private void setActions( Spatial node, float moveSpeed, float turnSpeed ) {
        addAction( new KeyNodeForwardAction( node, moveSpeed ), "forward", true );
        addAction( new KeyNodeBackwardAction( node, moveSpeed ), "backward", true );
        addAction( new KeyNodeStrafeLeftAction( node, moveSpeed ), "strafeLeft", true );
        addAction( new KeyNodeStrafeRightAction( node, moveSpeed ), "strafeRight", true );
        KeyNodeRotateRightAction rotateRight = new KeyNodeRotateRightAction( node, turnSpeed );
        rotateRight.setLockAxis( node.getLocalRotation().getRotationColumn( 1 ) );
        addAction( rotateRight, "turnRight", true );
        KeyNodeRotateLeftAction rotateLeft = new KeyNodeRotateLeftAction( node, turnSpeed );
        rotateLeft.setLockAxis( node.getLocalRotation().getRotationColumn( 1 ) );
        addAction( rotateLeft, "turnLeft", true );
    }
}


package physicsblaster;

import com.jme.input.Mouse;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyNodeRotateLeftAction;
import com.jme.input.action.KeyNodeRotateRightAction;
import com.jme.input.action.MouseInputAction;
import com.jme.math.Vector3f;
import com.jme.scene.Spatial;

/**
 * <code>NodeMouseLook</code> defines a mouse action that detects mouse
 * movement and converts it into node rotations and node tilts.
 *
 * @author Mark Powell
 * @version $Id: NodeMouseLook.java,v 1.14 2006/07/22 20:59:10 renanse Exp $
 */
public class PhysicsNodeMouseLook extends MouseInputAction {

    //the actions that handle looking up, down, left and right.
    private KeyNodeRotateLeftAction rotateLeft;

    private KeyNodeRotateRightAction rotateRight;

    //the axis to lock
    private Vector3f lockAxis;

    //the node to control
    private Spatial node;

    //the event to distribute to the look actions.
    private static InputActionEvent event;

    /**
     * Constructor creates a new <code>NodeMouseLook</code> object. It takes
     * the mouse, node and speed of the looking.
     *
     * @param mouse
     *            the mouse to calculate view changes.
     * @param node
     *            the node to move.
     * @param speed
     *            the speed at which to alter the camera.
     */
    public PhysicsNodeMouseLook(Mouse mouse, Spatial node, float speed) {
        this.mouse = mouse;
        this.speed = speed;
        this.node = node;

        rotateLeft = new KeyNodeRotateLeftAction(this.node, speed);
        rotateRight = new KeyNodeRotateRightAction(this.node, speed);
       
        event = new InputActionEvent();
    }

    /**
     *
     * <code>setLockAxis</code> sets the axis that should be locked down. This
     * prevents "rolling" about a particular axis. Typically, this is set to the
     * mouse's up vector.
     *
     * @param lockAxis
     *            the axis that should be locked down to prevent rolling.
     */
    public void setLockAxis(Vector3f lockAxis) {
        this.lockAxis = lockAxis;
        rotateLeft.setLockAxis(lockAxis);
        rotateRight.setLockAxis(lockAxis);
    }

    /**
     * Returns the axis that is currently locked.
     *
     * @return The currently locked axis
     * @see #setLockAxis(com.jme.math.Vector3f)
     */
    public Vector3f getLockAxis() {
        return lockAxis;
    }

    /**
     *
     * <code>setSpeed</code> sets the speed of the mouse look.
     *
     * @param speed
     *            the speed of the mouse look.
     */
    public void setSpeed(float speed) {
        super.setSpeed( speed );
        rotateRight.setSpeed(speed);
        rotateLeft.setSpeed(speed);
    }

    /**
     * <code>performAction</code> checks for any movement of the mouse, and
     * calls the appropriate method to alter the node's orientation when
     * applicable.
     *
     * @see com.jme.input.action.MouseInputAction#performAction(InputActionEvent)
     */
    public void performAction(InputActionEvent evt) {
        float time = 0.01f * speed;

        if (mouse.getLocalTranslation().x > 0) {
            event.setTime(time * mouse.getLocalTranslation().x);
            rotateRight.performAction(event);
        } else if (mouse.getLocalTranslation().x < 0) {
            event.setTime(time * mouse.getLocalTranslation().x * -1);
            rotateLeft.performAction(event);
        }
    }

}




It still directly sets rotations/locations. You can read from the 'old' threads that this causes unwanted behavior of the physics engine.

I have heard you can make it so the camera's rotation does not affect the cube's rotation, or even better, the cube's rotation does not affect the camera. I never tried it though.

I think I'd actually be more interested in a Gears of War styled controller… any help is welcome.

Has anybody figured out how to suppress the up/down mouselook using NodeHandler or similar?? I've tried so many things but have never succeeded.



Is there a way to define an Axis (the character's y-axis), then have the camera swing around that axis, while having the character always face away from the camera? However, I still need to ONLY swing on the y axis and never have it move up/down. Is there some way I can utilize the lookAt or getRotation methods for this?



:smiley:

I am close–if I use the following code:


charNode.lookAt(cam.getDirection());



During update, the character turns on it's axis but the up/down mouselook doesn't affect it. Now all I need to do is to get the keys working from the NodeHandler and I'm set...