We will make a class called FPCameraController that has 3 properties:
position – A vector that will store the x, y and z co-ords of the camera.
yaw – A float that will store the yaw (y axis rotation) of the camera.
pitch – A float that will store the pitch (x axis rotation) of the camera.
We do not need to store the roll (z rotation) as a First person camera will not roll (tilt) but you could add this if you want your camera to tilt (some games use tilt to peek around corners).
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Vector3f;
//First Person Camera Controller
public class FPCameraController
{
//3d vector to store the camera's position in
private Vector3f position = null;
//the rotation around the Y axis of the camera
private float yaw = 0.0f;
//the rotation around the X axis of the camera
private float pitch = 0.0f;
Now we will make the constructor that will take 3 float values as parameters: x, y and z. They will be the starting location of the camera.
//Constructor that takes the starting x, y, z location of the camera
public FPCameraController(float x, float y, float z)
{
//instantiate position Vector3f to the x y z params.
position = new Vector3f(x, y, z);
}
Next we will make yaw and pitch methods, these will be used to control the rotation of the camera. They will both take a float parameter : amount. This value will be the y movement of the mouse for the pitch method and the x movement of the parameter for the yaw method.
//increment the camera's current yaw rotation
public void yaw(float amount)
{
//increment the yaw by the amount param
yaw += amount;
}
//increment the camera's current yaw rotation
public void pitch(float amount)
{
//increment the pitch by the amount param
pitch += amount;
}
Next we will make the walking methods typically bound to the WASD keys. The methods will need to calculate to how much on the x and z axises the camera will need to move as the movement is relative to the the current yaw (y rotation) of the camera. For example if have turned the camera 45d to the right when you move forward you will half the amount on the z axis and half on the y axis (diagonal).
This is calculation is done using basic trigonometry. If we know the distance we want to move and the angle we want to move at (yaw) we can calculate how far to move with in the x and z axis like this:
x = distance * sin(yaw)
z = distance * cos(yaw)
//moves the camera forward relative to its current rotation (yaw)
public void walkForward(float distance)
{
position.x -= distance * (float)Math.sin(Math.toRadians(yaw));
position.z += distance * (float)Math.cos(Math.toRadians(yaw));
}
//moves the camera backward relative to its current rotation (yaw)
public void walkBackwards(float distance)
{
position.x += distance * (float)Math.sin(Math.toRadians(yaw));
position.z -= distance * (float)Math.cos(Math.toRadians(yaw));
}
//strafes the camera left relitive to its current rotation (yaw)
public void strafeLeft(float distance)
{
position.x -= distance * (float)Math.sin(Math.toRadians(yaw-90));
position.z += distance * (float)Math.cos(Math.toRadians(yaw-90));
}
//strafes the camera right relitive to its current rotation (yaw)
public void strafeRight(float distance)
{
position.x -= distance * (float)Math.sin(Math.toRadians(yaw+90));
position.z += distance * (float)Math.cos(Math.toRadians(yaw+90));
}
Next we will write a method that will be used to translate and rotate the modelview matrix so that we will look threw the camera.
//translates and rotate the matrix so that it looks through the camera
//this dose basic what gluLookAt() does
public void lookThrough()
{
//roatate the pitch around the X axis
GL11.glRotatef(pitch, 1.0f, 0.0f, 0.0f);
//roatate the yaw around the Y axis
GL11.glRotatef(yaw, 0.0f, 1.0f, 0.0f);
//translate to the position vector's location
GL11.glTranslatef(position.x, position.y, position.z);
}
}
This class would be used in the games main loop running the lookThrough() method before anything is rendered and with the movement and run from rotation methods run based on key presses and mouse movement. This is what a game loop might look like using this class.
public void gameLoop()
{
FPCameraController camera = new FPCameraController(0, 0, 0);
float dx = 0.0f;
float dy = 0.0f;
float dt = 0.0f; //length of frame
float lastTime = 0.0f; // when the last frame was
float time = 0.0f;
float mouseSensitivity = 0.05f;
float movementSpeed = 10.0f; //move 10 units per second
//hide the mouse
Mouse.setGrabbed(true);
// keep looping till the display window is closed the ESC key is down
while (!Display.isCloseRequested() &&
!Keyboard.isKeyDown(Keyboard.KEY_ESCAPE))
{
time = Sys.getTime();
dt = (time - lastTime)/1000.0f;
lastTime = time;
//distance in mouse movement from the last getDX() call.
dx = Mouse.getDX();
//distance in mouse movement from the last getDY() call.
dy = Mouse.getDY();
//controll camera yaw from x movement fromt the mouse
camera.yaw(dx * mouseSensitivity);
//controll camera pitch from y movement fromt the mouse
camera.pitch(dy * mouseSensitivity);
//when passing in the distance to move
//we times the movementSpeed with dt this is a time scale
//so if its a slow frame u move more then a fast frame
//so on a slow computer you move just as fast as on a fast computer
if (Keyboard.isKeyDown(Keyboard.KEY_W))//move forward
{
camera.walkForward(movementSpeed*dt);
}
if (Keyboard.isKeyDown(Keyboard.KEY_S))//move backwards
{
camera.walkBackwards(movementSpeed*dt);
}
if (Keyboard.isKeyDown(Keyboard.KEY_A))//strafe left
{
camera.strafeLeft(movementSpeed*dt);
}
if (Keyboard.isKeyDown(Keyboard.KEY_D))//strafe right
{
camera.strafeRight(movementSpeed*dt);
}
//set the modelview matrix back to the identity
GL11.glLoadIdentity();
//look through the camera before you draw anything
camera.lookThrough();
//you would draw your scene here.
//draw the buffer to the screen
Display.update();
}
}
If there are any problems, questions and or suggestions about this tutorial please leave a comment.
Thanks so much for this tutorial! I’ve been searching Google for what seems to be years! Just a question, could gluLookAt be used to do the same thing as you’ve done in lookThrough(); ?
Thanks again,
Matt.
Arn’t you suppost to translate, then rotate?
Thanks, that was very helpful. Could you please upload a simple example of this?
I’m having problems with this. It seems to be a bit more like orbit and less of a look around thing. I’m using gluPerspective with blocks of width 1, maybe it would work better with bigger blocks.
Hey mate, I have a working example I used to test it when I made the tutorial, I will try dig that up for you!
I have implemented a solution similar to this, though I added vertical movement:
public void walkForward(float distance) {
position.x -= distance * (float) Math.sin(Math.toRadians(yaw));
position.y += distance * (float) Math.tan(Math.toRadians(pitch));
position.z += distance * (float) Math.cos(Math.toRadians(yaw));
}
public void walkBackwards(float distance) {
position.x += distance * (float) Math.sin(Math.toRadians(yaw));
position.y -= distance * (float) Math.tan(Math.toRadians(pitch));
position.z -= distance * (float) Math.cos(Math.toRadians(yaw));
}
public void flyUp(float distance) {
position.y -= distance;
}
I have two problems. First, when I get the camera into certain orientations, it will jitter wildly when glRotatef() attempts to do the rotation. If you imagine standing in the middle of a cube and looking at any of the corners, this is where the camera jitters. Second, and more trivial – tan likes to shoot off to inf depending on how much I pitch. I have simply been bounding tan in that case.
Any ideas? Great example though
I should mention – the jitter only happens when I am quite far from the origin of my world. It’s got to be a lack of precision on glTranslatef and/or glRotatef as you leave the origin. I wonder if I can move the origin with me.. hmm.
The Camera seems to zoom in when I turn up and zoom out when I go back. I need a camera which will tell me where my player is in the world (its blocks are 1 wide in the VBOs)
Sorry for the issues I have caused it seems that I was translating to 100 before looking through the camera. It works fine now.
I have a really weird problem. The mouse movements returned by getDX and getDY alternate between large and small values. So, if I move my mouse fairly smoothly, I might get this series of DX’s: 181, 123, 178, 127, 180, 119 and so on. Notice how they alternate between large and small.
When my mouse isn’t moving, it is as you would expect; all zeros. Needless to say, this issue causes jittery turning on the screen.
While everything works great i’m having one problem: When i push my mouse up i get a negative number from DY. When i push my mouse down i get a positive. This results in a really weird way of looking up or down (inverted)
Can I have the Source? I have a problem and I want to see what I’m doing wrong. Can you post a download link plz?
Im trying to use this code and when i press W,S,A,D it doesnt move at all. It’s getting caught up here:
time = Sys.getTime();
dt = (time – lastTime)/1000.0f;
lastTime = time;
dt is always 0 and time is returned as a constant value (1.35410601E12). Since the distance is calculated as movementspeed*dt its always going to return 0;
and not move at all.
What could be a possible solution?
Thank you. This is the most straightforward tutorial I’ve found so far.