3-D Third Person Camera Tutorial

B

brian0102

Guest
3-d-third-person-camera-tutorial

GM Version: Studio
Target Platform: Windows
Download Editable Example:https://drive.google.com/open?id=0B2TkWVm2bJT0ZE9Zd1hTU2NuQ2M
Controls :" WASD - to move camera, Q and E to roll camera"
screen_two.png
Links:
http://planning.cs.uiuc.edu/node102.html
http://ncalculators.com/matrix/3x3-matrix-multiplication-calculator.htm

Summary:
When i was reading about matrices i made a 3rd person camera using a rotation matrix. This example is pretty straight forward but i'll explain step by step what's supposed to be happening.

Tutorial:

In the Create event :
Okay in the Create event 3d mode is being turned on
Code:
//Initialize
d3d_start();
d3d_set_perspective(true);
d3d_set_lighting(false);
draw_set_color(c_white);
d3d_set_fog(true,c_black,1,128);
d3d_set_culling(false);
d3d_set_shading(false);
texture_set_interpolation(true);
then i am going to create a 3x3 Matrix using the Create 3x3 Matrix in the Matrix Functions found in the Step Event of the Editable Example
Code:
cam_Roll=0;
cam_Position=0;
cam_LookAt=0;
cam_Distance=75;

//Set Rotation
rotateX = 0;
rotateY = 0;
rotateZ = 0;

//Create 3x3 Matrix - m
//  | m0 m1 m2 | X
//  | m3 m4 m5 | Y
//  | m6 m7 m8 | Z
//------------- Create Matrix - Camera Position Matrix
cam_Position[0] = 0;
cam_Position[1] = 0;
cam_Position[2] = 0;
cam_Position[3] = 0;
cam_Position[4] = 0;
cam_Position[5] = 0;
cam_Position[6] = 0;
cam_Position[7] = 0;
cam_Position[8] = 0;
//------------- Create Matrix - Camera LookAt Matrix
cam_LookAt[0] = 0;
cam_LookAt[1] = 0;
cam_LookAt[2] = 0;
cam_LookAt[3] = 0;
cam_LookAt[4] = 0;
cam_LookAt[5] = 0;
cam_LookAt[6] = 0;
cam_LookAt[7] = 0;
cam_LookAt[8] = 0;
//------------- Create Matrix - Camera Roll Matrix
cam_Roll[0] = 0;
cam_Roll[1] = 0;
cam_Roll[2] = 0;
cam_Roll[3] = 0;
cam_Roll[4] = 0;
cam_Roll[5] = 1;
cam_Roll[6] = 1;
cam_Roll[7] = 1;
cam_Roll[8] = 0;

In the Draw event :
In the draw event we are simply drawing the cameras projection from cam_Position to cam_LookAt using cam_Roll as the Up vector, "xyz(0,0,1) is up in this example". Remember we are using d3d_set_projection_ext(xfrom, yfrom, zfrom, xto, yto, zto, xup, yup, zup, angle, aspect, znear, zfar)
Code:
d3d_set_projection_ext(cam_Position[0],cam_Position[3],cam_Position[6],
                       cam_LookAt[0],cam_LookAt[3],cam_LookAt[6],
                       cam_Roll[0],cam_Roll[3],cam_Roll[6],  60,1.33,1,256);
d3d_draw_ellipsoid(x-1,y-1,z,x+1,y+1,2+z,background_get_texture(bac_tex),2,1,24);
d3d_draw_floor(-128,-128,0,128,128,0,background_get_texture(bac_tex2),32,16);
In the Step event :
In this event the camera is going to be rotated in a very specific order, first Roll, then Yaw, and then Pitch. In X, Y, Z terms, first we rotate the X-Axis, then we rotate the Y-Axis, and then finally the Z-Axis.

In rotation of these terms a X-Axis rotation is gathered by manipulating only the Y and Z coordinates of a (x,y,z)Position. A Y-Axis rotation is gathered by manipulating only the X and Z coordinates of a (x,y,z)Position. And a Z-Axis rotation is gathered by manipulating only the X and Y coordinates of a (x,y,z)Position.

This is the code that rotates the cam_Position(x,y,z) first by the X-Axis, then by the Y-Axis, and then by the Z-Axis. Now let's start the X axis rotation
Code:
//-----------------------------------Rotate X

var theta, a, b, c, d, e;
theta = degtorad(rotateX);//angle of rotation
//----------------------------------------Rotate(theta) on the X axis
//------------Create matrix - a
//Create 3x3 Matrix - m
//  | m0 m1 m2 |
//  | m3 m4 m5 |
//  | m6 m7 m8 |
//  |  1   0      0 | X
//  |  0  cos  -sin | Y
//  |  0  sin   cos | Z
// plug in values
a[0] = 1;
a[1] = 0;
a[2] = 0;
a[3] = 0;
a[4] = cos(theta);
a[5] = -sin(theta);
a[6] = 0;
a[7] = sin(theta);
a[8] = cos(theta);
This is the most important code to remember, this is where the Rotation Matrix is created, the order of sin and cos is the exact order for a counter clock-wise x-axis rotation. When a rotation matrix is multiplied with a (x,y,z)Position matrix an estimated (x,y,z)Position is created if the given (x,y,z)Position was rotated (theta) angles - for example rotateX is a 0 to 360 degree angle.

Let's continue.
Code:
//------------Create matrix - b
//Create 3x3 Matrix - m
//  | m0 m1 m2 |
//  | m3 m4 m5 |
//  | m6 m7 m8 |
// plug in values

b[0] = 0;
b[1] = 0;
b[2] = 0;
b[3] = 0;
b[4] = 0;
b[5] = 0;
b[6] = 1;
b[7] = 1;
b[8] = 1;
//    X   Y  Z
//  | 0  0  1 |
//  | 0  0  1 |
//  | 0  0  1 |
This is Matrix B, or the Up Vector (0,0,1) in 3x3 Matrix form

Now we are going to multiply the Up Vector by the X-Axis Rotation Matrix using a 3x3 Matrix Multiplication Formula.
Code:
// 3x3 Multiplication Syntax
//  | d0 d1 d2 |
//  | d3 d4 d5 |
//  | d6 d7 d8 |

// | d0 =(a0*b0 + a1*b3 + a2*b6) d1 =(a0*b1 + a1*b4 + a2*b7) d2 =(a0*b2 + a1*b5 + a2*b8)|
// | d3 =(a3*b0 + a4*b3 + a5*b6) d4 =(a3*b1 + a4*b4 + a5*b7) d5 =(a3*b2 + a4*b5 + a5*b8)|
// | d6 =(a6*b0 + a7*b3 + a8*b6) d7 =(a6*b1 + a7*b4 + a8*b7) d8 =(a6*b2 + a7*b5 + a8*b8)|
d[0] = (a[0] * b[0]) + (a[1] * b[3]) + (a[2] * b[6]);
d[1] = (a[0] * b[1]) + (a[1] * b[4]) + (a[2] * b[7]);
d[2] = (a[0] * b[2]) + (a[1] * b[5]) + (a[2] * b[8]);

d[3] = (a[3] * b[0]) + (a[4] * b[3]) + (a[5] * b[6]);
d[4] = (a[3] * b[1]) + (a[4] * b[4]) + (a[5] * b[7]);
d[5] = (a[3] * b[2]) + (a[4] * b[5]) + (a[5] * b[8]);

d[6] = (a[6] * b[0]) + (a[7] * b[3]) + (a[8] * b[6]);
d[7] = (a[6] * b[1]) + (a[7] * b[4]) + (a[8] * b[7]);
d[8] = (a[6] * b[2]) + (a[7] * b[5]) + (a[8] * b[8]);
Right here all you have to do is plug the 'a' matrix and the 'b' matrix into the 3x3 Multiplication Syntax.

Then we will finally update the Camera.
Code:
//Camera Update
cam_Roll[0] = d[0];
cam_Roll[1] = d[1];
cam_Roll[2] = d[2];
cam_Roll[3] = d[3];
cam_Roll[4] = d[4];
cam_Roll[5] = d[5];
cam_Roll[6] = d[6];
cam_Roll[7] = d[7];
cam_Roll[8] = d[8];
Next we are going to rotate the cam_Position on the Y Axis.
Code:
//-------------------------------Rotate Y

//Camera Limitation
var theta, a, b, c, d, e;
if(rotateY>89)rotateY = 89;
    if(rotateY<1)rotateY = 1;

theta = -degtorad(rotateY);//angle of rotation
//----------------------------------------Rotate(theta) on the Y axis
//------------Create matrix - a
//Create 3x3 Matrix - m
//  | m0 m1 m2 |
//  | m3 m4 m5 |
//  | m6 m7 m8 |

//  | cos 0 sin | X
//  |  0  1  0  | Y
//  |-sin 0 cos | Z
// plug in values
a[0] = cos(theta);
a[1] = 0;
a[2] = sin(theta);
a[3] = 0;
a[4] = 1;
a[5] = 0;
a[6] = -sin(theta);
a[7] = 0;
a[8] = cos(theta);
Notice the order of sin and cos is different in this matrix, that is because this is the Y Axis Rotation Matrix.
Code:
//------------Create matrix - b
//Create 3x3 Matrix - m
//  | m0 m1 m2 |
//  | m3 m4 m5 |
//  | m6 m7 m8 |
// plug in values
b[0] = cam_Distance;
b[1] = cam_Distance;
b[2] = cam_Distance;
b[3] = 0;
b[4] = 0;
b[5] = 0;
b[6] = 0;
b[7] = 0;
b[8] = 0;
This isn't the matrix form of the Up vector but the X vector, if you're confused maybe look up how vectors work but basically a vector a is a 3-D line drawn from point(0,0,0) to point(x,y,z) , usually vectors are normalized meaning the highest number in point(x,y,z) is brought down to 1 and all other points are brought down using the same ratio i.e. point(10 , 5 , 0) normalized is point(1 , 0.5 , 0). Vectors are usually used for direction for example the X Vector would be (1,0,0), the Y Vector would be (0,1,0) and the Up Vector would be (0,0,1).

Plug and play
Code:
//------------Rotation
// Multiply camera position matrix * rotation matrix
// 3x3 Multiplication Syntax
//  | d0 d1 d2 |
//  | d3 d4 d5 |
//  | d6 d7 d8 |

// | d0 =(a0*b0 + a1*b3 + a2*b6) d1 =(a0*b1 + a1*b4 + a2*b7) d2 =(a0*b2 + a1*b5 + a2*b8)|
// | d3 =(a3*b0 + a4*b3 + a5*b6) d4 =(a3*b1 + a4*b4 + a5*b7) d5 =(a3*b2 + a4*b5 + a5*b8)|
// | d6 =(a6*b0 + a7*b3 + a8*b6) d7 =(a6*b1 + a7*b4 + a8*b7) d8 =(a6*b2 + a7*b5 + a8*b8)|
d[0] = (a[0] * b[0]) + (a[1] * b[3]) + (a[2] * b[6]);
d[1] = (a[0] * b[1]) + (a[1] * b[4]) + (a[2] * b[7]);
d[2] = (a[0] * b[2]) + (a[1] * b[5]) + (a[2] * b[8]);

d[3] = (a[3] * b[0]) + (a[4] * b[3]) + (a[5] * b[6]);
d[4] = (a[3] * b[1]) + (a[4] * b[4]) + (a[5] * b[7]);
d[5] = (a[3] * b[2]) + (a[4] * b[5]) + (a[5] * b[8]);

d[6] = (a[6] * b[0]) + (a[7] * b[3]) + (a[8] * b[6]);
d[7] = (a[6] * b[1]) + (a[7] * b[4]) + (a[8] * b[7]);
d[8] = (a[6] * b[2]) + (a[7] * b[5]) + (a[8] * b[8]);
Then we will finally update the Camera.
Code:
//Camera Update
cam_Position[0] = d[0];
cam_Position[1] = d[1];
cam_Position[2] = d[2];
cam_Position[3] = d[3];
cam_Position[4] = d[4];
cam_Position[5] = d[5];
cam_Position[6] = d[6];
cam_Position[7] = d[7];
cam_Position[8] = d[8];
And finally we are going to rotate the cam_Position on the Z Axis.
Code:
//------------------------------Rotate Z

theta = degtorad(rotateZ);//angle of rotation

//----------------------------------------Rotate(theta) on the Z axis
//------------Create matrix - a
//Create 3x3 Matrix - m
//  | m0 m1 m2 |
//  | m3 m4 m5 |
//  | m6 m7 m8 |
//  | cos  -sin  0 | X
//  | sin   cos  0 | Y
//  | 0      0   1 | Z
// plug in values
a[0] = cos(theta);
a[1] = -sin(theta);
a[2] = 0;
a[3] = sin(theta);
a[4] = cos(theta);
a[5] = 0;
a[6] = 0;
a[7] = 0;
a[8] = 1;
Notice the order of sin and cos is different in this matrix, that is because this is the Z Axis Rotation Matrix.

Code:
//------------Create matrix - b
//Create 3x3 Matrix - m
//  | m0 m1 m2 |
//  | m3 m4 m5 |
//  | m6 m7 m8 |
// plug in values
b[0] = cam_Position[0];
b[1] = cam_Position[1];
b[2] = cam_Position[2];
b[3] = cam_Position[3];
b[4] = cam_Position[4];
b[5] = cam_Position[5];
b[6] = cam_Position[6];
b[7] = cam_Position[7];
b[8] = cam_Position[8];
This time we are multiplying the already Y Axis rotated cam_Position by the Z Axis, This will work in conjunction with one another due to the order of rotations, using the right hand grid and right hand rotation visualization with your actual hand might help if you don't understand.
The X axis is not being used with the Y or Z axis rotations and that is because the X Axis rotation is the cam_Roll and is used only as the Up Vector in d3d_set_projection_ext.

Plug and play
Code:
//------------Rotation
// Multiply camera position matrix * rotation matrix
// 3x3 Multiplication Syntax
//  | d0 d1 d2 |
//  | d3 d4 d5 |
//  | d6 d7 d8 |

// | d0 =(a0*b0 + a1*b3 + a2*b6) d1 =(a0*b1 + a1*b4 + a2*b7) d2 =(a0*b2 + a1*b5 + a2*b8)|
// | d3 =(a3*b0 + a4*b3 + a5*b6) d4 =(a3*b1 + a4*b4 + a5*b7) d5 =(a3*b2 + a4*b5 + a5*b8)|
// | d6 =(a6*b0 + a7*b3 + a8*b6) d7 =(a6*b1 + a7*b4 + a8*b7) d8 =(a6*b2 + a7*b5 + a8*b8)|
d[0] = (a[0] * b[0]) + (a[1] * b[3]) + (a[2] * b[6]);
d[1] = (a[0] * b[1]) + (a[1] * b[4]) + (a[2] * b[7]);
d[2] = (a[0] * b[2]) + (a[1] * b[5]) + (a[2] * b[8]);

d[3] = (a[3] * b[0]) + (a[4] * b[3]) + (a[5] * b[6]);
d[4] = (a[3] * b[1]) + (a[4] * b[4]) + (a[5] * b[7]);
d[5] = (a[3] * b[2]) + (a[4] * b[5]) + (a[5] * b[8]);

d[6] = (a[6] * b[0]) + (a[7] * b[3]) + (a[8] * b[6]);
d[7] = (a[6] * b[1]) + (a[7] * b[4]) + (a[8] * b[7]);
d[8] = (a[6] * b[2]) + (a[7] * b[5]) + (a[8] * b[8]);
Then we will finally update the Camera.
Code:
//Camera Update
cam_Position[0] = d[0];
cam_Position[1] = d[1];
cam_Position[2] = d[2];
cam_Position[3] = d[3];
cam_Position[4] = d[4];
cam_Position[5] = d[5];
cam_Position[6] = d[6];
cam_Position[7] = d[7];
cam_Position[8] = d[8];
 
Last edited by a moderator:

phillipPbor

Member
but one question I may ask. in making some sort of a klonoa game a 2.5D platform game. would you make the view to turn into a different angle if player goes to vertical platform?
we need more makers, like they how to make jump or gravity on z axes.
 

icuurd12b42

TMC Founder
GMC Elder
init 3d on create? no... nonono :) use the draw event of the camera to initialize 3d

+10 for the matrix solution
-2 for the darn 0,0,1 for zup... should be 0,0, -1 if you are anal like me. but that requires fiddling around other bits in the projection

Just out of curiosity is it possible to do this without using a matrix?
Yes you can use d3d_transforms + d3d_transform_vertex to rotate a up and a facing vector easily for the camera as you would rotate an 3d object....
 
K

Kombowz

Guest
I don't know if I'll ever make a 3d game with game maker but this gives me a nice starting point.

Thank you for sharing.
 
Top