I just started playing arround with 3D in libgdx. I allready know how to draw basic Model
s 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 MyMovingObject
s 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.
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.
This article is really helpful in my opinion. I have found a solution which should work, but i haven't tryed it yet. My MovingObject
s 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:
rotateYaw(float degrees)
: rotates the Vector3 direction
arround the Y-Axis
by the given degrees (libgdx has a rotate function for Vector3
)--> SimplerotatePitch(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.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:
direction.cross(upVector)
changes the direction
Vector. So store that data somewhere first! After using it reset the direction Vector.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
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With