MMORPG Style 3rd Person Camera for Players
AutoRotateCamera
Pros:
*camera that follows CharacterControl or VehicleControl of a target spatial
*if the control rotates, the camera rotates
*adjustable zoom, horizontal rotation, vertical rotation
*adjusted vertical rotation is added as an offset to the current rotation of the control
*good use with a control that is setting its viewdirection / forward vector accordingly, preferably a character control which is using left/right movement keys as left/right rotation
Cons:
*no effect if no character control / vehicle control is assigned
PhysicalAutoRotateCamera extension of AutoRotateCamera
Pros:
*adds physical collision checks
*if an physical object is between the camera and the target to look at, the camera zooms in and places itself infront of the object
*that means that line of sight to the target will be preserved
Cons:
*does not work correctly if the camera has to zoom in too closely, thus if minimum distance (specified by user) is greater than the distance the camera has to zoom in
*no effect if no character control / vehicle control is assigned
down below are 3 code samples:
- TestCase
- AutoRotateCamera
- PhysicalAutoRotateCamera
_______________________________________________________________________________________________________
TestWalkingChar_orig.java - Move with w,a,s,d / click and drag left mouse for rotating / hold right mouse for strafing
_______________________________________________________________________________________________________
[java collapse=âtrueâ]
/**
- Copyright © 2009-2010 jMonkeyEngine
- All rights reserved.
*
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
*
-
- Redistributions of source code must retain the above copyright
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
*
-
- Redistributions in binary form must reproduce the above copyright
- Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
*
-
- Neither the name of âjMonkeyEngineâ nor the names of its contributors
- Neither the name of âjMonkeyEngineâ nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
*
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package mygame;
import utility.PhysicalAutoRotateCamera;
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.animation.LoopMode;
import com.jme3.bullet.BulletAppState;
import com.jme3.app.SimpleApplication;
import com.jme3.bounding.BoundingBox;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.effect.EmitterSphereShape;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.scene.shape.Sphere.TextureMode;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory;
import java.util.ArrayList;
import java.util.List;
import jme3test.bullet.BombControl;
import jme3tools.converters.ImageToAwt;
/**
*
-
@author normenhansen
/
public class TestWalkingChar_orig extends SimpleApplication implements ActionListener, PhysicsCollisionListener, AnimEventListener {
public static final Quaternion YAW090 = new Quaternion().fromAngleAxis(FastMath.PI/2, new Vector3f(0,1,0));
public static final Quaternion ROT_LEFT = new Quaternion().fromAngleAxis(FastMath.PI/32, new Vector3f(0,1,0));
public static final Quaternion ROT_RIGHT = new Quaternion().fromAngleAxis(-FastMath.PI/32, new Vector3f(0,1,0));
private BulletAppState bulletAppState;
//character
CharacterControl character;
Node model;
//temp vectors
Vector3f walkDirection = new Vector3f();
//terrain
TerrainQuad terrain;
RigidBodyControl terrainPhysicsNode;
//Materials
Material matRock;
Material matWire;
Material matBullet;
//animation
AnimChannel animationChannel;
AnimChannel shootingChannel;
AnimControl animationControl;
float airTime = 0;
//camera
boolean left = false, right = false, up = false, down = false, strafeEnabled=false;
//ChaseCamera chaseCam;
PhysicalAutoRotateCamera chaseCam;
//bullet
Sphere bullet;
SphereCollisionShape bulletCollisionShape;
//explosion
ParticleEmitter effect;
//brick wall
Box brick;
float bLength = 0.8f;
float bWidth = 0.4f;
float bHeight = 0.4f;
FilterPostProcessor fpp;
public static void main(String[] args) {
TestWalkingChar_orig app = new TestWalkingChar_orig();
app.start();
}
@Override
public void simpleInitApp() {
bulletAppState = new BulletAppState();
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
setupKeys();
prepareBullet();
prepareEffect();
createLight();
createSky();
createTerrain();
createWall();
createCharacter();
setupChaseCamera();
setupAnimationController();
setupFilter();
}
private void setupFilter() {
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
BloomFilter bloom = new BloomFilter(BloomFilter.GlowMode.Objects);
fpp.addFilter(bloom);
viewPort.addProcessor(fpp);
}
private PhysicsSpace getPhysicsSpace() {
return bulletAppState.getPhysicsSpace();
}
private void setupKeys() {
inputManager.addMapping(âwireframeâ, new KeyTrigger(KeyInput.KEY_T));
inputManager.addListener(this, âwireframeâ);
inputManager.addMapping(âCharLeftâ, new KeyTrigger(KeyInput.KEY_A));
inputManager.addMapping(âCharRightâ, new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping(âCharUpâ, new KeyTrigger(KeyInput.KEY_W));
inputManager.addMapping(âCharDownâ, new KeyTrigger(KeyInput.KEY_S));
inputManager.addMapping(âCharSpaceâ, new KeyTrigger(KeyInput.KEY_RETURN));
inputManager.addMapping(âCharShootâ, new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addMapping(âCharStrafeâ, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
inputManager.addListener(this, âCharLeftâ);
inputManager.addListener(this, âCharRightâ);
inputManager.addListener(this, âCharUpâ);
inputManager.addListener(this, âCharDownâ);
inputManager.addListener(this, âCharSpaceâ);
inputManager.addListener(this, âCharShootâ);
inputManager.addListener(this, âCharStrafeâ);
}
private void createWall() {
float xOff = -144;
float zOff = -40;
float startpt = bLength / 4 - xOff;
float height = 6.1f;
brick = new Box(Vector3f.ZERO, bLength, bHeight, bWidth);
brick.scaleTextureCoordinates(new Vector2f(1f, .5f));
for (int j = 0; j < 15; j++) {
for (int i = 0; i < 4; i++) {
Vector3f vt = new Vector3f(i * bLength * 2 + startpt, bHeight + height, zOff);
addBrick(vt);
}
startpt = -startpt;
height += 1.01f * bHeight;
}
}
private void addBrick(Vector3f ori) {
Geometry reBoxg = new Geometry(âbrickâ, brick);
reBoxg.setMaterial(matRock);
reBoxg.setLocalTranslation(ori);
reBoxg.addControl(new RigidBodyControl(1.5f));
reBoxg.setShadowMode(ShadowMode.CastAndReceive);
this.rootNode.attachChild(reBoxg);
this.getPhysicsSpace().add(reBoxg);
}
private void prepareBullet() {
bullet = new Sphere(32, 32, 0.4f, true, false);
bullet.setTextureMode(TextureMode.Projected);
bulletCollisionShape = new SphereCollisionShape(0.4f);
matBullet = new Material(getAssetManager(), âCommon/MatDefs/Misc/SolidColor.j3mdâ);
matBullet.setColor(âColorâ, ColorRGBA.Green);
matBullet.setColor(âm_GlowColorâ, ColorRGBA.Green);
getPhysicsSpace().addCollisionListener(this);
}
private void prepareEffect() {
int COUNT_FACTOR = 1;
float COUNT_FACTOR_F = 1f;
effect = new ParticleEmitter(âFlameâ, Type.Triangle, 32 * COUNT_FACTOR);
effect.setSelectRandomImage(true);
effect.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f, (float) (1f / COUNT_FACTOR_F)));
effect.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
effect.setStartSize(1.3f);
effect.setEndSize(2f);
effect.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
effect.setParticlesPerSec(0);
effect.setGravity(-5f);
effect.setLowLife(.4f);
effect.setHighLife(.5f);
effect.setInitialVelocity(new Vector3f(0, 7, 0));
effect.setVelocityVariation(1f);
effect.setImagesX(2);
effect.setImagesY(2);
Material mat = new Material(assetManager, âCommon/MatDefs/Misc/Particle.j3mdâ);
mat.setTexture(âTextureâ, assetManager.loadTexture(âEffects/Explosion/flame.pngâ));
effect.setMaterial(mat);
effect.setLocalScale(100);
rootNode.attachChild(effect);
}
private void createLight() {
Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();
DirectionalLight dl = new DirectionalLight();
dl.setDirection(direction);
dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
rootNode.addLight(dl);
}
private void createSky() {
rootNode.attachChild(SkyFactory.createSky(assetManager, âTextures/Sky/Bright/BrightSky.ddsâ, false));
}
private void createTerrain() {
matRock = new Material(assetManager, âCommon/MatDefs/Terrain/TerrainLighting.j3mdâ);
matRock.setBoolean(âuseTriPlanarMappingâ, false);
matRock.setBoolean(âWardIsoâ, true);
matRock.setTexture(âAlphaMapâ, assetManager.loadTexture(âTextures/Terrain/splat/alphamap.pngâ));
Texture heightMapImage = assetManager.loadTexture(âTextures/Terrain/splat/mountains512.pngâ);
Texture grass = assetManager.loadTexture(âTextures/Terrain/splat/grass.jpgâ);
grass.setWrap(WrapMode.Repeat);
matRock.setTexture(âDiffuseMapâ, grass);
matRock.setFloat(âDiffuseMap_0_scaleâ, 64);
Texture dirt = assetManager.loadTexture(âTextures/Terrain/splat/dirt.jpgâ);
dirt.setWrap(WrapMode.Repeat);
matRock.setTexture(âDiffuseMap_1â, dirt);
matRock.setFloat(âDiffuseMap_1_scaleâ, 16);
Texture rock = assetManager.loadTexture(âTextures/Terrain/splat/road.jpgâ);
rock.setWrap(WrapMode.Repeat);
matRock.setTexture(âDiffuseMap_2â, rock);
matRock.setFloat(âDiffuseMap_2_scaleâ, 128);
Texture normalMap0 = assetManager.loadTexture(âTextures/Terrain/splat/grass_normal.pngâ);
normalMap0.setWrap(WrapMode.Repeat);
Texture normalMap1 = assetManager.loadTexture(âTextures/Terrain/splat/dirt_normal.pngâ);
normalMap1.setWrap(WrapMode.Repeat);
Texture normalMap2 = assetManager.loadTexture(âTextures/Terrain/splat/road_normal.pngâ);
normalMap2.setWrap(WrapMode.Repeat);
matRock.setTexture(âNormalMapâ, normalMap0);
matRock.setTexture(âNormalMap_1â, normalMap2);
matRock.setTexture(âNormalMap_2â, normalMap2);
matWire = new Material(assetManager, âCommon/MatDefs/Misc/WireColor.j3mdâ);
matWire.setColor(âColorâ, ColorRGBA.Green);
AbstractHeightMap heightmap = null;
try {
heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, true, 0), 0.25f);
heightmap.load();
} catch (Exception e) {
e.printStackTrace();
}
terrain = new TerrainQuad(âterrainâ, 65, 513, heightmap.getHeightMap());
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
TerrainLodControl control = new TerrainLodControl(terrain, cameras);
terrain.addControl(control);
terrain.setMaterial(matRock);
terrain.setModelBound(new BoundingBox());
terrain.updateModelBound();
terrain.setLocalScale(new Vector3f(2, 2, 2));
terrainPhysicsNode = new RigidBodyControl(CollisionShapeFactory.createMeshShape(terrain), 0);
terrain.addControl(terrainPhysicsNode);
rootNode.attachChild(terrain);
getPhysicsSpace().add(terrainPhysicsNode);
}
private void createCharacter() {
CapsuleCollisionShape capsule = new CapsuleCollisionShape(1.5f, 2f);
character = new CharacterControl(capsule, 0.1f);
model = (Node) assetManager.loadModel(âModels/Oto/Oto.mesh.xmlâ);
model.setLocalScale(0.5f);
model.addControl(character);
character.setPhysicsLocation(new Vector3f(-140, 10, -10));
rootNode.attachChild(model);
getPhysicsSpace().add(character);
}
private void setupChaseCamera() {
flyCam.setEnabled(false);
chaseCam = new PhysicalAutoRotateCamera(cam,model,inputManager,
bulletAppState.getPhysicsSpace());
/
chaseCam = new ChaseCamera(cam, model, inputManager);
chaseCam.setSmoothMotion(true);
chaseCam.setChasingSensitivity(1f);
chaseCam.setTrailingEnabled(false);
*
*/
}
private void setupAnimationController() {
animationControl = model.getControl(AnimControl.class);
animationControl.addListener(this);
animationChannel = animationControl.createChannel();
shootingChannel = animationControl.createChannel();
shootingChannel.addBone(animationControl.getSkeleton().getBone(âuparm.rightâ));
shootingChannel.addBone(animationControl.getSkeleton().getBone(âarm.rightâ));
shootingChannel.addBone(animationControl.getSkeleton().getBone(âhand.rightâ));
}
@Override
public void simpleUpdate(float tpf) {
System.out.println(tpf);
Vector3f viewDir = character.getViewDirection().normalize();
Vector3f leftDir = YAW090.mult(viewDir).normalize();
viewDir.y = 0;
walkDirection.set(0, 0, 0);
if (left) {
if (!strafeEnabled) {
ROT_LEFT.multLocal(viewDir);
} else {
walkDirection.addLocal(leftDir.mult(0.2f));
}
}
if (right) {
if (!strafeEnabled) {
ROT_RIGHT.multLocal(viewDir);
} else {
walkDirection.addLocal(leftDir.negate().mult(0.2f));
}
}
if (up) {
walkDirection.addLocal(viewDir.mult(0.2f));
//character.getControllerId().setVelocityForTimeInterval(new javax.vecmath.Vector3f(0,0.5f,0), 1000f);
}
if (down) {
walkDirection.addLocal(viewDir.negate().mult(0.1f));
}
if (!character.onGround()) {
airTime = airTime + tpf;
} else {
airTime = 0;
}
if (walkDirection.length() == 0) {
if (!âstandâ.equals(animationChannel.getAnimationName())) {
animationChannel.setAnim(âstandâ, 1f);
}
} else {
if (airTime > .3f) {
if (!âstandâ.equals(animationChannel.getAnimationName())) {
animationChannel.setAnim(âstandâ);
}
} else if (!âWalkâ.equals(animationChannel.getAnimationName())) {
animationChannel.setAnim(âWalkâ, 0.7f);
}
}
character.setViewDirection(viewDir);
character.setWalkDirection(walkDirection);
}
public void onAction(String binding, boolean value, float tpf) {
if (binding.equals(âCharLeftâ)) {
if (value) {
left = true;
} else {
left = false;
}
} else if (binding.equals(âCharRightâ)) {
if (value) {
right = true;
} else {
right = false;
}
} else if (binding.equals(âCharUpâ)) {
if (value) {
up = true;
} else {
up = false;
}
} else if (binding.equals(âCharDownâ)) {
if (value) {
down = true;
} else {
down = false;
}
} else if (binding.equals(âCharSpaceâ)) {
character.jump();
} else if (binding.equals(âCharShootâ) && !value) {
bulletControl();
} else if (binding.equals(âCharStrafeâ)) {
if (value) {
strafeEnabled = true;
} else {
strafeEnabled = false;
}
}
}
private void bulletControl() {
shootingChannel.setAnim(âDodgeâ, 0.1f);
shootingChannel.setLoopMode(LoopMode.DontLoop);
Geometry bulletg = new Geometry(âbulletâ, bullet);
bulletg.setMaterial(matBullet);
bulletg.setShadowMode(ShadowMode.CastAndReceive);
bulletg.setLocalTranslation(character.getPhysicsLocation().add(cam.getDirection().mult(2)));
RigidBodyControl bulletControl = new BombControl(bulletCollisionShape, 1);
bulletControl.setCcdMotionThreshold(0.1f);
bulletControl.setLinearVelocity(cam.getDirection().mult(80));
bulletg.addControl(bulletControl);
rootNode.attachChild(bulletg);
getPhysicsSpace().add(bulletControl);
}
public void collision(PhysicsCollisionEvent event) {
if (event.getObjectA() instanceof BombControl) {
final Spatial node = event.getNodeA();
effect.killAllParticles();
effect.setLocalTranslation(node.getLocalTranslation());
effect.emitAllParticles();
} else if (event.getObjectB() instanceof BombControl) {
final Spatial node = event.getNodeB();
effect.killAllParticles();
effect.setLocalTranslation(node.getLocalTranslation());
effect.emitAllParticles();
}
}
public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
if (channel == shootingChannel) {
channel.setAnim(âstandâ);
}
}
public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
}
}
[/java]
________________________________________________________________________________________________________
AutoRotateCamera.java
________________________________________________________________________________________________________
[java collapse=âtrueâ]
/**
- Copyright © 2009-2010 jMonkeyEngine
- All rights reserved.
*
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
*
-
- Redistributions of source code must retain the above copyright
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
*
-
- Redistributions in binary form must reproduce the above copyright
- Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
*
-
- Neither the name of âjMonkeyEngineâ nor the names of its contributors
- Neither the name of âjMonkeyEngineâ nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
*
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package mygame;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.PhysicsControl;
import com.jme3.bullet.control.VehicleControl;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control;
import java.io.IOException;
/**
- A camera that follows a spatial and can turn around it by dragging the mouse
-
@author nehon
*/
public class AutoRotateCamera implements ActionListener, AnalogListener, Control {
private InputManager inputManager;
private Camera cam = null;
private Spatial target = null;
private Vector3f initialUpVec;
private PhysicsControl playerControl = null;
private float minVerticalRotation = 0.00f;
private float maxVerticalRotation = FastMath.PI / 2;
private float minDistance = 1.0f;
private float maxDistance = 40.0f;
private float zoomSpeed = 2f;
private float rotationSpeed = 1.0f;
private float camRotation = 0;
private float camVRotation = FastMath.PI / 6;
private float camDistance = 20;
private Quaternion qCamRotation = Quaternion.IDENTITY;
private Vector3f rotatedCamDir = Vector3f.ZERO;
private Vector3f playerDir = Vector3f.ZERO;
private Vector3f camLoc = Vector3f.ZERO;
private boolean canRotate;
private boolean enabled = true;
private enum possibleControl {CharacterControl,VehicleControl};
private possibleControl selectedControl = null;
/**
- Constructs the chase camera
-
@param cam the application camera
-
@param target the spatial to follow
*/
public AutoRotateCamera(Camera cam, final Spatial target) {
this.setSpatial(target);
this.cam = cam;
initialUpVec = cam.getUp().clone();
computePosition();
target.addControl(this);
cam.setLocation(camLoc);
}
/**
- Constructs the chase camera, and registers inputs
-
@param cam the application camera
-
@param target the spatial to follow
-
@param inputManager the inputManager of the application to register inputs
*/
public AutoRotateCamera(Camera cam, final Spatial target, InputManager inputManager) {
this(cam, target);
registerWithInput(inputManager);
}
public void onAction(String name, boolean keyPressed, float tpf) {
if (name.equals("camToggleRotate") && enabled) {
if (keyPressed) {
canRotate = true;
inputManager.setCursorVisible(false);
} else {
canRotate = false;
inputManager.setCursorVisible(true);
}
}
}
//change Mouse Axis here (swap the negations)
public void onAnalog(String name, float value, float tpf) {
if (name.equals("camMouseLeft")) {
rotateCamera(value);
} else if (name.equals("camMouseRight")) {
rotateCamera(-value);
} else if (name.equals("camUp")) {
vRotateCamera(value);
} else if (name.equals("camDown")) {
vRotateCamera(-value);
} else if (name.equals("camZoomIn")) {
zoomCamera(value);
} else if (name.equals("camZoomOut")) {
zoomCamera(-value);
}
}
/**
- Registers inputs with the input manager
-
@param inputManager
*/
public void registerWithInput(InputManager inputManager) {
String[] inputs = {"camToggleRotate", "camDown", "camUp", "camMouseLeft", "camMouseRight", "camZoomIn", "camZoomOut"};
this.inputManager = inputManager;
inputManager.addMapping("camDown", new MouseAxisTrigger(1, true));
inputManager.addMapping("camUp", new MouseAxisTrigger(1, false));
inputManager.addMapping("camZoomIn", new MouseAxisTrigger(2, true));
inputManager.addMapping("camZoomOut", new MouseAxisTrigger(2, false));
inputManager.addMapping("camMouseLeft", new MouseAxisTrigger(0, true));
inputManager.addMapping("camMouseRight", new MouseAxisTrigger(0, false));
inputManager.addMapping("camToggleRotate", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addMapping("camToggleRotate", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
inputManager.addListener(this, inputs);
}
//computes the position of the camera
private void computePosition() {
qCamRotation = qCamRotation.fromAngleNormalAxis(camRotation, initialUpVec);
if (playerDir != null) {
rotatedCamDir = qCamRotation.mult(playerDir).normalize();
} else {
rotatedCamDir = qCamRotation.mult(Vector3f.UNIT_XYZ);
rotatedCamDir.y = 0;
rotatedCamDir.normalizeLocal();
rotatedCamDir.y = 0;
}
rotatedCamDir.addLocal(0, FastMath.sin(camVRotation), 0);
camLoc = rotatedCamDir.mult(camDistance);
camLoc.addLocal(target.getWorldTranslation());
}
//rotate the camera around the target on the horizontal plane
private void rotateCamera(float value) {
if (!canRotate || !enabled) {
return;
}
camRotation += value * rotationSpeed;
}
//move the camera toward or away the target
private void zoomCamera(float value) {
if (!enabled) {
return;
}
camDistance += value * zoomSpeed;
if (camDistance > maxDistance) {
camDistance = maxDistance;
}
if (camDistance < minDistance) {
camDistance = minDistance;
}
if ((camVRotation (minDistance + 1.0f))) {
camVRotation = minVerticalRotation;
}
}
//rotate the camera around the target on the vertical plane
private void vRotateCamera(float value) {
if (!canRotate || !enabled) {
return;
}
camVRotation += value * rotationSpeed;
if (camVRotation > maxVerticalRotation) {
camVRotation = maxVerticalRotation;
}
if ((camVRotation (minDistance + 1.0f))) {
camVRotation = minVerticalRotation;
}
}
/**
- Updates the camera, should only be called internally
*/
protected void updateCamera(float tpf) {
if (enabled) {
playerDir=getViewDirection();
computePosition();
cam.setLocation(camLoc);
cam.lookAt(target.getWorldTranslation(), initialUpVec);
}
}
/**
- Return the enabled/disabled state of the camera
-
@return true if the camera is enabled
*/
public boolean isEnabled() {
return enabled;
}
/**
- Enable or disable the camera
-
@param enabled true to enable
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
if (!enabled) {
canRotate = false; // reset this flag in-case it was on before
}
}
/**
- Returns the max zoom distance of the camera (default is 40)
-
@return maxDistance
*/
public float getMaxDistance() {
return maxDistance;
}
/**
- Sets the max zoom distance of the camera (default is 40)
-
@param maxDistance
*/
public void setMaxDistance(float maxDistance) {
this.maxDistance = maxDistance;
}
/**
- Returns the min zoom distance of the camera (default is 1)
-
@return minDistance
*/
public float getMinDistance() {
return minDistance;
}
/**
- Sets the min zoom distance of the camera (default is 1)
-
@return minDistance
*/
public void setMinDistance(float minDistance) {
this.minDistance = minDistance;
}
/**
- clone this camera for a spatial
-
@param spatial
-
@return
*/
public Control cloneForSpatial(Spatial spatial) {
AutoRotateCamera cc = new AutoRotateCamera(cam, spatial, inputManager);
cc.setMaxDistance(getMaxDistance());
cc.setMinDistance(getMinDistance());
return cc;
}
/**
- Sets the spacial for the camera control, should only be used internally
-
@param spatial
*/
public void setSpatial(Spatial spatial) {
target = spatial;
selectedControl = null;
VehicleControl vehicleControl = target.getControl(VehicleControl.class);
if (vehicleControl != null) {
this.playerControl = vehicleControl;
this.selectedControl = possibleControl.VehicleControl;
}
CharacterControl characterControl = target.getControl(CharacterControl.class);
if (characterControl != null) {
this.playerControl = characterControl;
this.selectedControl = possibleControl.CharacterControl;
}
}
private Vector3f getViewDirection() {
if (this.selectedControl==possibleControl.CharacterControl) {
return ((CharacterControl)playerControl).getViewDirection().clone();
}
if (this.selectedControl==possibleControl.VehicleControl) {
return ((VehicleControl)playerControl).getForwardVector(null).clone();
}
return null;
}
/**
- update the camera control, should on ly be used internally
-
@param tpf
*/
public void update(float tpf) {
updateCamera(tpf);
}
/**
- renders the camera control, should on ly be used internally
-
@param rm
-
@param vp
*/
public void render(RenderManager rm, ViewPort vp) {
//nothing to render
}
/**
- Write the camera
-
@param ex the exporter
-
@throws IOException
*/
public void write(JmeExporter ex) throws IOException {
OutputCapsule capsule = ex.getCapsule(this);
capsule.write(maxDistance, "maxDistance", 40);
capsule.write(minDistance, "minDistance", 1);
}
/**
- Read the camera
-
@param im
-
@throws IOException
*/
public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this);
maxDistance = ic.readFloat("maxDistance", 40);
minDistance = ic.readFloat("minDistance", 1);
}
/**
*
-
@deprecated use getMaxVerticalRotation()
*/
@Deprecated
public float getMaxHeight() {
return getMaxVerticalRotation();
}
/**
*
-
@deprecated use setMaxVerticalRotation()
*/
@Deprecated
public void setMaxHeight(float maxHeight) {
setMaxVerticalRotation(maxHeight);
}
/**
*
-
@deprecated use getMinVerticalRotation()
*/
@Deprecated
public float getMinHeight() {
return getMinVerticalRotation();
}
/**
*
-
@deprecated use setMinVerticalRotation()
*/
@Deprecated
public void setMinHeight(float minHeight) {
setMinVerticalRotation(minHeight);
}
/**
- returns the maximal vertical rotation angle of the camera around the target
-
@return
*/
public float getMaxVerticalRotation() {
return maxVerticalRotation;
}
/**
- sets the maximal vertical rotation angle of the camera around the target default is Pi/2;
-
@param maxVerticalRotation
*/
public void setMaxVerticalRotation(float maxVerticalRotation) {
this.maxVerticalRotation = maxVerticalRotation;
}
/**
- returns the minimal vertical rotation angle of the camera around the target
-
@return
*/
public float getMinVerticalRotation() {
return minVerticalRotation;
}
/**
- sets the minimal vertical rotation angle of the camera around the target default is 0;
-
@param minHeight
*/
public void setMinVerticalRotation(float minHeight) {
this.minVerticalRotation = minHeight;
}
/**
- Sets the default distance at start of applicaiton
-
@param defaultDistance
*/
public void setDefaultDistance(float defaultDistance) {
camDistance = defaultDistance;
}
/**
- sets the default horizontal rotation of the camera at start of the application
-
@param angle
*/
public void setDefaultHorizontalRotation(float angle) {
camRotation = angle;
}
/**
- sets the default vertical rotation of the camera at start of the application
-
@param angle
*/
public void setDefaultVerticalRotation(float angle) {
camVRotation = angle;
}
}
[/java]
_________________________________________________________________________________________________________
PhysicalAutoRotateCamera.java
_________________________________________________________________________________________________________
[java collapse=âtrueâ]
/**
- Copyright © 2009-2010 jMonkeyEngine
- All rights reserved.
*
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
*
-
- Redistributions of source code must retain the above copyright
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
*
-
- Redistributions in binary form must reproduce the above copyright
- Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
*
-
- Neither the name of âjMonkeyEngineâ nor the names of its contributors
- Neither the name of âjMonkeyEngineâ nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
*
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsRayTestResult;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.PhysicsControl;
import com.jme3.bullet.control.VehicleControl;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.input.InputManager;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control;
import java.io.IOException;
import java.util.LinkedList;
/**
- A camera that follows a spatial and can turn around it by dragging the mouse
-
@author nehon (ChaseCamera), Mark (PhysicalChaseCamera), nego (adaptation)
*
*/
public class PhysicalAutoRotateCamera implements ActionListener, AnalogListener, Control {
private InputManager inputManager;
private Camera cam = null;
private Spatial target = null;
private Vector3f initialUpVec;
private PhysicsControl playerControl = null;
private PhysicsSpace physicsSpace;
private float minVerticalRotation = 0.00f;
private float maxVerticalRotation = FastMath.PI / 2;
private float minDistance = 1.0f;
private float maxDistance = 40.0f;
private float zoomSpeed = 2f;
private float rotationSpeed = 1.0f;
private float camRotation = 0;
private float camVRotation = FastMath.PI / 6;
private float camDistance = 20;
private Quaternion qCamRotation = Quaternion.IDENTITY;
private Vector3f rotatedCamDir = Vector3f.ZERO;
private Vector3f playerDir = Vector3f.ZERO;
private Vector3f camLoc = Vector3f.ZERO;
private boolean canRotate;
private boolean enabled = true;
private enum possibleControl {CharacterControl,VehicleControl};
private possibleControl selectedControl = null;
private LinkedList<PhysicsRayTestResult> collisionResults = null;
private float noCollisionDistance = 0f;
private Vector3f noCollisionLoc = Vector3f.ZERO;
/**
- Constructs the chase camera
-
@param cam the application camera
-
@param target the spatial to follow
*/
public PhysicalAutoRotateCamera(Camera cam, final Spatial target) {
this.setSpatial(target);
this.cam = cam;
initialUpVec = cam.getUp().clone();
computeCamPosition();
target.addControl(this);
cam.setLocation(camLoc);
}
/**
- Constructs the chase camera, and registers inputs
-
@param cam the application camera
-
@param target the spatial to follow
-
@param inputManager the inputManager of the application to register inputs
*/
public PhysicalAutoRotateCamera(Camera cam, final Spatial target, InputManager inputManager) {
this(cam, target);
registerWithInput(inputManager);
}
/**
- Constructs the chase camera, and registers inputs and physicspace
-
@param cam the application camera
-
@param target the spatial to follow
-
@param inputManager the inputManager of the application to register inputs
-
@param physicsSpace the PhysicsSpace to be added for Collision testing
*/
public PhysicalAutoRotateCamera(Camera cam, final Spatial target,
InputManager inputManager, PhysicsSpace physicsSpace) {
this(cam, target, inputManager);
this.physicsSpace=physicsSpace;
}
public void onAction(String name, boolean keyPressed, float tpf) {
if (name.equals("camToggleRotate") && enabled) {
if (keyPressed) {
canRotate = true;
inputManager.setCursorVisible(false);
} else {
canRotate = false;
inputManager.setCursorVisible(true);
}
}
}
//change Mouse Axis here (swap the negations)
public void onAnalog(String name, float value, float tpf) {
if (name.equals("camMouseLeft")) {
rotateCamera(value);
} else if (name.equals("camMouseRight")) {
rotateCamera(-value);
} else if (name.equals("camUp")) {
vRotateCamera(value);
} else if (name.equals("camDown")) {
vRotateCamera(-value);
} else if (name.equals("camZoomIn")) {
zoomCamera(value);
} else if (name.equals("camZoomOut")) {
zoomCamera(-value);
}
}
/**
- Registers inputs with the input manager
-
@param inputManager
*/
public void registerWithInput(InputManager inputManager) {
String[] inputs = {"camToggleRotate", "camDown", "camUp", "camMouseLeft", "camMouseRight", "camZoomIn", "camZoomOut"};
this.inputManager = inputManager;
inputManager.addMapping("camDown", new MouseAxisTrigger(1, true));
inputManager.addMapping("camUp", new MouseAxisTrigger(1, false));
inputManager.addMapping("camZoomIn", new MouseAxisTrigger(2, true));
inputManager.addMapping("camZoomOut", new MouseAxisTrigger(2, false));
inputManager.addMapping("camMouseLeft", new MouseAxisTrigger(0, true));
inputManager.addMapping("camMouseRight", new MouseAxisTrigger(0, false));
inputManager.addMapping("camToggleRotate", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addMapping("camToggleRotate", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
inputManager.addListener(this, inputs);
}
//computes the rotation of the relative camera, based on target rotation and
//camera rotation
private void computeRotation() {
qCamRotation = qCamRotation.fromAngleNormalAxis(camRotation, initialUpVec);
if (playerDir != null) {
rotatedCamDir = qCamRotation.mult(playerDir).normalize();
} else {
rotatedCamDir = qCamRotation.mult(Vector3f.UNIT_XYZ);
rotatedCamDir.y = 0;
rotatedCamDir.normalizeLocal();
rotatedCamDir.y = 0;
}
rotatedCamDir.addLocal(0, FastMath.sin(camVRotation), 0);
}
//computes the position of the camera
private void computeCamPosition() {
camLoc = rotatedCamDir.mult(camDistance);
camLoc.addLocal(target.getWorldTranslation());
}
//computes the position of the camera with no collision
private void computeNoCollisionPosition() {
noCollisionLoc = rotatedCamDir.mult(noCollisionDistance);
noCollisionLoc.addLocal(target.getWorldTranslation());
}
//check for collisions
private void checkCollisions() {
if (physicsSpace!=null) {
collisionResults = (LinkedList) physicsSpace.rayTest(target.getWorldTranslation(), camLoc);
float hitFraction = 1f;
if (collisionResults != null && collisionResults.size() > 0) {
hitFraction = collisionResults.getFirst().getHitFraction();
noCollisionDistance = ((float) ((int) (hitFraction * 100))) / 100 * camDistance;
} else {
noCollisionDistance = camDistance;
}
} else {
noCollisionDistance = camDistance;
}
}
//rotate the camera around the target on the horizontal plane
private void rotateCamera(float value) {
if (!canRotate || !enabled) {
return;
}
camRotation += value * rotationSpeed;
}
//move the camera toward or away the target
private void zoomCamera(float value) {
if (!enabled) {
return;
}
camDistance += value * zoomSpeed;
if (camDistance > maxDistance) {
camDistance = maxDistance;
}
if (camDistance < minDistance) {
camDistance = minDistance;
}
if ((camVRotation < minVerticalRotation) && (camDistance > (minDistance + 1.0f))) {
camVRotation = minVerticalRotation;
}
}
//rotate the camera around the target on the vertical plane
private void vRotateCamera(float value) {
if (!canRotate || !enabled) {
return;
}
camVRotation += value * rotationSpeed;
if (camVRotation > maxVerticalRotation) {
camVRotation = maxVerticalRotation;
}
if ((camVRotation < minVerticalRotation) && (camDistance > (minDistance + 1.0f))) {
camVRotation = minVerticalRotation;
}
}
/**
- Updates the camera, should only be called internally
*/
protected void updateCamera(float tpf) {
if (enabled) {
playerDir=getViewDirection();
playerDir.y=0;
computeRotation();
computeCamPosition();
checkCollisions();
computeNoCollisionPosition();
cam.setLocation(noCollisionLoc);
cam.lookAt(target.getWorldTranslation(), initialUpVec);
}
}
/**
- Return the enabled/disabled state of the camera
-
@return true if the camera is enabled
*/
public boolean isEnabled() {
return enabled;
}
/**
- Enable or disable the camera
-
@param enabled true to enable
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
if (!enabled) {
canRotate = false; // reset this flag in-case it was on before
}
}
/**
- Returns the max zoom distance of the camera (default is 40)
-
@return maxDistance
*/
public float getMaxDistance() {
return maxDistance;
}
/**
- Sets the max zoom distance of the camera (default is 40)
-
@param maxDistance
*/
public void setMaxDistance(float maxDistance) {
this.maxDistance = maxDistance;
}
/**
- Returns the min zoom distance of the camera (default is 1)
-
@return minDistance
*/
public float getMinDistance() {
return minDistance;
}
/**
- Sets the min zoom distance of the camera (default is 1)
-
@return minDistance
*/
public void setMinDistance(float minDistance) {
this.minDistance = minDistance;
}
/**
- clone this camera for a spatial
-
@param spatial
-
@return
*/
public Control cloneForSpatial(Spatial spatial) {
PhysicalAutoRotateCamera cc = new PhysicalAutoRotateCamera(cam, spatial, inputManager);
cc.setMaxDistance(getMaxDistance());
cc.setMinDistance(getMinDistance());
return cc;
}
/**
- Sets the spacial for the camera control, should only be used internally
-
@param spatial
*/
public void setSpatial(Spatial spatial) {
target = spatial;
selectedControl = null;
VehicleControl vehicleControl = target.getControl(VehicleControl.class);
if (vehicleControl != null) {
this.playerControl = vehicleControl;
this.selectedControl = possibleControl.VehicleControl;
}
CharacterControl characterControl = target.getControl(CharacterControl.class);
if (characterControl != null) {
this.playerControl = characterControl;
this.selectedControl = possibleControl.CharacterControl;
}
}
private Vector3f getViewDirection() {
if (this.selectedControl==possibleControl.CharacterControl) {
return ((CharacterControl)playerControl).getViewDirection().clone();
}
if (this.selectedControl==possibleControl.VehicleControl) {
return ((VehicleControl)playerControl).getForwardVector(null).clone();
}
return null;
}
/**
- update the camera control, should on ly be used internally
-
@param tpf
*/
public void update(float tpf) {
updateCamera(tpf);
}
/**
- renders the camera control, should on ly be used internally
-
@param rm
-
@param vp
*/
public void render(RenderManager rm, ViewPort vp) {
//nothing to render
}
/**
- Write the camera
-
@param ex the exporter
-
@throws IOException
*/
public void write(JmeExporter ex) throws IOException {
OutputCapsule capsule = ex.getCapsule(this);
capsule.write(maxDistance, "maxDistance", 40);
capsule.write(minDistance, "minDistance", 1);
}
/**
- Read the camera
-
@param im
-
@throws IOException
*/
public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this);
maxDistance = ic.readFloat("maxDistance", 40);
minDistance = ic.readFloat("minDistance", 1);
}
/**
*
-
@deprecated use getMaxVerticalRotation()
*/
@Deprecated
public float getMaxHeight() {
return getMaxVerticalRotation();
}
/**
*
-
@deprecated use setMaxVerticalRotation()
*/
@Deprecated
public void setMaxHeight(float maxHeight) {
setMaxVerticalRotation(maxHeight);
}
/**
*
-
@deprecated use getMinVerticalRotation()
*/
@Deprecated
public float getMinHeight() {
return getMinVerticalRotation();
}
/**
*
-
@deprecated use setMinVerticalRotation()
*/
@Deprecated
public void setMinHeight(float minHeight) {
setMinVerticalRotation(minHeight);
}
/**
- returns the maximal vertical rotation angle of the camera around the target
-
@return
*/
public float getMaxVerticalRotation() {
return maxVerticalRotation;
}
/**
- sets the maximal vertical rotation angle of the camera around the target default is Pi/2;
-
@param maxVerticalRotation
*/
public void setMaxVerticalRotation(float maxVerticalRotation) {
this.maxVerticalRotation = maxVerticalRotation;
}
/**
- returns the minimal vertical rotation angle of the camera around the target
-
@return
*/
public float getMinVerticalRotation() {
return minVerticalRotation;
}
/**
- sets the minimal vertical rotation angle of the camera around the target default is 0;
-
@param minHeight
*/
public void setMinVerticalRotation(float minHeight) {
this.minVerticalRotation = minHeight;
}
/**
- Sets the default distance at start of applicaiton
-
@param defaultDistance
*/
public void setDefaultDistance(float defaultDistance) {
camDistance = defaultDistance;
}
/**
- sets the default horizontal rotation of the camera at start of the application
-
@param angle
*/
public void setDefaultHorizontalRotation(float angle) {
camRotation = angle;
}
/**
- sets the default vertical rotation of the camera at start of the application
-
@param angle
*/
public void setDefaultVerticalRotation(float angle) {
camVRotation = angle;
}
}
[/java]