Demeter Dzadik
Demeter Dzadik

Reputation: 141

Trigonometry of a 3D "free" camera

I'm sorry if this question was asked before, I did search, and I did not find an answer.

My problem is, that I'd like to make movement on all 3 axes with the X and Y rotation of the camera being relevant.

This is what I did:

private static void fly(int addX, int addY){ //parameters are the direction change relative to the current rotation

    float angleX = rotation.x + addX;   //angle is basically the direction, into which we will be moving(when moving forward this is always the same as our actual rotation, therefore addX and addY would be 0, 0)
    float angleY = rotation.y + addY;


    float speed = (moveSpeed * 0.0002f) * delta;
    float hypotenuse = speed; //the length that is SUPPOSED TO BE moved overall on all 3 axes


    /* Y-Z side*/
    //Hypotenuse, Adjacent and Opposite side lengths of a triangle on the Y-Z side
    //The point where the Hypotenuse and the Adjacent meet is where the player currently is.
    //OppYZ is the opposite of this triangle, which is the ammount that should be moved on the Y axis.
    //the Adjacent is not used, don't get confused by it. I just put it there, so it looks nicer.
    float HypYZ = speed;
    float AdjYZ = (float) (HypYZ * Math.cos(Math.toRadians(angleX))); //adjacent is on the Z axis
    float OppYZ = (float) (HypYZ * Math.sin(Math.toRadians(angleX))); //opposite is on the Y axis

    /* X-Z side*/
    //Side lengths of a triangle on the Y-Z side
    //The point where the Hypotenuse and the Adjacent meet is where the player currently is.
    float HypXZ = speed;
    float AdjXZ = (float) (HypXZ * Math.cos(Math.toRadians(angleY)));  //on X
    float OppXZ = (float) (HypXZ * Math.sin(Math.toRadians(angleY)));  //on Z

    position.x += AdjXZ;
    position.y += OppYZ;
    position.z += OppXZ;
}

I only implement this method when moving forwards(parameters: 0, 90) or backwards(params: 180, 270), since movement can't happen on the Y axis while going sideways, since you don't rotate on the Z axis. ( the method for going sideways(strafing) works just fine, so I won't add that.)

the problem is that when I look 90 degrees up or -90 down and then move forward I should be moving only on the Y axis(vertically) but for some reason I also move forwards(which means on the Z axis, as the X axis is the strafing).

I do realize that movement speed this way is not constant. If you have a solution for that, I'd gladly accept it as well.

Upvotes: 2

Views: 1922

Answers (1)

benjaminplanche
benjaminplanche

Reputation: 15119

I think your error lies in the fact that you don't fully project your distance (your quantity of movement hypothenuse) on your horizontal plane and vertical one.

In other words, whatever the chosen direction, what you are doing right now is moving your point of hypothenuse in the horizontal plane X-Z, even though you already move it of a portion of hypothenuse in the vertical direction Y.

What you probably want to do is moving your point of a hypothenuse quantity as a total.

So you have to evaluate how much of the movement takes place in the horizontal plane and how much in the vertical axis. Your direction gives you the answer.

Now, it is not clear to me right now what your 2 angles represent. I highly recommend you to use Tait–Bryan angles in this situation (using only yawn and pitch, since you don't seem to need the rolling - what you call the Z-rotation), to simplify the calculations.

In this configuration, the yawn angle would be apparently similar to your definition of your angleY, while the pitch angle would be the angle between the horizontal plane and your hypothenuse vector (and not the angle of the projection in the plane Y-Z).

A schema to clarify:

Evaluation of the displacement given two angles

With :

  • s your quantity of movement from your initial position P_0 to P_1 (hypothenuse)
  • a_y the yawn angle and a_p the pitch one
  • D_x, D_y, D_z the displacements for each axis (to be added to position, ie AdjXZ, OppYZ and OppXZ)

So if you look at this representation, you can see that your triangle in X-Z doesn't have s as hypotenuse but its projection s_xz. The evaluation of this distance is quite straightforward: if you place yourself in the triangle P_0 P_1 P_1xz, you can see that s_xz = s * cos(a_p). Which gives you:

float HypXZ = speed * Math.cos(Math.toRadians(angleP))); // s_xz
float AdjXZ = (float) (HypXZ * Math.cos(Math.toRadians(angleY)));  // D_x
float OppXZ = (float) (HypXZ * Math.sin(Math.toRadians(angleY)));  // D_z

As for D_y ie OppYZ, place yourself in the triangle P_0 P_1 P_1xz again, and you'll obtain:

float OppYZ = (float) (speed * Math.sin(Math.toRadians(angleP))); // D_y

Now, if by angleX you actually meant the angle of elevation as I suppose you did, then angleP = angleX and HypXZ = AdjYZ in your code.

With this correction, if angleX = 90 or angleX = -90, then

HypXZ = speed * cos(angleX) = speed * cos(90deg) = speed * 0;

... and thus AdjXZ = 0 and OppXZ = 0. No movement in the horizontal plane.


Note:

To check if your calculations are correct, you can verify if you actually move your point of the wanted quantity of movement (hypothenuse ie speed ie s). Using Pythagorean theorem:

s² = s_xz² + D_z² // Applied in the triangle P_0 P_1 P_1xz
   = D_x² + D_y² + D_z² // Applied in the triangle P_0 P_1x P_1xz

With the definitions of the displacements given above:

D_x² + D_y² + D_z²
   = (s * cos(a_p) * cos(a_y))² + (s * cos(a_p) * sin(a_y))² + (s * sin(a_p))²
   = s² * (cos(a_p)² * cos(a_y)² + cos(a_p)² * sin(a_y)² + sin(a_p)²)
   = s² * (cos(a_p)² * (cos(a_y)² + sin(a_y)²) + sin(a_p)²)
   = s² * (cos(a_p)² * 1 + sin(a_p)²)
   = s² * (cos(a_p)² + sin(a_p)²)
   = s² * 1 // Correct

Hope it helped... Bye!

Upvotes: 5

Related Questions