0
\$\begingroup\$

I want my camera to following behind the player at all times. When the player turns to the left, I would expect the camera would move behind the player while remaining focused on the player.

My camera stays focused on the player. However, it does not rotate to stay behind the player.

I tried following the directions from here, and here. Neither of which seems to help.

Here's my code:

public class CameraFollow : MonoBehaviour
{

    private GameObject player;
    private GameObject mainCamera;
    private Vector3 offset;
    private float distance;
    private Vector3 playerPrevPos;
    private Vector3 playerMoveDir;

    // Use this for initialization
    private void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player");
        mainCamera = GameObject.FindGameObjectWithTag("MainCamera");

        //Calculate and store the offset value by getting the distance between the player's position and camera's position.
        offset = transform.position - player.transform.position;

        distance = offset.magnitude;
        playerPrevPos = player.transform.position;
    }

    // Update is called once per frame
    private void Update() { }

    private void LateUpdate()
    {
        playerMoveDir = player.transform.position - playerPrevPos;

        if (playerMoveDir != Vector3.zero)
        {
            playerMoveDir.Normalize();
            transform.position = player.transform.position + offset;
            transform.LookAt(player.transform.position);
            transform.rotation = Quaternion.Lerp(transform.rotation, player.transform.rotation, Time.deltaTime * 1);

            mainCamera.transform.position = player.transform.position + offset;
            mainCamera.transform.LookAt(player.transform.position);
            mainCamera.transform.rotation = Quaternion.Lerp(transform.rotation, player.transform.rotation, Time.deltaTime * 1);

            playerPrevPos = player.transform.position;
        }
    }
}

I have done something a little different, per instructions from a online unity class. My main camera is a subobject of a rootobject, as shown in this picture.

enter image description here

I have attached image of the game with the yellow line showing which way the player is facing. The camera is facing in a different direction.

enter image description here

Any help would be appreciated.
Thnx Matt

Follow-up from answers: I cannot get either solution to work. @alejandrodlsp solution doesn't compile (I've added a comment to the answer explaining why). If I do not set offset on Start (aka its always 0), then the camera rotates. The problem is that is at the feet the player and not behind like I wanted. Once I compute offset, then the camera never rotates behind the player.

\$\endgroup\$
  • \$\begingroup\$ Hey @tatmanblue, there is no difference between subtracting and adding the offset, since the needed value is absolute (distance). I've used subtract because I'm used to it when calculating distance. Unfortunately I don't have enough time to improve my answer so I'll delete it for now. \$\endgroup\$ – TomTsagk Feb 12 at 9:34
1
\$\begingroup\$

I would separate the following and rotating functionalities into different functions, and call them both from the fixed Update for the purpose of clarity. I'd also set the follow movement for the parent object and Slerp to the same rotation as the player's; no need to use the transform.LookAt() function.

    float rotationSmoothing = 0;
    float followSpeed = 0.1f;

    private GameObject player;
    private GameObject mainCamera;
    private Vector3 offset;

    public void Start(){
       player = GameObject.FindGameObjectWithTag("Player");
       mainCamera = GameObject.FindGameObjectWithTag("MainCamera");

       offset = player.transform.position - mainCamera.transform.position;
    }

    public void FixedUpdate(){
     follow();
     rotate();
    }

    void follow(){
      if(player == null){
        return;
      }

      Vector3 tPos = new Vector3(player.position.x + offset.x,
                                 player.position.y + offset.y,
                                 player.position.z + offset.z,);
      transform.position = Vector3.Lerp(mainCamera.transform.position,
                                      tPos, followSpeed);
    }

 void rotate(){


     float targetRot = Quaternion.Euler(player.transform.rotation).y;   // Creates a from player

     if (rotationSmoothing > 0){
         mainCamera.transform.localRotation = Quaternion.Slerp(transform.localRotation, targetRot, rotationSmoothing * Time.deltaTime); // Slerp between rotation
     }else{
         mainCamera.transform.localRotation= targetRot; // Sets rotation
     }
 }

Another option may be to set the camera system to be a child of the player itself, but that path rules out any type of movement smoothing.

I hope this is useful for you and fixes your problem.

\$\endgroup\$
  • 1
    \$\begingroup\$ I cannot get your code to compile. I assume this differences in unity/mono versions. Quaternion.EulerAngles requires a vector3 and player.transform.rotation is a Quaternion. \$\endgroup\$ – tatmanblue Feb 11 at 14:27
  • \$\begingroup\$ @tatmanblue You're right, It should be Quaternion.Euler() rather than Quaternion.EulerAngles(). Which takes a quaternion and transforms it into Euler angles. \$\endgroup\$ – alejandrodlsp Feb 11 at 20:15
  • \$\begingroup\$ see my second edit please :) thank you. I have changed a few lines of code. Using what you provided did not work (the camera was under the player and to the right side) so I was playing with values. \$\endgroup\$ – tatmanblue Feb 12 at 15:53
  • 1
    \$\begingroup\$ @tatmanblue Unfortunatelly I don't have enough time to update my answer, but I have uploaded this script I made a long time ago to github. It uses the mouse position to move the camera, but you can change it easily to face the player. I think it might help you solve your problem. link \$\endgroup\$ – alejandrodlsp Feb 12 at 20:09
1
\$\begingroup\$

I have a solution to what I needed. I appreciate everyone's input. The solution was definitely "outside the box". The key to the solution is this property [gameobject].transform.forward. My other change was to use LateUpdate() as it is called once per frame, rather than FixedUpdate(), since that's called during the physics phase.

[gameobject].transform.forward gives the direction the player is looking at. Obviously, when the player rotates, the vector rotates too. For more info, see this post.

Here's the code.

public class CameraRotateFollow : MonoBehaviour
    {
        private GameObject player;
        private Vector3 distanceVecToPlayer;
        private float distanceToPlayer = 3f;
        private float cameraHeight = 2f;
        private long count = 0;

        // Use this for initialization
        private void Start()
        {
            player = GameObject.FindGameObjectWithTag("Player");

            // Calculate and store the camera arm position relative to player
            // by getting the distance between the player's position and camera arm 's position.
            distanceVecToPlayer = transform.position - player.transform.position;

            // on start, we are assuming player is facing north (rotation 0, 0, 0) (this is design flaw I can live with)
            // therefore transform.position.z is the distance from camera arm to player
            // y Height above player
            // x is left or right of player (and should always be 0)
            distanceToPlayer = distanceVecToPlayer.z;
            cameraHeight = distanceVecToPlayer.y;       
        }

        private void LateUpdate()
        {
            Follow();
        }

        private void Follow()
        {
            var forward = player.transform.forward;
            float distanceModifer = distanceToPlayer * -1.0f;

            // compute camera arm location. 
            Vector3 setCameraLocation = player.transform.position - distanceModifer * player.transform.forward;

            // camera is set above the player (to look over his shoulder). To keep a consistent height
            // set height (Vector3.y) to player position (which is techincally at the ground) + starting camera height
            setCameraLocation.y = player.transform.position.y + cameraHeight;

            // Set the position of the camera's transform to be the same as the player's, but offset by the calculated offset distance.
            // this keeps the camera on the same axis as the previous update            
            transform.position = setCameraLocation;

            // make sure camera arm (and hence the camara) is looking at the player
            transform.LookAt(player.transform);
        }

Thnx again. Everyone's help is appreciated. I wouldn't have been able to think through the problem enough without it.

Matt

\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.