I’ve noticed the serious lack of good explanation of FPS camera methods. I’ve seen everything from using cross product to using sketchy pi calculations and back around to poorly explained trigonometry. I am writing this forum post for novices in hopes that a random google search will pick it up… but whoever browses is good too!
This tutorial is not based off OpenGL and doesn’t use 3rd party libraries. I’ve seen many people use OpenGL fixed function pipeline and many other inadapatable means of showing what entails a FPS camera. So… that was a problem. I am going the trig route because its fast, simple, and easy to understand. Pure math.
Things you should know before this:
Using trigonometry functions sin, cos, and tan (sin and cos are to be used)
I am writing this in Java, because well… this is JGO.
Okay let’s get started!
We are in the main game loop, what we want to do is this:
If a key is down move, in the direction the object is facing, with respect to the key’s direction. Also, strafing.
Simple enough. As you see in the picture, I’ve put out a right triangle on a smiley face. Since, in game, we allot a predefined variable for speed, we can set this as the Hypotenuse, or the distance. In the game, the object has rotation. Let the Yaw value (pitch = x, yaw = y, roll = z) be the angle between the hypotenuse and the adjacent side. I am calling this angle A for alpha, as per the angles name. We have everything we need now.
This is what they mean, in case you had no idea. Sine works with opposite and hypotenuse, Cosine works with adjacent and hypotenuse, and tangent works with opposite and adjacent. We are going to use both Sine and Cosine.
S.oh C.ah T.oa
Sine Cosine Tangent
Looking back at the picture, let’s imagine the hypotenuse, coming out of Mr. Smiley, is the direction that we want to go. Now look at the adjacent side and the opposite side… they describe how much you need to “go over” and “go up” in order to reach that goal. Genius right?
So if the adjacent comes out to be 5 and the opposite comes out as 5 (no you don’t just get an equilateral triangle) you need to translate your object’s x by 5 and y by 5.
Opposite = y
Adjacent = x
We need to do maths for that now.
Here is the formula for the adjacent side (what we are trying to find)
Cos(A) = adj / hyp
Here is the formula for the opposite side (what we are trying to find)
Sin(A) = opp / hyp
Since we have the hypotenuse, and opp and adj are variables, we should multiply hyp over.
hyp * Cos(A) = adj
hyp * Sin(A) = opp
Now we have obvious means.
// define some variables float hypotenuse = 1f; // our distance we want to travel float rotation = 0f; // the rotation of our object around the y axis (yaw), in degrees // do the calculations float adjacent = hypotenuse * Math.cos(Math.toRadians(rotation)); // note, toRadians because we were working in degrees float opposite = hypotenuse * Math.sin(Math.toRadians(rotation)); // cos and sin only accept radians // move the camera Camera.x += opposite; Camera.z += adjacent;
That wasn’t so bad. You want to make this code run when you have arrow key up or W pressed. This allows you to move forward. If you want to move backwards instead, repeat the code… but switch the +='s with -=, naturally.
If you increase and decrease the rotation with keys A and D plus move the object as shown, you get a normal FPS camera, but you maybe want strafing. Strafing is the act of looking forward, but moving to the side, still looking forward. Like the hammer time dance.
If you think about it, we are going to rotate 90 degrees to the right and 90 degrees to the left then move. Naturally, we want to not alter the rotation permanently, because we don’t want to instantly face the way we are walking… or have to translate the rotation back. This would be an example of redundancy. So we will just add 90 degrees to our Yaw (y) temporarily.
// code as before float hypotenuse = 1f; float rotation = 0f; // manipulated rotation with not permanent 90 degrees float adjacent = hypotenuse * Math.cos(Math.toRadians(rotation+90)); float opposite = hypotenuse * Math.sin(Math.toRadians(rotation+90)); Camera.x += opposite; Camera.z += adjacent;
That wasn’t so bad. You want to make this code run when you have arrow key left or A pressed. This allows you to move left. If you want to move right instead, repeat the code… but switch the +='s with -=, naturally.
Alternatively, you can do use ternary operations.
float hypotenuse = isADown() || isWDown() ? 1f : isDDown() || isSDown() ? -1f : 0f;
Anything I didn’t go over? Any questions? I will edit this post.