[SOLVED] ChaseCamera problem: Camera is twisting when the vehicle turns almost 360 degrees

I use ChaseCamera for vehicles. When I turn left or right far more than 360 degrees (back into the first place), suddenly the camera rotates 360 degrees quickly. Here is the ChaseCamera setting I used, with modifications of TestPhysicsCar.java:



[java]

package jme3test.bullet;



import com.jme3.app.SimpleApplication;

import com.jme3.bullet.BulletAppState;

import com.jme3.bullet.PhysicsSpace;

import com.jme3.bullet.collision.shapes.BoxCollisionShape;

import com.jme3.bullet.collision.shapes.CompoundCollisionShape;

import com.jme3.bullet.control.VehicleControl;

import com.jme3.input.ChaseCamera;

import com.jme3.input.KeyInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.FastMath;

import com.jme3.math.Matrix3f;

import com.jme3.math.Vector3f;

import com.jme3.scene.Geometry;

import com.jme3.scene.Node;

import com.jme3.scene.Spatial;

import com.jme3.scene.shape.Cylinder;



public class TestPhysicsCar extends SimpleApplication implements ActionListener {



private BulletAppState bulletAppState;

private VehicleControl vehicle;

private final float accelerationForce = 1000.0f;

private final float brakeForce = 100.0f;

private float steeringValue = 0;

private float accelerationValue = 0;

private Vector3f jumpForce = new Vector3f(0, 3000, 0);

private ChaseCamera chaseCam;

private Spatial camSpatial;



public static void main(String[] args) {

TestPhysicsCar app = new TestPhysicsCar();

app.start();

}



@Override

public void simpleInitApp() {

bulletAppState = new BulletAppState();

stateManager.attach(bulletAppState);

bulletAppState.getPhysicsSpace().enableDebug(assetManager);



PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());

setupKeys();

buildPlayer();

setupCamera();

}



private PhysicsSpace getPhysicsSpace() {

return bulletAppState.getPhysicsSpace();

}



private void setupKeys() {

inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_H));

inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_K));

inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_U));

inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_J));

inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE));

inputManager.addMapping("Reset", new KeyTrigger(KeyInput.KEY_RETURN));

inputManager.addListener(this, "Lefts");

inputManager.addListener(this, "Rights");

inputManager.addListener(this, "Ups");

inputManager.addListener(this, "Downs");

inputManager.addListener(this, "Space");

inputManager.addListener(this, "Reset");

}



private void buildPlayer() {

Material mat = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");

mat.getAdditionalRenderState().setWireframe(true);

mat.setColor("Color", ColorRGBA.Red);



//create a compound shape and attach the BoxCollisionShape for the car body at 0,1,0

//this shifts the effective center of mass of the BoxCollisionShape to 0,-1,0

CompoundCollisionShape compoundShape = new CompoundCollisionShape();

BoxCollisionShape box = new BoxCollisionShape(new Vector3f(1.2f, 0.5f, 2.4f));

compoundShape.addChildShape(box, new Vector3f(0, 1, 0));



//create vehicle node

Node vehicleNode = new Node("vehicleNode");

vehicle = new VehicleControl(compoundShape, 600);

vehicleNode.addControl(vehicle);



//setting suspension values for wheels, this can be a bit tricky

//see also https://docs.google.com/Doc?docid=0AXVUZ5xw6XpKZGNuZG56a3FfMzU0Z2NyZnF4Zmo&hl=en

float stiffness = 120.0f;//200=f1 car

float compValue = .3f; //(should be lower than damp)

float dampValue = .4f;

vehicle.setSuspensionCompression(compValue * 2.0f * FastMath.sqrt(stiffness));

vehicle.setSuspensionDamping(dampValue * 2.0f * FastMath.sqrt(stiffness));

vehicle.setSuspensionStiffness(stiffness);

vehicle.setMaxSuspensionForce(40000.0f);



//Create four wheels and add them at their locations

Vector3f wheelDirection = new Vector3f(0, -1, 0); // was 0, -1, 0

Vector3f wheelAxle = new Vector3f(-1, 0, 0); // was -1, 0, 0

float radius = 0.3f;

float restLength = 0.3f;

float yOff = 0.5f;

float xOff = 1f;

float zOff = 2f;



Cylinder wheelMesh = new Cylinder(16, 16, radius, radius * 0.6f, true);



Node node1 = new Node("wheel 1 node");

Geometry wheels1 = new Geometry("wheel 1", wheelMesh);

node1.attachChild(wheels1);

wheels1.rotate(0, FastMath.HALF_PI, 0);

wheels1.setMaterial(mat);

vehicle.addWheel(node1, new Vector3f(-xOff, yOff, zOff),

wheelDirection, wheelAxle, restLength, radius, true);



Node node2 = new Node("wheel 2 node");

Geometry wheels2 = new Geometry("wheel 2", wheelMesh);

node2.attachChild(wheels2);

wheels2.rotate(0, FastMath.HALF_PI, 0);

wheels2.setMaterial(mat);

vehicle.addWheel(node2, new Vector3f(xOff, yOff, zOff),

wheelDirection, wheelAxle, restLength, radius, true);



Node node3 = new Node("wheel 3 node");

Geometry wheels3 = new Geometry("wheel 3", wheelMesh);

node3.attachChild(wheels3);

wheels3.rotate(0, FastMath.HALF_PI, 0);

wheels3.setMaterial(mat);

vehicle.addWheel(node3, new Vector3f(-xOff, yOff, -zOff),

wheelDirection, wheelAxle, restLength, radius, false);



Node node4 = new Node("wheel 4 node");

Geometry wheels4 = new Geometry("wheel 4", wheelMesh);

node4.attachChild(wheels4);

wheels4.rotate(0, FastMath.HALF_PI, 0);

wheels4.setMaterial(mat);

vehicle.addWheel(node4, new Vector3f(xOff, yOff, -zOff),

wheelDirection, wheelAxle, restLength, radius, false);



vehicleNode.attachChild(node1);

vehicleNode.attachChild(node2);

vehicleNode.attachChild(node3);

vehicleNode.attachChild(node4);

rootNode.attachChild(vehicleNode);



getPhysicsSpace().add(vehicle);

camSpatial = vehicleNode.center();

}



@Override

public void simpleUpdate(float tpf) {

//cam.lookAt(vehicle.getPhysicsLocation(), Vector3f.UNIT_Y);

}



public void onAction(String binding, boolean value, float tpf) {

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

if (value) {

steeringValue += .5f;

} else {

steeringValue += -.5f;

}

vehicle.steer(steeringValue);

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

if (value) {

steeringValue += -.5f;

} else {

steeringValue += .5f;

}

vehicle.steer(steeringValue);

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

if (value) {

accelerationValue += accelerationForce;

} else {

accelerationValue -= accelerationForce;

}

vehicle.accelerate(accelerationValue);

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

if (value) {

vehicle.brake(brakeForce);

} else {

vehicle.brake(0f);

}

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

if (value) {

vehicle.applyImpulse(jumpForce, Vector3f.ZERO);

}

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

if (value) {

System.out.println("Reset");

vehicle.setPhysicsLocation(Vector3f.ZERO);

vehicle.setPhysicsRotation(new Matrix3f());

vehicle.setLinearVelocity(Vector3f.ZERO);

vehicle.setAngularVelocity(Vector3f.ZERO);

vehicle.resetSuspension();

} else {

}

}

}



private void setupCamera() {

flyCam.setEnabled(false);

Vector3f camOff = (new Vector3f(0f, -1.0f, 0f));

//cam.setDirection(new Vector3f(-1f, 0f, 0f));

cam.lookAtDirection(new Vector3f(-1f, 0f, 0f), new Vector3f(0, 0, 0));

chaseCam = new ChaseCamera(cam, camSpatial, inputManager);

chaseCam.setLookAtOffset(camOff);



chaseCam.setDefaultVerticalRotation(0.25f);

chaseCam.setMaxVerticalRotation(0.3f);

chaseCam.setTrailingSensitivity(25);

chaseCam.setMinVerticalRotation(0.2f);



chaseCam.setDefaultHorizontalRotation(0.1f);

chaseCam.setChasingSensitivity(25);

chaseCam.setSmoothMotion(true);



chaseCam.setMaxDistance(15);

chaseCam.setMinDistance(10);

chaseCam.setDefaultDistance(12.5f);

}

}

[/java]



Try to turn it slowly until you get back into place. Is this problem can be solved?

I have searched the forum and found this: http://hub.jmonkeyengine.org/groups/contribution-depot-jme3/forum/topic/fixing-chasecamera/

I think the post has the same problem, but when I apply it, the camera is still twisting.

I need to solve this camera problem because the game I made is a driving simulation game and use a circuit track. When the vehicle is almost complete the circuit, the camera suddenly turns around and this really bothers me.

What is the most suitable camera for racing car game?



Thank you all :D.

Finally i solved the problem…

i created a camera class that extends ChaseCamera class and override the updateCamera method. I modified the condition inside targetMoves :



[java]

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

if ((targetRotation<0 && previousTargetRotation>0))

rotation -= previousTargetRotation;

rotation -= previousTargetRotation;

trailingLerpFactor = 0;

}

[/java]



i don’t know how to explain this, but it work…

so far I’m getting the results I want.

1 Like

Could you post the entire class?

Here it is my chasecamera class:



[java]

import com.jme3.input.ChaseCamera;

import com.jme3.input.InputManager;

import com.jme3.math.FastMath;

import com.jme3.math.Vector3f;

import com.jme3.renderer.Camera;

import com.jme3.scene.Spatial;





public class CarCamera extends ChaseCamera{



float oldZ = 0;



public CarCamera(Camera cam, final Spatial target, InputManager inputManager) {

super(cam, target);

registerWithInput(inputManager);

}



@Override

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 ) {

if ((targetRotation<0 && previousTargetRotation>0))

rotation -= previousTargetRotation;

rotation -= previousTargetRotation;

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), 1f);

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



}

}

}

[/java]