Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Libgdx first person camera controll

I just started playing arround with 3D in libgdx. I allready know how to draw basic Models and i tryed to play arround with the CameraController. Now i want to create a FirstPersonCamera or FirstPersonCameraController. I thought about extending PerspectiveCamera and adding a MyMovingObject target to it. The MyMovingObject would hold a x, y, z position, where y is a constant value, cause i can't move up/down at the moment. So my movement is basicly in 2D. The MyMovingObject would also store the left/right rotation, needed for its moving direction/ xSpeed, zSpeed. But the Player should also be able to look up and down, and this up/down rotation is not really needed for the MyMovingObject, as it only changes the view and no other properties. So i am not sure if i go the right way.
I want to be able to go forward, left, right, backward by using W,A,S,D and rotate left right by using the mouse. Also i want to look up and down by using the mouse, like in most First Person games.
Should i use another way, not creating my own camera by extending PerspectiveCamera? Or is this approach good and i just have to store the up/down rotation in the MyMovingObject to, also if it is only needed for the view?
Or would it be better to controll the camera with W,A,S,D and mouse and update the MyMovingObjects position, depending on cameras position and rotation?
I hope you understand what I mean. It seems a bit complicated to explain it (at least for me).

EDIT: I am now using Vector3 direction, Vector3 position and Vector3 size for my NPCs and the player. I calculate the speed by doing: xSpeed = direction.x / (direction.x + direction.z) * speed; the same for zSpeed. By doing this i "filter" the y value out of it and i get only the percent of x and y. The only problem is, that when i look straight up x and z are 0. I could fix this by using an UpVecotr, which gets rotated when i do a "Pitch-rotation". But how do i rotate him? I need to rotate it arround the sideway Vector. Thanks

EDIT: The rotation and movement work now (see my answer), but i have really big problems with the limitation of the "Pitch-rotation". I am using: if (direction.y < 0.9 && angle > 1) doPitchRotation(); else if (direction.y > -0.9 && angle < 1) doPitchRotation(); so if i rotate down and i still look down at least at -0.9 y it just does not perform the rotation. But what really happens: I rotates to - 0.9 then it rotates arround the Y-Axis and at the other side it rotates up, even if i move my mous down. Can you explain why? Why does the Y-Axis flip when i turn arround by looking down?

EDIT: It works now. It seems like my upVector got some wrong values sometimes. For landbased cams you can also use crossproduct of Y-Axis and direction Vector. No need for upVector.

like image 773
Robert P Avatar asked Feb 17 '14 10:02

Robert P


3 Answers

Hey thanks for sharing this link. I found it very useful. Here's my code on rotating a land based camera and it seems to work without problems.

private int mouseX = 0;
private int mouseY = 0;
private float rotSpeed = 0.2f;

@Override
public boolean mouseMoved(int screenX, int screenY) {
    int magX = Math.abs(mouseX - screenX);
    int magY = Math.abs(mouseY - screenY);

    if (mouseX > screenX) {
        cam.rotate(Vector3.Y, 1 * magX * rotSpeed);
        cam.update();
    }

    if (mouseX < screenX) {
        cam.rotate(Vector3.Y, -1 * magX * rotSpeed);
        cam.update();
    }

    if (mouseY < screenY) {
        if (cam.direction.y > -0.965)
            cam.rotate(cam.direction.cpy().crs(Vector3.Y), -1 * magY * rotSpeed);
        cam.update();
    }

    if (mouseY > screenY) {

        if (cam.direction.y < 0.965)
            cam.rotate(cam.direction.cpy().crs(Vector3.Y), 1 * magY * rotSpeed);
        cam.update();
    }

    mouseX = screenX;
    mouseY = screenY;

    return false;
}

This works for landbased cameras. If you want to make a flightcontroll camera, you have to do a pitch rotation arround the cam.direction.crs(cam.up). Instead of using the Vector3.cpy() i would store a Vector3 help, which gets those temporary values, because Vector3.cpy() creates a new Vector3 and this operation is performed every render loop. For flightcontroll cameras you also need to add a roll rotation and do the yaw rotation arround the cam.up Vector.

like image 105
Guest Avatar answered Oct 19 '22 23:10

Guest


This article is really helpful in my opinion. I have found a solution which should work, but i haven't tryed it yet. My MovingObjects all have a Vector3 position, Vector3 direction, Vector3 size and Vecotr3 upVector. The Player class extends this MovingObject class and adds Mouse and Keycontroll to the movement. In the MovingObject class i have the moethods:

  1. rotateYaw(float degrees): rotates the Vector3 direction arround the Y-Axis by the given degrees (libgdx has a rotate function for Vector3)--> Simple
  2. rotatePitch(float degrees): rotates the Vector3 direction arround the: direction.cross(Vector3.Y), which is the rotated side Vector of your MovingObject, by the given degrees. Also a Pitch-Rotation has to rotate the upVector, so you rotate the upVector arround the same axis, by the given degrees. As soon as you understand this it is simple.
  3. move(delta) moves your MovingObject in x,z direction by doing:

    if (direction.y == 1) {
      // You are looking straight up, no x,z direction, move in the opposite
      // direction of upVector
      xSpeed = upVector.x / (Math.abs(upVetor.x) + Math.abs(upVector.z)) * (-speed);
      zSpeed = upVector.z / (Math.abs(upVetor.x) + Math.abs(upVector.z)) * (-speed);
      position.add(xSpeed * delta, 0, ySpeed * delta);
    } else if (direction.y == -1) {
      // You are looking straight down, no x,z direction, move in the direction of
      // upVector
      xSpeed = upVector.x / (Math.abs(upVetor.x) + Math.abs(upVector.z)) * speed;
      zSpeed = upVector.z / (Math.abs(upVetor.x) + Math.abs(upVector.z)) * speed;
      position.add(xSpeed * delta, 0, ySpeed * delta);
    } else {
      // You are not looking straight up or down, so you have x,z direction. Use
      // that.
      xSpeed = direction.x / (Math.abs(direction.x) + Math.abs(direction.z)) * speed;
      zSpeed = direction.z / (Math.abs(direction.x) + Math.abs(direction.z)) * speed;
      position.add(xSpeed * delta, 0, ySpeed * delta);
    }
    

I did not test this until now, but i think it should work. Note, that in the Pitch-rotation you should also limit it to straight up/ straight down. Do this by checking the signum of x and z. If they change while you are doing a Pitch-rotation you rotated over 90 degrees. I am stil waiting for other answers and if i am wrong please correct me!

EDIT: I tested it. It works like this, but there are a few things to take care of:

  1. direction.cross(upVector) changes the direction Vector. So store that data somewhere first! After using it reset the direction Vector.
  2. The Pitch limitation has a problem: If you controll signum change, as i suggested the following happens: you look straight up, signum x and signum z are 0. You look down, signum changes and your action (limiting) starts. So take care that you also check, if it is not zero. I stil don't know how to do the pitch limitation and i edit my question to explain my issue.
  3. Think about normalizing your direction and upVector whenever you change something!

I think this should work pretty good. If you have any improvements let me know and i will update this here. If you have another solution please add an answer! Thanks

like image 3
Robert P Avatar answered Oct 19 '22 22:10

Robert P


I know that this question already has good answers. But I had some issues with the selected answer. And I just want to help someone who is looking for the same solution. I noticed some strange behaviour with the selected answer. I doesn't keep the Y exis as up. Which is of course very important on a fps. So this one is not perfect but I wanted to put this here.

// put into the create() method.
Gdx.input.setInputProcessor(new InputProcessor() {
        private int dragX, dragY;
        float rotateSpeed = 0.2f;
        // dont' forget to override other methods.
        @Override
        public boolean mouseMoved(int screenX, int screenY) {
            Vector3 direction = cam.direction.cpy();

            // rotating on the y axis
            float x = dragX -screenX;
            // change this Vector3.y with cam.up if you have a dynamic up.
            cam.rotate(Vector3.Y,x * rotateSpeed);

            // rotating on the x and z axis is different
            float y = (float) Math.sin( (double)(dragY -screenY)/180f);
            if (Math.abs(cam.direction.y + y * (rotateSpeed*5.0f))< 0.9) {
                cam.direction.y +=  y * (rotateSpeed*5.0f) ;
            }

            cam.update();
            dragX = screenX;
            dragY = screenY;
            return true;
        }

    });

NOTE: Don't use any camera controllers.
NOTE2: This might come handy: Gdx.input.setCursorCatched(true);

EDIT: I just wanted to share the walking function I use to change position of the camera with wasd keys. When W key is down the forward is true. And when the key is up forward is false. Other directions have the same principle. To detect the key down and up, please use the InputProcessor in the above code.
This creates movement in the two dimensional space (X-Z axises). The direction of the camera will not change the direction of the movement as we are eliminating the Y axis. of the direction.
One must now even if camera is directed to the sky (at a non 90 degree angle with the ground), the length of the direction vector is not fixed to 1.0f. So there will not be any loss of movement speed. To test this I rotated camera up and down (moved the mouse forward and backward) and the x and z values of the direction vector didn't change. So when the y axis of the direction vector is eliminated, we have a 2d direction vector which doesn't effected by the Y angle of the camera.

private void walking(float timeElapsed) {
        float speed = movementSpeed;
        if ((forward | back) & (right | left)) {
            speed /= Math.sqrt(2);
        }
        System.out.println(speed);
        if (forward) {
            Vector3 v = cam.direction.cpy();
            v.y = 0f;
            v.x *= speed * timeElapsed;
            v.z *= speed * timeElapsed;
            cam.translate(v);
            cam.update();
        }
        if (back) {
            Vector3 v = cam.direction.cpy();
            v.y = 0f;
            v.x = -v.x;
            v.z = -v.z;
            v.x *= speed * timeElapsed;
            v.z *= speed * timeElapsed;
            cam.translate(v);
            cam.update();
        }
        if (left) {
            Vector3 v = cam.direction.cpy();
            v.y = 0f;
            v.rotate(Vector3.Y, 90);
            v.x *= speed * timeElapsed;
            v.z *= speed * timeElapsed;
            cam.translate(v);
            cam.update();
        }
        if (right) {
            Vector3 v = cam.direction.cpy();
            v.y = 0f;
            v.rotate(Vector3.Y, -90);
            v.x *= speed * timeElapsed;
            v.z *= speed * timeElapsed;
            cam.translate(v);
            cam.update();

        }
    }
like image 3
ossobuko Avatar answered Oct 19 '22 22:10

ossobuko