Sunday 7 February 2016

Third person camera

I would like to show you how I came up with the movement of the camera for the previous post. I couldn't come up with any particularly fancy way to do it and I didn't want to check tutorials on how to do it either, I kind of wanted to achieve that "by myself", really just for the sake of it. So I went with the most basic approach: trigonometry, using sine and cosine.

Also, I will show how the player movement is related to the camera, so when we press the "up" key (or the "w"), the character will move forward, and by that I mean, away from the camera.

This is the camera script:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class CameraMovement : MonoBehaviour {

 public Transform target;

 public float speed;
 public float zoomSpeed;

 public float angularSpeed = 50;
 public float camDistance = 10;

 public float angle = 0;
 public float xPos;
 public float yPos;
 public float zPos;

 // Use this for initialization
 void Start () {
 
 }
 
 // Update is called once per frame
 void Update () {

 

  if (Mathf.Abs(Input.GetAxis ("Mouse ScrollWheel")) > 0.01f)
   camDistance -= Input.GetAxis ("Mouse ScrollWheel") * zoomSpeed;

  camDistance = Mathf.Clamp (camDistance, 3, 40);

  angle -= Input.GetAxis ("Mouse X") * angularSpeed * Time.deltaTime;

  yPos -= Input.GetAxis ("Mouse Y") * speed/10;
 

  if (angle > 360)
   angle = 0;
  if (angle < 0)
   angle = 360;

  yPos = Mathf.Clamp (yPos, 1, 10);

  zPos = target.position.z + Mathf.Sin (Mathf.Deg2Rad * angle) * camDistance;
  xPos = target.position.x + Mathf.Cos (Mathf.Deg2Rad *angle) * camDistance;

  Vector3 cameraPosition = new Vector3 (xPos, yPos, zPos); 

  if(interpolation)
   transform.position = Vector3.Lerp (transform.position,cameraPosition, speed * Time.deltaTime);
  else
  transform.position =  cameraPosition;
 
 
 }

 void LateUpdate()
 {

  transform.LookAt (target.position);


 }
}

After declaring all the variables I need, I start by checking the mouse scrollwheel for the zoom (Line 26). This basically does nothing but moving the camera towards the player or away from it. To void going through the player, I clamp the zoom value (Line 29).

The variable angle is changed according to the mouse x movement on line 31, while the camera X position is governed by the Y movement of the mouse (line 33). Then I proceed to calculate the position of the camera with respect to the player: for the Z position, I start from the Z position of the target (an empty game object child of the player character) and I add the sine of the variable angle, multiplied by the cameraDistance, which, as we are using trigonometry, is our radius. I do the same thing for the X position, only this time I use the cosine of the angle.  When it comes to moving the camera I have 2 options for simple transform or interpolation, you will see it in the example scene.

By doing this, the camera rotates around the player according to the angle given. Finally, after the camera movement is done, I simply call the LookAt() function in the late update.

Now, the player script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class PlayerMovementImp : MonoBehaviour {

 public float turnSmoothing = 15f;
 public float speedDampTime = 0.1f;

 private Animator anim;
 bool run;

 void Awake()
 {
  anim = GetComponent<Animator> ();
 }

 void FixedUpdate()
 {
  float hor = Input.GetAxis ("Horizontal");
  float ver = Input.GetAxis ("Vertical");

   run = Input.GetKey (KeyCode.Space);

  Move (hor, ver,run);
 }


 public void Move(float h, float v,bool run)
 {
  if (h != 0f || v != 0f) {

      Rotate (h, v);
      if(!run)
       anim.SetFloat ("Speed", 0.5f, speedDampTime, Time.deltaTime);
      if(run)
       anim.SetFloat ("Speed", 1f, speedDampTime, Time.deltaTime);
    } else { 
   anim.SetFloat ("Speed", 0f);
    }


 }

 void Rotate(float h, float v)
 {
  Vector3 cam = new Vector3 (Camera.main.transform.position.x, transform.position.y, Camera.main.transform.position.z);
  Vector3 playerToCam = cam - transform.position;
  playerToCam.Normalize (); 

  Vector3 finalVector = -playerToCam * v + Camera.main.transform.right *h;

  finalVector.Normalize ();

  Vector3 targetDirection = new Vector3 (h, 0f, v);
  Quaternion targetRotation = Quaternion.LookRotation (finalVector, Vector3.up);
  Quaternion newRotation = Quaternion.Lerp (GetComponent<Rigidbody>().rotation, targetRotation, turnSmoothing * Time.deltaTime);
  GetComponent<Rigidbody>().MoveRotation (newRotation);

 }

 public Animator getAnim()
  {
   return anim;
  }


 public bool getRun()
 {
  return run;
 }
}

You should be familiar with the functions used in the Awake() and the FixedUpdate().

In the Move() function I just detect whether the player has pressed any of the movement keys and I pass values to the animator (Line 25 to 39). I also called the Rotate() function, which is really where everything happens.

First, I create a Vector3, which represents the camera position but with the Y position as that of the player character, so it will rotate only on the Y axes. Then, I calculate the vector that goes from the player model too the just create camera position position, and I normalize it, so that is going to be the direction.

Next, I calculate our final Vector, which is the direction towards which the player will rotate to. I used -playerToCam, as we want the player to move in the opposite direction of the camera, multiplied y the vertical input float to give it a magnitude and then I added the camera right vector multiplied by the Input horizontal float. When the player press the right key, the direction of the character will be the right vector of the camera, therefore, we will see the character going to the right. When the left key is pressed, the direction will be still the right vector but this time multiplied by -1, causing the character to go the opposite direction of right....which is left. So, the player left and right movement are always calculated in respect of the camera forward vector. I hope this is clear enough as explanation :)

Example scene here.

No comments:

Post a Comment