Third Person Camera

Hi,

Relatively new to coding, and I’ve no idea what Quaternions are. I would like to know what I need to add to this script so I can get a third person camera movement when I move the mouse or Right analog stick. Thanks in advance! ^^

This is the script tied to the Main Camera, the Player is linked to the “public Transform player

using UnityEngine;

public class FollowPlayer : MonoBehaviour
{
    public Transform player;
    public Vector3 cameraOffset;

    void Update()
    {
        transform.position = player.position + cameraOffset;
    }
}

Try this script

using UnityEngine;

public class CameraFollow : MonoBehaviour {

    public Transform target;

    public float smoothSpeed = 1f;
    public Vector3 offset;

    void FixedUpdate ()
    {
        Vector3 desiredPosition = target.position + offset;
        Vector3 smoothedPosition = Vector3.Lerp(transform.position, desiredPosition, smoothSpeed);
        transform.position = smoothedPosition;

        transform.LookAt(target);
    }

}

Note:

  • This script works for both 2D and 3D
  • Apply this script to your camera
  • Then, add an obbject (in your case, the player) into the follow bar
  • After that set the value to -10
  • If you do not get your desired results, try chaning these values until you’re satisfied XD

Anyways, if you did find it helpful. Please, consider supporting my YouTube Channel here :slight_smile:

Thank you, it does the same thing so there’s progress. But you didn’t show me how I would use the Mouse or Controller to move the camera while it stays looking at the target. What variable must be changed with the input actions?

Wrote this some time ago:
Setup:

  1. Put the script on your camera.
  2. Create an emtpy child on your player and call it “CameraOrigin” (if you want to)
  3. Move the camera origin up so it is placed around the shoulders of your character
  4. Parent your camera to the CameraOrigin
  5. Move the camera so you can see the player character
  6. Set the noPlayerMask so that objects, the camera should collide with are included and the player as well as other objects the camera should not collide with are excluded
  7. play around with the values, camera and origin position until you are happy with the result

Hierarchy once you are done should look like this:
Player
–CameraOrigin
----Camera (with the script “ThirdPersonCam” attached)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace PAD.Player
{
    public class ThirdPersonCam : MonoBehaviour
    {
        [SerializeField] bool _useInitialOffset = true;
        [SerializeField] bool _cursorVisible = true;
        [SerializeField] bool _collisionAvoidance = true;
        [SerializeField] float _collisionAvoidanceSpeed = 5;
        [SerializeField] float _maxSmoothMoveOffset = 2.5f;
        [SerializeField] Vector3 _originOffset;
        public float camOffsetDistance;
        float _currentCamOffsetDistance;
        [SerializeField] Vector2 _rotationSpeed = Vector2.one;
        [SerializeField] Vector2 _rotationLimitVertical = new Vector2(-70, 85);
        public Transform target;
        Transform _origin;
        [SerializeField] LayerMask noPlayerMask;


        void Start()
        {
            _origin = transform.parent;

            if (!target)
            {
                target = _origin.parent;
            }


            if (_useInitialOffset)
            {
                _originOffset = target.position - _origin.position;
            }
            if (camOffsetDistance == 0)
            {
                camOffsetDistance = (transform.position - _origin.position).magnitude;
            }
            _currentCamOffsetDistance = camOffsetDistance;

            SetCursorVisibility();
        }

        private void Update()
        {
            ChangeCursorVisibility();

            // do not rotate when the cursor is invisible
            if (!_cursorVisible)
            {
                OrbitCameraRotation();
            }
        }

        public void FixedUpdate()
        {
            // keep position
            ManageOriginPosition();

            if (_collisionAvoidance)
            {
                CollisionAvoidance();
            }
            else
            {
                _currentCamOffsetDistance = camOffsetDistance;
            }

            ManageDistance();
        }

        void ManageOriginPosition()
        {
            // non-smoothed version:
            //_origin.position = target.position + _originOffset;

            Vector3 wantedOriginPosition = target.position + _originOffset;
            Vector3 originToTargetVector = _origin.position - wantedOriginPosition;
            float originToTargetDistance = originToTargetVector.magnitude;

            float maxMoveDistanceLimit = originToTargetDistance - _maxSmoothMoveOffset;

            float maxMoveDistance = 0.01f + originToTargetDistance * 0.2f;

            maxMoveDistance = Mathf.Max(maxMoveDistance, maxMoveDistanceLimit);
            _origin.position =
                Vector3.MoveTowards(_origin.position, wantedOriginPosition, maxMoveDistance);
        }

        void ChangeCursorVisibility()
        {
            if (Input.GetKeyDown(KeyCode.LeftAlt) || Input.GetMouseButtonDown(2))
            {
                _cursorVisible = !_cursorVisible;
                SetCursorVisibility();
            }
        }

        private float rotationVertical = 0f;

        void OrbitCameraRotation()
        {
            float hAxis = Input.GetAxis("Mouse X");
            float vAxis = -Input.GetAxis("Mouse Y");

            _origin.Rotate(Vector3.up, hAxis * _rotationSpeed.x, Space.World);

            rotationVertical += vAxis * _rotationSpeed.y;
            rotationVertical = ClampAngle(rotationVertical,
                                            _rotationLimitVertical.x,
                                            _rotationLimitVertical.y);

            _origin.localEulerAngles = new Vector3(rotationVertical,
                                                    _origin.localEulerAngles.y,
                                                    _origin.localEulerAngles.z);

            //_origin.Rotate(_origin.right, vAxis * _rotationSpeed.y * Time.deltaTime, Space.World);

            //_origin.rotation = ClampRotationAroundXAxis(_origin.rotation);
        }

        public static float ClampAngle(float angle, float min, float max)
        {
            if (angle < -360F)
                angle += 360F;
            if (angle > 360F)
                angle -= 360F;
            return Mathf.Clamp(angle, min, max);
        }

        Quaternion ClampRotationAroundXAxis(Quaternion q)
        {
            q.x /= q.w;
            q.y /= q.w;
            q.z /= q.w;
            q.w = 1.0f;

            float angleX = 2.0f * Mathf.Rad2Deg * Mathf.Atan(q.x);

            angleX = Mathf.Clamp(angleX, _rotationLimitVertical.x, _rotationLimitVertical.y);

            q.x = Mathf.Tan(0.5f * Mathf.Deg2Rad * angleX);

            return q;
        }

        void CollisionAvoidance()
        {
            Vector3 camToOriginVector = (transform.position - _origin.position).normalized * camOffsetDistance;
            RaycastHit hit;
            bool anythingBetweenCameraAndPlayer =
                Physics.Raycast(_origin.position, camToOriginVector, out hit,
                    camToOriginVector.magnitude, noPlayerMask);

            if (anythingBetweenCameraAndPlayer && hit.transform != target)
            {
                _currentCamOffsetDistance = (hit.point - _origin.position).magnitude;
              
                // non-smoothed version:
                //transform.position = hit.point - camToOriginVector.normalized * 0.1f;
            }
            else
            {
                _currentCamOffsetDistance = camOffsetDistance;
            }
        }

        void ManageDistance()
        {
            Vector3 camToOriginVector = transform.position - _origin.position;
            if (Vector3.Dot(camToOriginVector, transform.forward) > 0)
            {
                camToOriginVector *= -1;
            }
            float currentDistance = camToOriginVector.magnitude;
            currentDistance = Mathf.MoveTowards(currentDistance, _currentCamOffsetDistance,
                Time.deltaTime * _collisionAvoidanceSpeed);
            transform.position = _origin.position + camToOriginVector.normalized * currentDistance;
        }

        public void OnDeath()
        {
            _originOffset = Vector3.zero;
            transform.LookAt(_origin, Vector3.up);
            _collisionAvoidance = false;
        }

        void SetCursorVisibility()
        {
            Cursor.visible = _cursorVisible;
            Cursor.lockState = _cursorVisible ? CursorLockMode.None : CursorLockMode.Locked;
        }

    }
}

That’s not here. You have to write seperate scripts for movement. This script will only make the camera follow the player. Now, if you want the player to move, tell me your platform and your mode (3D/2D) Anyways, if you’re making for PC on 3D, then use this. This will make the player move:

using UnityEngine;
using System.Collections;

// This script moves the character controller forward
// and sideways based on the arrow keys.
// It also jumps when pressing space.
// Make sure to attach a character controller to the same game object.
// It is recommended that you make only one call to Move or SimpleMove per frame.
//Subscribe to ANF Studios YT on YouTube

public class PlayerMovement: MonoBehaviour
{
    CharacterController characterController;

    public float speed = 6.0f;
    public float jumpSpeed = 8.0f;
    public float gravity = 20.0f;

    private Vector3 moveDirection = Vector3.zero;

    void Start()
    {
        characterController = GetComponent<CharacterController>();
    }

    void Update()
    {
        if (characterController.isGrounded)
        {
            // We are grounded, so recalculate
            // move direction directly from axes

            moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0.0f, Input.GetAxis("Vertical"));
            moveDirection *= speed;

            if (Input.GetButton("Jump"))
            {
                moveDirection.y = jumpSpeed;
            }
        }

        // Apply gravity. Gravity is multiplied by deltaTime twice (once here, and once below
        // when the moveDirection is multiplied by deltaTime). This is because gravity should be applied
        // as an acceleration (ms^-2)
        moveDirection.y -= gravity * Time.deltaTime;

        // Move the controller
        characterController.Move(moveDirection * Time.deltaTime);
    }
}

If this is not what controls you’re looking for. Let me know. And if that’s the case RIP this really big script X_X
Steps:

  • Make a C# script called: PlayerMovement
  • Paste this script into PlayerMovement
  • Apply (this script to inspector) of the object (in your case, player) you want to move
  • Test the script for your desired speed after

If this is not what you are looking for try this script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraScript : MonoBehaviour
{
    float speed = 10.0f;

    // Use this for initialization
    void Start()
    {

    }

    void Update()
    {
        if (Input.GetAxis("Mouse X") > 0)
        {
            transform.position += new Vector3(Input.GetAxisRaw("Mouse X") * Time.deltaTime * speed,
                                       0.0f, Input.GetAxisRaw("Mouse Y") * Time.deltaTime * speed);
        }

        else if (Input.GetAxis("Mouse X") < 0)
        {
            transform.position += new Vector3(Input.GetAxisRaw("Mouse X") * Time.deltaTime * speed,
                                       0.0f, Input.GetAxisRaw("Mouse Y") * Time.deltaTime * speed);
        }
    }

}

This would move the screen i 1st person and 3rd person.
Let me know if it works

Steps:

  • Same steps as above except that you insert it to the camera

Oh and one more thing, if your game is 1st person, change the value to 0

Thank you very much for the code but I want to learn more instead of copying code. Your code looks very complex and I’m not sure what it does internally, with the Quaternions and all that. :frowning:

Thanks. I already have a player movement script, I just want a camera movement script that takes the mouse or controller analog stick and pivot moves around the player at a fixed distance. The second code you gave doesn’t work for that for some reason. Could you use the new Input System code?

The quaternion stuff is copied right from the standardAssets Character controller, I’ve no idea what it actually does - it just clamps a rotation along one axis…
Everything else should be quite clear. ^^
basically it’s just a camera parented to an empty gameObject, which follows the player - the setup was wrong btw - the origin should not be parented to the player character, it follows the character smoothly.

so it works basically as if you have a fist person controller and parent the camera to the firstPersonCamera, so it orbits the character.

To avoid the case where the character is hidden by an object, the camera moves towards the player if it detects such an object - actually pretty straight forward logic.

1 Like

All this copy paste thing sure is a bummer. Every newbie (including me, I am not an AAA game developer) needs some place to learn. YouTube is a great and free source, but there are very little YouTubers that actually explain what’s going on. My favourite YouTuber (who mostly does 3D stuff, but some 2D) is Brackeys. Compared to all other’s he has the top league (of knowledge and subscribers, lol). Check his channel out. Literary the first script I provided you was from him XD. Anyways, did you try the script below the player controls? I don’t develop 3D, so I do not know if it will work.
Let me (try to) explain the code:

//This is what resources you are using for the scripts, you mostly don't need to worry about it
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//You are starting your script as MonoBehaviour with the name of the script: CameraScript
public class CameraScript : MonoBehaviour
{
//Float is used to define numeric values with floating decimal points, in other words a value of number in our case this is movement speed of camrea
    float speed = 10.0f;

    // This is used to start your script
    void Start()
    {

    }

//You are refreshing in-game contents over and over
    void Update()
    {
//If an input of the mouse is detected in X axis (right) and the value is greater than 0
        if (Input.GetAxis("Mouse X") > 0)
        {
//then from the current position at the time * speed of your movement
            transform.position += new Vector3(Input.GetAxisRaw("Mouse X") * Time.deltaTime * speed,
  //for Y axis (up)
                                     0.0f, Input.GetAxisRaw("Mouse Y") * Time.deltaTime * speed);
        }
//if not the above then this statment (left and for y down)
        else if (Input.GetAxis("Mouse X") < 0)
        {
            transform.position +new Vector3(Input.GetAxisRaw("Mouse X") * Time.deltaTime * speed,
                                       0.0f, Input.GetAxisRaw("Mouse Y") * Time.deltaTime * speed);
        }
    }
//Hope I could help, because I am too still learning
}

Now, follow the instructions above. If it does not work (I do not know if it works because I work on 2D) though it should, watch this video of Brackeys:

Also, you never told me, what platform is it for and what type first person of 3rd person

1 Like

This:

1 Like

Also if you are starting out - idk, I learnt programming by copy&paste, I looked out for scripts, tried to read them, altered them, used parts of another script to plug it into the first script and tadaa after a couple of hours it was magically doing what I wanted it to do - back then my english was horrible and I had no idea what I was doing but somehow, I understood more and more how things work … I learnt about loops, conditions, how to reference scripts from other scripts … then algorithms, invented some and made various things.
The learning part was completely natural, I actually never wanted to learn anything, I just wanted to create.
Later, with more complex projects I understood that I had to learn to write code with long term value, extensibility, modularity.
I have no idea how to learn programming actually, at some point you can look at it and understand it - it’s like learning every other language, use it to learn it - learning it from books or videos or websites? Maybe this works for some people, I just learnt it by writing games. I can’t tell you how to start other than reading and writing code - check the API, check the User Manual provided by Unity (thou, some informations on the manual, especially in the beginner coding section are wrong and I reported them …) and just try to use the provided information to hack something together.

GameDev is fulfilling, whatever you do, you get some kind of visual feedback, so just start, just copy and paste and read and write - thats atleast what worked for me ^^

1 Like

Oh and Quaternions are just 4 float values (xyzw) , they define an axis (Vector3 xyz) and a rotation around this axis (float w).
They are used because of gimbal lock - when using euler angles, it can happen, that a certain rotation is not possible - you can google “gimbal lock” on youtube to find a good visual representation of the problem.

The only problem with Quaternions is, they are insanely complex - you can’t just add them like euler angles - also to keep things somewhat simpler, Unitys Quaternions are normalized, so the length of the theoretical 4D-Vector is 1 (if I remember this correct) - to add them, you have to multiply them, which will call a custom function to actually add them.
Also worth mentioning is that every Quaterion is absolute, it is always a rotation from zero … but … the more I think about this, thats also true for position values.

So yea, Quaternion is an Axis (Vector3) and a rotation around this axis - represented by 4 values xyzw and they are complicated things. Using them works best when you use the Methods provided by Unity such as Quaternion.FromToRotation() or Quaternion.Euler()

1 Like

Thank you so much both of you! Right now I’m still struggling getting it to work, copied all your codes and I get errors and cannot figure out what’s wrong.

I would like to know the logic behind making a third person camera that can be controlled by a mouse or controller, like the step by step process of code. This way I can build the most simplest third person camera on my own. :slight_smile:

I just found a new video with a very easy solution! I will be still here if anyone has a simple approach, but here it is:

Lol, I showed you this video way earlier (no offense)

No you didn’t, that was the first person video, scroll up. :confused:

(

(No offense, do not take any of this seriously) That is because you never told me was your game first person or third person, lol. Sorry thoiugh, I kept thinking that your game was 3rd person, that is why I told you to set the z value to -10. Anyways I did recommend you this channel earlier. Oof, overall

So, aside from all this and that. Do you need any more help?
I am always, here (except when I am offline. What do you expect ¯_(ツ)_/¯, lol)

No problem, thanks for helping!

Sure, I’ll post here if I come across a question. Thanks so much! ^^

Right now I’m trying to go in the direction of the camera like in Brackey’s video, but I’m not sure how to do it since I’m not using a character controller. I’m using the rigidbody force to move the player.

        //Keyboard
        Vector2 kmove = Vector2.zero;

        if(keyboard != null)
        {
            if (keyboard.wKey.isPressed)
            {
                kmove.y = 1f;
            }
            if (keyboard.sKey.isPressed)
                kmove.y = -1f;
            if (keyboard.aKey.isPressed)
                kmove.x = -1f;
            if (keyboard.dKey.isPressed)
                kmove.x = 1f;
            if (canJump == true && keyboard.spaceKey.isPressed)
            {
                rb.velocity = Vector2.up * jumpspeed * Time.deltaTime;
                canJump = false;
                    //rb.AddForce(0, JumpForce * jumpspeed * Time.deltaTime, 0);


            }

            kmove.Normalize();

            rb.AddForce(-kmove.y * speed * Time.deltaTime, 0, kmove.x * speed * Time.deltaTime);
        }

So from this I want to know how I can change the code so that it will always go in the direction of the main camera. Any ideas?

So, technically, y camera doess not move with the player?
(Once again, I have no expierence with 3D games), I gave you a script earlier, i.e:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//You are starting your script as MonoBehaviour with the name of the script: CameraScript
public class CameraScript : MonoBehaviour
{
//Float is used to define numeric values with floating decimal points, in other words a value of number in our case this is movement speed of camrea
    float speed = 10.0f;
    // This is used to start your script
    void Start()
    {
    }
//You are refreshing in-game contents over and over
    void Update()
    {
//If an input of the mouse is detected in X axis (right) and the value is greater than 0
        if (Input.GetAxis("Mouse X") > 0)
        {
//then from the current position at the time * speed of your movement
            transform.position += new Vector3(Input.GetAxisRaw("Mouse X") * Time.deltaTime * speed,
  //for Y axis (up)
                                     0.0f, Input.GetAxisRaw("Mouse Y") * Time.deltaTime * speed);
        }
//if not the above then this statment (left and for y down)
        else if (Input.GetAxis("Mouse X") < 0)
        {
            transform.position +new Vector3(Input.GetAxisRaw("Mouse X") * Time.deltaTime * speed,
                                       0.0f, Input.GetAxisRaw("Mouse Y") * Time.deltaTime * speed);
        }
    }
//Hope I could help, because I am too still learning
}

I don’t quite know, but I think this will work, try this script unitl I come up with some other script (just incase)

1 Like