-
Notifications
You must be signed in to change notification settings - Fork 77
Third Person Control
Renanse edited this page Oct 24, 2012
·
2 revisions
/**
* Copyright (c) 2008-2010 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/
package com.ardor3d.input.control;
import com.ardor3d.framework.Canvas;
import com.ardor3d.input.Key;
import com.ardor3d.input.KeyboardState;
import com.ardor3d.input.MouseState;
import com.ardor3d.input.logical.InputTrigger;
import com.ardor3d.input.logical.LogicalLayer;
import com.ardor3d.input.logical.TriggerAction;
import com.ardor3d.input.logical.TriggerConditions;
import com.ardor3d.input.logical.TwoInputStates;
import com.ardor3d.math.Matrix3;
import com.ardor3d.math.Vector3;
import com.ardor3d.math.type.ReadOnlyVector3;
import com.ardor3d.renderer.Camera;
import com.ardor3d.scenegraph.Node;
import com.ardor3d.scenegraph.extension.CameraNode;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
// This class extends FirstPersonControl only to make it easier to use
// with ExampleBase.java. Remove this in your own projects!
public class ThirdPersonControl extends FirstPersonControl {
private final Vector3 _upAxis = new Vector3();
private double _mouseRotateSpeed = .005;
private double _moveSpeed = 50;
private double _keyRotateSpeed = 2.25;
private final Matrix3 _workerMatrix = new Matrix3();
private final Vector3 _workerStoreA = new Vector3();
private InputTrigger _mouseTrigger;
private InputTrigger _keyTrigger;
public Node _cameraRotationNode;
public CameraNode _cameraTranslationNode = new CameraNode();
private ThirdPersonControl(final ReadOnlyVector3 upAxis, final Node cameraRotationNode, final Camera cam,
final boolean updateFromCamera) {
super(upAxis);
_upAxis.set(upAxis);
_cameraRotationNode = cameraRotationNode;
// cam.set(new Camera()); // reset all previous settings
_cameraTranslationNode.setCamera(cam);
if (updateFromCamera) {
_cameraTranslationNode.updateFromCamera();
} else {
_cameraTranslationNode.setTranslation(new Vector3(0, 0, 20));
final Matrix3 rotation = new Matrix3(_cameraTranslationNode.getRotation());
rotation.setValue(0, 0, -1);
rotation.setValue(1, 1, 1);
rotation.setValue(2, 2, -1);
// rotation.lookAt(new Vector3(0, 0, 21), Vector3.NEG_UNIT_Y);
_cameraTranslationNode.setRotation(rotation);
}
// cam.
// _cameraTranslationNode.updateFromCamera();
// cameraTranslationNode.setTranslation(new Vector3(0, 40, 20));
cameraRotationNode.attachChild(_cameraTranslationNode);
cameraRotationNode.updateWorldTransform(true);
}
@Override
public ReadOnlyVector3 getUpAxis() {
return _upAxis;
}
@Override
public void setUpAxis(final ReadOnlyVector3 upAxis) {
_upAxis.set(upAxis);
}
@Override
public double getMouseRotateSpeed() {
return _mouseRotateSpeed;
}
@Override
public void setMouseRotateSpeed(final double speed) {
_mouseRotateSpeed = speed;
}
@Override
public double getMoveSpeed() {
return _moveSpeed;
}
@Override
public void setMoveSpeed(final double speed) {
_moveSpeed = speed;
}
@Override
public double getKeyRotateSpeed() {
return _keyRotateSpeed;
}
@Override
public void setKeyRotateSpeed(final double speed) {
_keyRotateSpeed = speed;
}
Vector3 temp = new Vector3();
Vector3 getLeft(final Node n) {
return n.getRotation().getColumn(0, temp);
}
Vector3 getUp(final Node n) {
return n.getRotation().getColumn(1, temp);
}
Vector3 getDir(final Node n) {
return n.getRotation().getColumn(2, temp);
}
Matrix3 tempM = new Matrix3();
void setLeft(final Node n, final ReadOnlyVector3 v) {
tempM.setColumn(0, v);
n.setRotation(tempM);
}
void setUp(final Node n, final ReadOnlyVector3 v) {
tempM.setColumn(1, v);
n.setRotation(tempM);
}
void setDir(final Node n, final ReadOnlyVector3 v) {
tempM.setColumn(2, v);
n.setRotation(tempM);
}
@Override
protected void move(final Camera camera, final KeyboardState kb, final double tpf) {
// MOVEMENT
int moveFB = 0, strafeLR = 0;
if (kb.isDown(Key.W)) {
moveFB -= 1;
}
if (kb.isDown(Key.S)) {
moveFB += 1;
}
if (kb.isDown(Key.A)) {
strafeLR -= 1;
}
if (kb.isDown(Key.D)) {
strafeLR += 1;
}
if (moveFB != 0 || strafeLR != 0) {
final Vector3 loc = _workerStoreA.zero();
if (moveFB == 1) {
loc.addLocal(getDir(_cameraRotationNode));
} else if (moveFB == -1) {
loc.subtractLocal(getDir(_cameraRotationNode));
}
if (strafeLR == 1) {
loc.addLocal(getLeft(_cameraRotationNode));
} else if (strafeLR == -1) {
loc.subtractLocal(getLeft(_cameraRotationNode));
}
loc.normalizeLocal().multiplyLocal(_moveSpeed * tpf).addLocal(_cameraRotationNode.getTranslation());
_cameraRotationNode.setTranslation(loc);
_cameraRotationNode.updateWorldTransform(true);
}
// ROTATION
int rotX = 0, rotY = 0;
if (kb.isDown(Key.UP)) {
rotY -= 1;
}
if (kb.isDown(Key.DOWN)) {
rotY += 1;
}
if (kb.isDown(Key.LEFT)) {
rotX += 1;
}
if (kb.isDown(Key.RIGHT)) {
rotX -= 1;
}
if (rotX != 0 || rotY != 0) {
rotate(camera, rotX * (_keyRotateSpeed / _mouseRotateSpeed) * tpf, rotY
* (_keyRotateSpeed / _mouseRotateSpeed) * tpf);
}
}
@Override
protected void rotate(final Camera camera, final double dx, final double dy) {
if (dx != 0) {
_workerMatrix.fromAngleNormalAxis(_mouseRotateSpeed * dx, _upAxis != null ? _upAxis
: getUp(_cameraRotationNode));
_workerMatrix.applyPost(getLeft(_cameraRotationNode), _workerStoreA);
setLeft(_cameraRotationNode, _workerStoreA);
_workerMatrix.applyPost(getDir(_cameraRotationNode), _workerStoreA);
setDir(_cameraRotationNode, _workerStoreA);
_workerMatrix.applyPost(getUp(_cameraRotationNode), _workerStoreA);
setUp(_cameraRotationNode, _workerStoreA);
}
if (dy != 0) {
_workerMatrix.fromAngleNormalAxis(_mouseRotateSpeed * dy, getLeft(_cameraRotationNode));
_workerMatrix.applyPost(getLeft(_cameraRotationNode), _workerStoreA);
setLeft(_cameraRotationNode, _workerStoreA);
_workerMatrix.applyPost(getDir(_cameraRotationNode), _workerStoreA);
setDir(_cameraRotationNode, _workerStoreA);
_workerMatrix.applyPost(getUp(_cameraRotationNode), _workerStoreA);
setUp(_cameraRotationNode, _workerStoreA);
}
/*
* final Quaternion transformAngle = Quaternion.fetchTempInstance();
*
* transformAngle .fromEulerAngles((xAngle -= x) * MathUtils.DEG_TO_RAD, 0.0D, (yAngle += y) *
* MathUtils.DEG_TO_RAD); _cameraRotationNode.setRotation(transformAngle); // transformAngle.apply(translation,
* translation); // main.cameraNode.setTranslation(translation); Quaternion.releaseTempInstance(transformAngle);
*/
_cameraRotationNode.updateWorldTransform(true);
}
/**
* @param layer
* the logical layer to register with
* @param upAxis
* the up axis of the camera
* @param dragOnly
* if true, mouse input will only rotate the camera if one of the mouse buttons (left, center or right)
* is down.
* @return a new ThirdPersonControl object
*/
public static ThirdPersonControl setupTriggers(final LogicalLayer layer, final ReadOnlyVector3 upAxis,
final boolean dragOnly, final Node controlNode, final Camera cam, final boolean updateFromCamera) {
final ThirdPersonControl control = new ThirdPersonControl(upAxis, controlNode, cam, updateFromCamera);
control.setupKeyboardTriggers(layer);
control.setupMouseTriggers(layer, dragOnly);
return control;
}
/**
* Deregister the triggers of the given ThirdPersonControl from the given LogicalLayer.
*
* @param layer
* @param control
*/
public static void removeTriggers(final LogicalLayer layer, final ThirdPersonControl control) {
if (control._mouseTrigger != null) {
layer.deregisterTrigger(control._mouseTrigger);
}
if (control._keyTrigger != null) {
layer.deregisterTrigger(control._keyTrigger);
}
}
@Override
public void setupMouseTriggers(final LogicalLayer layer, final boolean dragOnly) {
final ThirdPersonControl control = this;
// Mouse look
final Predicate<TwoInputStates> someMouseDown = Predicates.or(TriggerConditions.leftButtonDown(), Predicates
.or(TriggerConditions.rightButtonDown(), TriggerConditions.middleButtonDown()));
final Predicate<TwoInputStates> dragged = Predicates.and(TriggerConditions.mouseMoved(), someMouseDown);
final TriggerAction dragAction = new TriggerAction() {
// Test boolean to allow us to ignore first mouse event. First event can wildly vary based on platform.
private boolean firstPing = true;
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
final MouseState mouse = inputStates.getCurrent().getMouseState();
if (mouse.getDx() != 0 || mouse.getDy() != 0) {
if (!firstPing) {
control.rotate(source.getCanvasRenderer().getCamera(), -mouse.getDx(), -mouse.getDy());
} else {
firstPing = false;
}
}
}
};
_mouseTrigger = new InputTrigger(dragOnly ? dragged : TriggerConditions.mouseMoved(), dragAction);
layer.registerTrigger(_mouseTrigger);
}
@Override
public Predicate<TwoInputStates> setupKeyboardTriggers(final LogicalLayer layer) {
final ThirdPersonControl control = this;
// WASD control
final Predicate<TwoInputStates> keysHeld = new Predicate<TwoInputStates>() {
Key[] keys = new Key[] { Key.W, Key.A, Key.S, Key.D, Key.LEFT, Key.RIGHT, Key.UP, Key.DOWN };
public boolean apply(final TwoInputStates states) {
for (final Key k : keys) {
if (states.getCurrent() != null && states.getCurrent().getKeyboardState().isDown(k)) {
return true;
}
}
return false;
}
};
final TriggerAction moveAction = new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
control.move(source.getCanvasRenderer().getCamera(), inputStates.getCurrent().getKeyboardState(), tpf);
}
};
_keyTrigger = new InputTrigger(keysHeld, moveAction);
layer.registerTrigger(_keyTrigger);
return keysHeld;
}
@Override
public InputTrigger getKeyTrigger() {
return _keyTrigger;
}
@Override
public InputTrigger getMouseTrigger() {
return _mouseTrigger;
}
}