I implemented a camera behavoir that I need. The camera will look at a point, rotate around it, and move in the direction that it’s facing (not including height). It works great! However, when moving forwards (in the facing direction), the camera never goes “spot on” in the right direction. It always strays a little to the right. This is really frustrating.
I’m using jPCT, and the coordinate system is as follows:
X-axis points to the right.
Y-axis points downwards.
Z-axis points into the screen (depth).
The camera behavoir is as follows.
package com.jd247.model;
import com.threed.jpct.Camera;
import com.threed.jpct.SimpleVector;
/**
* Utility used to view a World. This object can transform a Camera with the {@link #apply(Camera)} function.
* This has the limitation of only being able to look at the World in a 45 degree angle.
* Its coordinates represents the center of its view, and not it's actual position.
*
* @author Mads Peter Horndrup
*/
public class WorldCamera {
/** Vector along the z-axis. */
private static SimpleVector zDirection = new SimpleVector(0f,0f,1f);
/** Vector along the reverse y-axis. */
private static SimpleVector reverseYDirection = new SimpleVector(0f, -1f, 0f);
/** Vector that has no length */
private static SimpleVector zero = new SimpleVector(0f, 0f, 0f);
/** Tile coordinates in the plane */
private float x = 0f;
private float z = 0f;
/** Camera's angle around the y-axis, in radians. */
private float angle = (float)(Math.PI * 2f - Math.PI * 0.25f);
/** Distance from the camera's focus to the actual camera on the xz-plane*/
private float radius = 150f;
/** How many tiles per second the WorldCamera should move */
private float moveSpeed = 1f;
/** How fast the WorldCamera will rotate, in radians per second */
private float rotationSpeed = 1f;
/** How many GL-units per tile */
private int tileSize;
/**
* The direction this WorldCamera is currently facing.
* This field only serves to avoid defragmentation with repetitive use of {@link #getDirection()}
*/
private SimpleVector cameraDirection;
/**
* @param tileSize How many GL units per tile in the game.
*/
public WorldCamera(int tileSize) {
this.tileSize = tileSize;
}
/**
* Transforms the given Camera to match the WorldCamera. This adjusts position, direction and up.
* @param camera Camera that will the transformed to match this WorldCamera.
*/
public void apply(Camera camera) {
// Set the camera's position to the default.
camera.setPosition(zero);
// Set the camera's direction and up to the default.
camera.setOrientation(zDirection, reverseYDirection);
// Rotate around the camera's own y-axis.
camera.rotateY(angle);
// Rotate 45 degrees downwards around the camera's own x-axis.
camera.rotateX((float)(-0.25f*Math.PI));
// Set the cameras position, based on radius and angle.
camera.setPosition(
(float)(Math.sin(angle)*radius),
(float)(-radius),
(float)(-Math.cos(angle)*radius));
// Translate camera to look at its coordinates instead of (0, 0, 0)
camera.setPosition(
camera.getPosition().x+x*tileSize,
camera.getPosition().y,
camera.getPosition().z+z*tileSize);
}
/**
* Rotates the WorldCamera around its viewpoint, and around the y-axis.
* @param angle How many seconds that has passed.
*/
public void rotate(float delta) {
this.angle += delta*rotationSpeed;
if (this.angle > 2*Math.PI) {
this.angle = 0;
}
if (this.angle < 0) {
this.angle = (float)(2*Math.PI);
}
}
/**
* Moves the WorldCamera forward in the direction it's pointing.
* @param delta How many seconds that has passed.
*/
public void moveForward(float delta) {
x += getDirection().x * moveSpeed * delta;
z += getDirection().z * moveSpeed * delta;
}
/**
* Moves the WorldCamera backwards in the direction it's pointing.
* @param delta How many seconds that has passed.
*/
public void moveBackward(float delta) {
x -= getDirection().x * moveSpeed * delta;
z -= getDirection().z * moveSpeed * delta;
}
/**
* Moves the WorldCamera sideways to the right.
* @param delta
*/
public void moveSidewaysRight(float delta) {
SimpleVector moveAxis = getDirection().calcCross(reverseYDirection);
x += moveAxis.x * moveSpeed * delta;
z += moveAxis.z * moveSpeed * delta;
}
/**
* Moves the WorldCamera sideways to the left.
* @param delta
*/
public void moveSidewaysLeft(float delta) {
SimpleVector moveAxis = getDirection().calcCross(reverseYDirection);
x -= moveAxis.x * moveSpeed * delta;
z -= moveAxis.z * moveSpeed * delta;
}
/**
* Gives this camera a new moving speed.
* @param speed The new speed, in tiles per second.
*/
public void setMoveSpeed(float moveSpeed) {
this.moveSpeed = moveSpeed;
}
/**
* Gives this camera a new rotation speed.
* @param rotationSpeed The new rotation speed, in radians per second.
*/
public void setRotationSpeed(float rotationSpeed) {
this.rotationSpeed = rotationSpeed;
}
/**
* @return Unit vector in the direction the WorldCamera is currently facing
*/
public SimpleVector getDirection() {
cameraDirection = new SimpleVector(zDirection);
cameraDirection.rotateY(angle);
cameraDirection.rotateX((float)(-0.25f*Math.PI));
return cameraDirection.normalize();
}
}
I think the problem comes from assembling the direction-vector in WorldCamera#getDirection(), or from moving along said vector in WorldCamera#moveForward() or WorldCamera#moveBackward().
SimpleVector comes form jPCT, by the way.
Any ideas as to why this might be happening?