Search
Featured Game

Comming Soon

Games / Projects

Comming Soon

First Person Camera Control with LWJGL

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

17 Comments

  1. Matt says:

    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.

  2. Paul says:

    Arn’t you suppost to translate, then rotate?

  3. Bob says:

    Thanks, that was very helpful. Could you please upload a simple example of this?

  4. Paul says:

    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.

  5. Lloyd says:

    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!

  6. Jesse says:

    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

  7. Jesse says:

    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.

  8. Paul says:

    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)

  9. Paul says:

    Sorry for the issues I have caused it seems that I was translating to 100 before looking through the camera. It works fine now.

  10. fred says:

    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.

  11. Nique says:

    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)

  12. Paul says:

    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?

  13. Gasper says:

    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?

  14. Roland says:

    Thank you. This is the most straightforward tutorial I’ve found so far.

  15. Jonas says:

    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

  16. Florian R. Klein says:

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

  17. Michael Eric Oberlin says:

    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.

Leave a Comment