Search

Featured Game

Games / Projects

First Person Camera Control with LWJGL

This tutorial will show you a way of implementing a first person camera control system in Java using the Lightweight Java Game Library (LWJGL).

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.

Comments

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.

Hey Gasper: there’s a mistake in is code, the time shouldn’t be stored as a float, but as a long! After that, it’ll work!;)

Maybe you’d want to change that in the tut, Lloyd? Thanks, it’s been a great help!

Cheers

Jonas

I thought I was lost when all my own attempts on creating a first person camera failed, but your tutorial helped me out of this misery. My ideas were way too complex or – should I say – confused

Thanks man, I hit a rough patch on this one. It’s been way too long since I took linear algebra for me to remember the appropriate transforms, and I kept hitting a rough spot when I tried to turn left and right. This solved all of that.

Have you considered submitting this to the main LWJGL tutorial site? A lot of younger programmers, particularly those that don’t have the slightest clue what a matrix transform is, could really use it.

Pretty Awesome. Now To add something to ‘LookThrough()’ at!

No matter how I move the mouse, the Mouse.getDX() and Mouse.getDY() always return 0.0, any ideas?

When I press a key, it still keeps moving/rotating even when I release the key. What is going on?

I love you. That’s all

I keep getting the error:

Exception in thread “main” java.lang.IllegalStateException: Function is not supported

at org.lwjgl.BufferChecks.checkFunctionAddress(BufferChecks.java:58)

at org.lwjgl.opengl.GL11.glRotatef(GL11.java:2399)

at entities.FPCameraController.lookThrough(FPCameraController.java:44)

at engine.MainGameLoop.main(MainGameLoop.java:100)

When I tried this, I found that some of the controls were backwards(adding to the y axis made the camera go down, looking left with mouse makes camera look right, etc) so I set the starting pitch to -180 to see if the camera was upside down, and lo and behold, the controls returned to normal! Now adding to the y axis makes the camera go up, and looking left makes the camera look left. Hope this helps someone.

I eventually found out that you translate the matrix negative that of the camera’s position, and that I didn’t need to flip my camera upside down.