Auto Zoom on Chase Camera

I would like to setup my Chase Camera to automatically / programmatically zoom in and out based on whether or not there is something inbetween the camera and the spatial. This way, if the character walks to a place where a wall is between the camera and the spatial, the camera will zoom in until it has a clear view of the spatial again.



Is there a way to programatically zoom in?



Do you think this the right way to deal with this situation or does someone have a better way?

i guess you could play with the setDefaultDistance, setMaxDistance methods.



Or you can extends the chase cam behavior

“Correct” chase cam behaviour is hard, since you also get situations such as someone standing right next to a wall with their back to it. If you just soom in then you get a view of nothing but their back…

1 Like

I agree. I wasn’t sure what else to do. This is my first atttempt at a 3rd person game (still relatively new to 3D games). Maybe a better thing to do is rotate vertically up instead or maybe even a combination?

@nehon

Would it be a possible to change the zoomin routine to protected instead of private so if I extend ChaseCam I could call the zoomin routine instead of only using mouse triggers? I doing this on Android so I don’t have a lot of key or mouse options and I’d like the control to do it programmatically. If not, then I’ll try dynamically getting the distancetotarget and adjusting the defaultdistance.

this should help

http://hub.jmonkeyengine.org/groups/physics/forum/topic/zelda-camera-collision-and-rotation/

2 Likes

@wezrule that thread was very helpful. Thanks.

@iwgeric

It’s done in last svn revision.

No more private attribute or method.

@nehon said:
@iwgeric
It's done in last svn revision.
No more private attribute or method.


I must say that puting private members in the chase cam was something I did't understand. I only set things to private when I'm sure that the logic behind it it's exclusive to that class, and no sub-classes should even see that (wich is kinda hard to guess).
@shirkit said:
I must say that puting private members in the chase cam was something I did't understand. I only set things to private when I'm sure that the logic behind it it's exclusive to that class, and no sub-classes should even see that (wich is kinda hard to guess).

ChaseCam was not meant for the core when it was designed, it was just for a project of mine. and there was no point i extend the chase cam a this time

Also letting everyone extend a core class can lead to disaster for maintenance.
In the case of the ChaseCam it's completely stand alone, and can be copied and re-implemented. Also i think this kind of control should be self-contained as it's far easier for maintenance.
There are reasons why the private modifier exists.
@shirkit said:
I must say that puting private members in the chase cam was something I did't understand. I only set things to private when I'm sure that the logic behind it it's exclusive to that class, and no sub-classes should even see that (wich is kinda hard to guess).


Effective Java (every experienced Java programmer should read that book - if you haven't read it then get it) has a really good section on this.

Essentially whenever you make a method in a class non-private then that becomes part of the interface of that class and you are committing to keeping the existence, behaviour, etc of that method the same in any future versions of that class you do. Any change you do to a non private method is effectively a change to your published API and would need to be documented and people informed of it as such.
1 Like

I slapped this together based on the work contributed by another member



[java]/*

  • 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
  • notice, this list of conditions and the following disclaimer.

    *
    • 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
  • 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 hks.input;



    import com.jme3.bullet.PhysicsSpace;

    import com.jme3.bullet.collision.PhysicsRayTestResult;

    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.input.controls.Trigger;

    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

    */

    public class ChaseCamera2 implements ActionListener, AnalogListener, Control {



    private Spatial target = null;

    private float minVerticalRotation = 0.00f;

    private float maxVerticalRotation = FastMath.PI / 2;

    private float minDistance = 1.0f;

    private float maxDistance = 40.0f;

    private float distance = 15.5f;

    private float maxDistanceByZoom = distance;

    private float zoomSpeed = 2f;

    private float rotationSpeed = 1.0f;

    private float rotation = 0;

    private float trailingRotationInertia = 0.05f;

    private float zoomSensitivity = 5f;

    private float rotationSensitivity = 5f;

    private float chasingSensitivity = 5f;

    private float trailingSensitivity = 0.5f;

    private float vRotation = FastMath.PI / 6;

    private boolean smoothMotion = false;

    private boolean trailingEnabled = true;

    private float rotationLerpFactor = 0;

    private float trailingLerpFactor = 0;

    private boolean rotating = false;

    private boolean vRotating = false;

    private float targetRotation = rotation;

    private InputManager inputManager;

    private Vector3f initialUpVec;

    private float targetVRotation = vRotation;

    private float vRotationLerpFactor = 0;

    private float targetDistance = distance;

    private float distanceLerpFactor = 0;

    private boolean zooming = false;

    private boolean trailing = false;

    private boolean chasing = false;

    private boolean canRotate;

    private float offsetDistance = 0.002f;

    private Vector3f prevPos;

    private boolean targetMoves = false;

    private boolean enabled = true;

    private Camera cam = null;

    private final Vector3f targetDir = new Vector3f();

    private float previousTargetRotation;

    private final Vector3f pos = new Vector3f();

    protected Vector3f targetLocation = new Vector3f(0, 0, 0);

    protected boolean dragToRotate = true;

    protected Vector3f lookAtOffset = new Vector3f(0, 0, 0);

    protected boolean leftClickRotate = true;

    protected boolean rightClickRotate = true;

    private Vector3f temp = new Vector3f(0, 0, 0);

    protected boolean invertYaxis = false;

    protected boolean invertXaxis = false;

    private final static String ChaseCamDown = "ChaseCamDown";

    private final static String ChaseCamUp = "ChaseCamUp";

    private final static String ChaseCamZoomIn = "ChaseCamZoomIn";

    private final static String ChaseCamZoomOut = "ChaseCamZoomOut";

    private final static String ChaseCamMoveLeft = "ChaseCamMoveLeft";

    private final static String ChaseCamMoveRight = "ChaseCamMoveRight";

    private final static String ChaseCamToggleRotate = "ChaseCamToggleRotate";

    private Vector3f maxPos = new Vector3f();

    private PhysicsSpace myPhysicsSpace;



    private Quaternion qCamRotation = Quaternion.IDENTITY;

    private Vector3f rotatedCamDir = Vector3f.ZERO;

    private Vector3f playerDir = Vector3f.ZERO;

    private float camRotation = 0;

    private float camVRotation = FastMath.PI / 6;

    /**
  • Constructs the chase camera
  • @param cam the application camera
  • @param target the spatial to follow

    */

    public ChaseCamera2(Camera cam, final Spatial target) {

    this(cam);

    target.addControl(this);

    }



    /**
  • Constructs the chase camera
  • if you use this constructor you have to attach the cam later to a spatial
  • doing spatial.addControl(chaseCamera);
  • @param cam the application camera

    */

    public ChaseCamera2(Camera cam) {

    this.cam = cam;

    initialUpVec = cam.getUp().clone();

    }



    /**
  • 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);
  • @param cam the application camera
  • @param inputManager the inputManager of the application to register inputs

    */

    public ChaseCamera2(Camera cam, InputManager inputManager) {

    this(cam);

    registerWithInput(inputManager);

    }



    /**
  • 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 ChaseCamera2(Camera cam, final Spatial target, InputManager inputManager) {

    this(cam, target);

    registerWithInput(inputManager);

    }



    /**
  • Constructs the physical chase camera, registers inputs and physics
  • @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, the cam shall be active in

    */

    public ChaseCamera2(Camera cam, final Spatial target,

    InputManager inputManager, PhysicsSpace physicsSpace) {

    //chase cam constructor stuff

    this.target = target;

    this.cam = cam;

    initialUpVec = cam.getUp().clone();



    //add PhysicsNode to world physicsSpace

    myPhysicsSpace = physicsSpace;



    //chase cam stuff.

    computePosition();

    target.addControl(this);

    prevPos = new Vector3f(target.getWorldTranslation());

    cam.setLocation(pos);

    registerWithInput(inputManager);

    }





    public void onAction(String name, boolean keyPressed, float tpf) {

    if (dragToRotate) {

    if (name.equals(ChaseCamToggleRotate) && enabled) {

    if (keyPressed) {

    canRotate = true;

    inputManager.setCursorVisible(false);

    } else {

    canRotate = false;

    inputManager.setCursorVisible(true);

    }

    }

    }



    }

    private boolean zoomin;



    public void onAnalog(String name, float value, float tpf) {

    if (name.equals(ChaseCamMoveLeft)) {

    rotateCamera(-value);

    } else if (name.equals(ChaseCamMoveRight)) {

    rotateCamera(value);

    } else if (name.equals(ChaseCamUp)) {

    vRotateCamera(value);

    } else if (name.equals(ChaseCamDown)) {

    vRotateCamera(-value);

    } else if (name.equals(ChaseCamZoomIn)) {

    zoomCamera(-value);

    if (zoomin == false) {

    distanceLerpFactor = 0;

    }

    zoomin = true;

    } else if (name.equals(ChaseCamZoomOut)) {

    zoomCamera(+value);

    if (zoomin == true) {

    distanceLerpFactor = 0;

    }

    zoomin = false;

    }

    }



    /**
  • Registers inputs with the input manager
  • @param inputManager

    */

    public final void registerWithInput(InputManager inputManager) {



    String[] inputs = {ChaseCamToggleRotate,

    ChaseCamDown,

    ChaseCamUp,

    ChaseCamMoveLeft,

    ChaseCamMoveRight,

    ChaseCamZoomIn,

    ChaseCamZoomOut};



    this.inputManager = inputManager;

    if (!invertYaxis) {

    inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, true));

    inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, false));

    } else {

    inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, false));

    inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, true));

    }

    inputManager.addMapping(ChaseCamZoomIn, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));

    inputManager.addMapping(ChaseCamZoomOut, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true));

    if(!invertXaxis){

    inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, true));

    inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, false));

    }else{

    inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, false));

    inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, true));

    }

    inputManager.addMapping(ChaseCamToggleRotate, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));

    inputManager.addMapping(ChaseCamToggleRotate, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));



    inputManager.addListener(this, inputs);

    }



    /**
  • Sets custom triggers for toggleing the rotation of the cam
  • deafult are
  • new MouseButtonTrigger(MouseInput.BUTTON_LEFT) left mouse button
  • new MouseButtonTrigger(MouseInput.BUTTON_RIGHT) right mouse button
  • @param triggers

    */

    public void setToggleRotationTrigger(Trigger… triggers) {

    inputManager.deleteMapping(ChaseCamToggleRotate);

    inputManager.addMapping(ChaseCamToggleRotate, triggers);

    inputManager.addListener(this, ChaseCamToggleRotate);

    }



    /**
  • Sets custom triggers for zomming in the cam
  • default is
  • new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true) mouse wheel up
  • @param triggers

    */

    public void setZoomInTrigger(Trigger… triggers) {

    inputManager.deleteMapping(ChaseCamZoomIn);

    inputManager.addMapping(ChaseCamZoomIn, triggers);

    inputManager.addListener(this, ChaseCamZoomIn);

    }



    /**
  • Sets custom triggers for zomming out the cam
  • default is
  • new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false) mouse wheel down
  • @param triggers

    /

    public void setZoomOutTrigger(Trigger… triggers) {

    inputManager.deleteMapping(ChaseCamZoomOut);

    inputManager.addMapping(ChaseCamZoomOut, triggers);

    inputManager.addListener(this, ChaseCamZoomOut);

    }



    private void computePosition() {



    float hDistance = (distance) * FastMath.sin((FastMath.PI / 2) - vRotation);

    pos.set(hDistance * FastMath.cos(rotation), (distance) * FastMath.sin(vRotation), hDistance * FastMath.sin(rotation));

    pos.addLocal(target.getWorldTranslation());



    hDistance = (maxDistanceByZoom) * FastMath.sin((FastMath.PI / 2) - vRotation);

    maxPos = new Vector3f(hDistance * FastMath.cos(rotation), (maxDistanceByZoom) * FastMath.sin(vRotation), hDistance * FastMath.sin(rotation));

    maxPos = maxPos.add(target.getWorldTranslation());

    this.collide();

    }



    //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);

    }



    //rotate the camera around the target on the horizontal plane

    private void rotateCamera(float value) {

    if (!canRotate || !enabled) {

    return;

    }

    rotating = true;

    targetRotation += value * rotationSpeed;





    }



    //move the camera toward or away the target

    private void zoomCamera(float value) {

    if (!enabled) {

    return;

    }



    zooming = true;

    //targetDistance += value * zoomSpeed;

    maxDistanceByZoom += value * zoomSpeed;

    /if (targetDistance > maxDistance) {

    targetDistance = maxDistance;

    }

    if (targetDistance < minDistance) {

    targetDistance = minDistance;

    }
    /



    if (maxDistanceByZoom > maxDistance) {

    maxDistanceByZoom = maxDistance;

    }

    if (maxDistanceByZoom < minDistance) {

    maxDistanceByZoom = minDistance;

    }



    if ((targetVRotation < minVerticalRotation) /
    && (targetDistance > (minDistance + 1.0f))/) {

    targetVRotation = minVerticalRotation;

    }



    }



    //rotate the camera around the target on the vertical plane

    private void vRotateCamera(float value) {

    if (!canRotate || !enabled) {

    return;

    }

    vRotating = true;

    targetVRotation += value * rotationSpeed;

    if (targetVRotation > maxVerticalRotation) {

    targetVRotation = maxVerticalRotation;

    }

    if ((targetVRotation < minVerticalRotation) /
    && (targetDistance > (minDistance + 1.0f))*/) {

    targetVRotation = minVerticalRotation;

    }

    }



    /**
  • Updates the camera, should only be called internally

    */

    protected void updateCamera(float tpf) {

    if (enabled) {

    targetLocation.set(target.getWorldTranslation()).addLocal(lookAtOffset);

    if (smoothMotion) {



    //computation of target direction

    targetDir.set(targetLocation).subtractLocal(prevPos);

    float dist = targetDir.length();



    //Low pass filtering on the target postition to avoid shaking when physics are enabled.

    if (offsetDistance < dist) {

    //target moves, start chasing.

    chasing = true;

    //target moves, start trailing if it has to.

    if (trailingEnabled) {

    trailing = true;

    }

    //target moves…

    targetMoves = true;

    } else {

    //if target was moving, we compute a slight offset in rotation to avoid a rought stop of the cam

    //We do not if the player is rotationg the cam

    if (targetMoves && !canRotate) {

    if (targetRotation - rotation > trailingRotationInertia) {

    targetRotation = rotation + trailingRotationInertia;

    } else if (targetRotation - rotation < -trailingRotationInertia) {

    targetRotation = rotation - trailingRotationInertia;

    }

    }

    //Target stops

    targetMoves = false;

    }



    //the user is rotating the cam by dragging the mouse

    if (canRotate) {

    //reseting the trailing lerp factor

    trailingLerpFactor = 0;

    //stop trailing user has the control

    trailing = false;

    }





    if (trailingEnabled && trailing) {

    if (targetMoves) {

    //computation if the inverted direction of the target

    Vector3f a = targetDir.negate().normalizeLocal();

    //the x unit vector

    Vector3f b = Vector3f.UNIT_X;

    //2d is good enough

    a.y = 0;

    //computation of the rotation angle between the x axis and the trail

    if (targetDir.z > 0) {

    targetRotation = FastMath.TWO_PI - FastMath.acos(a.dot(b));

    } else {

    targetRotation = FastMath.acos(a.dot(b));

    }

    if (targetRotation - rotation > FastMath.PI || targetRotation - rotation < -FastMath.PI) {

    targetRotation -= FastMath.TWO_PI;

    }



    //if there is an important change in the direction while trailing reset of the lerp factor to avoid jumpy movements

    if (targetRotation != previousTargetRotation && FastMath.abs(targetRotation - previousTargetRotation) > FastMath.PI / 8) {

    trailingLerpFactor = 0;

    }

    previousTargetRotation = targetRotation;

    }

    //computing lerp factor

    trailingLerpFactor = Math.min(trailingLerpFactor + tpf * tpf * trailingSensitivity, 1);

    //computing rotation by linear interpolation

    rotation = FastMath.interpolateLinear(trailingLerpFactor, rotation, targetRotation);



    //if the rotation is near the target rotation we’re good, that’s over

    if (targetRotation + 0.01f >= rotation && targetRotation - 0.01f <= rotation) {

    trailing = false;

    trailingLerpFactor = 0;

    }

    }



    //linear interpolation of the distance while chasing

    if (chasing) {

    distance = temp.set(targetLocation).subtractLocal(cam.getLocation()).length();

    distanceLerpFactor = Math.min(distanceLerpFactor + (tpf * tpf * chasingSensitivity * 0.05f), 1);

    distance = FastMath.interpolateLinear(distanceLerpFactor, distance, targetDistance);

    if (targetDistance + 0.01f >= distance && targetDistance - 0.01f <= distance) {

    distanceLerpFactor = 0;

    chasing = false;

    }

    }



    //linear interpolation of the distance while zooming

    if (zooming) {

    distanceLerpFactor = Math.min(distanceLerpFactor + (tpf * tpf * zoomSensitivity), 1);

    distance = FastMath.interpolateLinear(distanceLerpFactor, distance, targetDistance);

    if (targetDistance + 0.1f >= distance && targetDistance - 0.1f <= distance) {

    zooming = false;

    distanceLerpFactor = 0;

    }

    }



    //linear interpolation of the rotation while rotating horizontally

    if (rotating) {

    rotationLerpFactor = Math.min(rotationLerpFactor + tpf * tpf * rotationSensitivity, 1);

    rotation = FastMath.interpolateLinear(rotationLerpFactor, rotation, targetRotation);

    if (targetRotation + 0.01f >= rotation && targetRotation - 0.01f <= rotation) {

    rotating = false;

    rotationLerpFactor = 0;

    }

    }



    //linear interpolation of the rotation while rotating vertically

    if (vRotating) {

    vRotationLerpFactor = Math.min(vRotationLerpFactor + tpf * tpf * rotationSensitivity, 1);

    vRotation = FastMath.interpolateLinear(vRotationLerpFactor, vRotation, targetVRotation);

    if (targetVRotation + 0.01f >= vRotation && targetVRotation - 0.01f <= vRotation) {

    vRotating = false;

    vRotationLerpFactor = 0;

    }

    }

    //computing the position

    computePosition();

    //setting the position at last

    cam.setLocation(pos.addLocal(lookAtOffset));

    } else {

    //easy no smooth motion

    vRotation = targetVRotation;

    rotation = targetRotation;

    distance = targetDistance;

    computePosition();

    cam.setLocation(pos.addLocal(lookAtOffset));

    }

    //keeping track on the previous position of the target

    prevPos.set(targetLocation);



    //the cam looks at the target

    cam.lookAt(targetLocation, 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) {

    ChaseCamera2 cc = new ChaseCamera2(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;

    if (spatial == null) {

    return;

    }

    computePosition();

    prevPos = new Vector3f(target.getWorldTranslation());

    cam.setLocation(pos);

    }



    /**
  • 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;

    }



    /**
  • returns true is smmoth motion is enabled for this chase camera
  • @return

    */

    public boolean isSmoothMotion() {

    return smoothMotion;

    }



    /**
  • Enables smooth motion for this chase camera
  • @param smoothMotion

    */

    public void setSmoothMotion(boolean smoothMotion) {

    this.smoothMotion = smoothMotion;

    }



    /**
  • returns the chasing sensitivity
  • @return

    */

    public float getChasingSensitivity() {

    return chasingSensitivity;

    }



    /**
  • Sets the chasing sensitivity, the lower the value the slower the camera will follow the target when it moves
  • @param chasingSensitivity

    */

    public void setChasingSensitivity(float chasingSensitivity) {

    this.chasingSensitivity = chasingSensitivity;

    }



    /**
  • Returns the rotation sensitivity
  • @return

    */

    public float getRotationSensitivity() {

    return rotationSensitivity;

    }



    /**
  • Sets the rotation sensitivity, the lower the value the slower the camera will rotates around the target when draging with the mouse
  • default is 5
  • @param rotationSensitivity

    */

    public void setRotationSensitivity(float rotationSensitivity) {

    this.rotationSensitivity = rotationSensitivity;

    }



    /**
  • returns true if the trailing is enabled
  • @return

    */

    public boolean isTrailingEnabled() {

    return trailingEnabled;

    }



    /**
  • Enable the camera trailing : The camera smoothly go in the targets trail when it moves.
  • @param trailingEnabled

    */

    public void setTrailingEnabled(boolean trailingEnabled) {

    this.trailingEnabled = trailingEnabled;

    }



    /**
  • returns the trailing rotation inertia
  • @return

    */

    public float getTrailingRotationInertia() {

    return trailingRotationInertia;

    }



    /**
  • Sets the trailing rotation inertia : default is 0.1. This prevent the camera to roughtly stop when the target stops moving
  • before the camera reached the trail position.
  • @param trailingRotationInertia

    */

    public void setTrailingRotationInertia(float trailingRotationInertia) {

    this.trailingRotationInertia = trailingRotationInertia;

    }



    /**
  • returns the trailing sensitivity
  • @return

    */

    public float getTrailingSensitivity() {

    return trailingSensitivity;

    }



    /**
  • Sets the trailing sensitivity, the lower the value, the slower the camera will go in the target trail when it moves.
  • default is 0.5;
  • @param trailingSensitivity

    */

    public void setTrailingSensitivity(float trailingSensitivity) {

    this.trailingSensitivity = trailingSensitivity;

    }



    /**
  • returns the zoom sensitivity
  • @return

    */

    public float getZoomSensitivity() {

    return zoomSensitivity;

    }



    /**
  • Sets the zoom sensitivity, the lower the value, the slower the camera will zoom in and out.
  • default is 5.
  • @param zoomSensitivity

    */

    public void setZoomSensitivity(float zoomSensitivity) {

    this.zoomSensitivity = zoomSensitivity;

    }



    /**
  • Sets the default distance at start of applicaiton
  • @param defaultDistance

    */

    public void setDefaultDistance(float defaultDistance) {

    distance = defaultDistance;

    targetDistance = distance;

    }



    /**
  • sets the default horizontal rotation of the camera at start of the application
  • @param angle

    */

    public void setDefaultHorizontalRotation(float angle) {

    rotation = angle;

    targetRotation = angle;

    }



    /**
  • sets the default vertical rotation of the camera at start of the application
  • @param angle

    */

    public void setDefaultVerticalRotation(float angle) {

    vRotation = angle;

    targetVRotation = angle;

    }



    /**
  • sets the current zoom distance for the chase camera
  • @param new distance

    */

    public void alterDistance(float alterBy) {

    this.zoomCamera(alterBy);

    }



    /**
  • @return If drag to rotate feature is enabled.

    *
  • @see FlyByCamera#setDragToRotate(boolean)

    */

    public boolean isDragToRotate() {

    return dragToRotate;

    }



    /**
  • @param dragToRotate When true, the user must hold the mouse button
  • and drag over the screen to rotate the camera, and the cursor is
  • visible until dragged. Otherwise, the cursor is invisible at all times
  • and holding the mouse button is not needed to rotate the camera.
  • This feature is disabled by default.

    */

    public void setDragToRotate(boolean dragToRotate) {

    this.dragToRotate = dragToRotate;

    this.canRotate = !dragToRotate;

    inputManager.setCursorVisible(dragToRotate);

    }



    /**
  • return the current distance from the camera to the target
  • @return

    */

    public float getDistanceToTarget() {

    return distance;

    }



    /**
  • returns the current horizontal rotation around the target in radians
  • @return

    */

    public float getHorizontalRotation() {

    return rotation;

    }



    /**
  • returns the current vertical rotation around the target in radians.
  • @return

    */

    public float getVerticalRotation() {

    return vRotation;

    }



    /**
  • returns the offset from the target’s position where the camera looks at
  • @return

    */

    public Vector3f getLookAtOffset() {

    return lookAtOffset;

    }



    /**
  • Sets the offset from the target’s position where the camera looks at
  • @param lookAtOffset

    */

    public void setLookAtOffset(Vector3f lookAtOffset) {

    this.lookAtOffset = lookAtOffset;

    }



    /**

    *
  • @param invertYaxis
  • @deprecated use setInvertVerticalAxis

    */

    @Deprecated

    public void setInvertYaxis(boolean invertYaxis) {

    setInvertVerticalAxis(invertYaxis);

    }



    /**
  • invert the vertical axis movement of the mouse
  • @param invertYaxis

    */

    public void setInvertVerticalAxis(boolean invertYaxis) {

    this.invertYaxis = invertYaxis;

    inputManager.deleteMapping(ChaseCamDown);

    inputManager.deleteMapping(ChaseCamUp);

    if (!invertYaxis) {

    inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, true));

    inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, false));

    } else {

    inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, false));

    inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, true));

    }

    inputManager.addListener(this, ChaseCamDown, ChaseCamUp);

    }



    /**
  • invert the Horizontal axis movement of the mouse
  • @param invertYaxis

    /

    public void setInvertHorizontalAxis(boolean invertXaxis) {

    this.invertXaxis = invertXaxis;

    inputManager.deleteMapping(ChaseCamMoveLeft);

    inputManager.deleteMapping(ChaseCamMoveRight);

    if(!invertXaxis){

    inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, true));

    inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, false));

    }else{

    inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, false));

    inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, true));

    }

    inputManager.addListener(this, ChaseCamMoveLeft, ChaseCamMoveRight);

    }



    public void collide() {

    LinkedList<PhysicsRayTestResult> testResults;

    testResults = (LinkedList) myPhysicsSpace.rayTest(target.getWorldTranslation(), maxPos.addLocal(lookAtOffset));

    float hitFraction = 1f;

    if(testResults != null && testResults.size() > 0) {

    hitFraction = testResults.getFirst().getHitFraction();

    }



    targetDistance = ((float)((int)(hitFraction
    100)))/100 * maxDistanceByZoom;

    }

    }

    [/java]











    and got this one which was “tuned” with rotations in mind, the authors are credited in the code

    [java]



    /**
  • 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
  • notice, this list of conditions and the following disclaimer.

    *
    • 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
  • 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 hks.input;

    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]