Hark-the-Moon-Men-Cometh / Assets / Scripts / CameraBehaviour.cs
CameraBehaviour.cs
Raw
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.InputSystem;

public class CameraBehaviour : MonoBehaviour
{
    //references to relevent transforms
    public PlayerAvatar r_player;
    public Transform r_cameraPivot;
    public Transform r_cameraTransform;
    public Transform r_cameraMaxDistanceMarker;

    //These are used to transition from 3rd person to OTS cameras
    public float m_zoomTime;
    private float m_zoomClock;
    private float m_lerpA;
    public float m_minZ, m_maxZ, m_otsZ;
    public Vector3 m_defaultPivotLocation, m_otsPivotLocation;

    //These are used to limit how far up or down the player can look
    public float m_minRotationDegrees, m_maxRotationDegrees;
    private float m_minRotationCos, m_maxRotationCos;

    //Used to adjust how much closer to the player the camera moves when trying
    //to avoid wall clipping. This can stop the player being able to see through
    //the inside edge of a wall.
    public float m_wallClipDistanceFactor;

    //They make do stuff
    private InputAction m_lookAction;    

    private void Awake()
    {
        m_minRotationCos = Mathf.Cos(m_minRotationDegrees * Mathf.Deg2Rad);
        m_maxRotationCos = Mathf.Cos(m_maxRotationDegrees * Mathf.Deg2Rad);
        m_zoomClock = 0f;
        m_lerpA = 0f;
    }

    void Start()
    {
        r_cameraMaxDistanceMarker.localPosition = Vector3.forward * m_maxZ;
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
        m_lookAction = InputSystem.actions.FindAction("Look");        
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        Vector2 lookValue = m_lookAction.ReadValue<Vector2>() 
            * r_player.m_lookSpeed
            * Time.deltaTime;
        this.transform.position = r_player.transform.position;
        this.transform.rotation = r_player.transform.rotation;

        PivotAroundXAxis(-lookValue.y);
        AdjustCameraDistance();        
    }

    //Does two things:
    //1) Moves the camera to the OTS position when prompted
    //2) Moves the camera forward to prevent wall clipping
    private void AdjustCameraDistance()
    {
        if (m_zoomClock > 0f || r_player.m_isOTSCamera)
        {
            if (r_player.m_isOTSCamera && m_zoomClock == 0)
            {
                m_lerpA = r_cameraTransform.localPosition.z;
            }
            else if (!r_player.m_isOTSCamera)
            {
                m_lerpA = CheckCollisionDistance();
            }
            m_zoomClock += 
                r_player.m_isOTSCamera ? Time.deltaTime : -Time.deltaTime;
            m_zoomClock = Mathf.Clamp(m_zoomClock, 0f, m_zoomTime);
            float t = m_zoomClock / m_zoomTime;
            float dZ = Mathf.Lerp(m_lerpA, m_otsZ, t);
            r_cameraTransform.localPosition = new Vector3(0f, 0f, dZ);
            r_cameraPivot.localPosition = Vector3.Lerp(m_defaultPivotLocation,
                m_otsPivotLocation, t);
        }
        else
        {
            float clipDist = CheckCollisionDistance();
            r_cameraTransform.localPosition = new Vector3(0f, 0f, clipDist 
                * m_wallClipDistanceFactor);
        }
    }


    //Checks how far away the closest wall is behind the player. If it's within
    //the normal camera range, this gives the new distance to prevent clipping
    //Always returns negative
    float CheckCollisionDistance()
    {
        RaycastHit hit;
        Vector3 direction = r_cameraMaxDistanceMarker.position 
            - r_cameraPivot.position;
        float maxDistance = direction.magnitude * m_wallClipDistanceFactor;
        LayerMask layerMask =~ LayerMask.GetMask(new string[] { "Player", 
            "Enemy" });
        float dist = float.MaxValue;
        if (Physics.Raycast(r_cameraPivot.position, direction, out hit,
            maxDistance, layerMask))
        {
            //Debug.Log(hit.transform.name);
            dist = hit.distance;
            if(dist < 0.1)
            {
                Debug.Log("Hit " + hit.collider.gameObject.name + " at " 
                    + dist + " units");
            }
        }
        return -1 * Mathf.Min(Mathf.Max(Mathf.Abs(dist), Mathf.Abs(m_minZ)), 
            Mathf.Abs(m_maxZ));
    }

    //Controls up-down look direction. Stops over-rotation, and also prevents
    //camera from clipping through the floor.
    //TODO: Stop it clipping through ceilings
    void PivotAroundXAxis(float deltaX)
    {
        Vector3 rot = r_cameraPivot.transform.rotation.eulerAngles;
        float deltaRotX = rot.x + deltaX;
        float sinRotX = Mathf.Sin(deltaRotX * Mathf.Deg2Rad);
        float cosRotX = Mathf.Cos(deltaRotX * Mathf.Deg2Rad);
        cosRotX = (sinRotX >= 0f) ? Mathf.Clamp(cosRotX, m_maxRotationCos, 1f)
            : Mathf.Clamp(cosRotX, m_minRotationCos, 1f);        
        rot.x = Mathf.Acos(cosRotX) * Mathf.Rad2Deg * ((sinRotX < 0) ? -1 : 1);
        r_cameraPivot.transform.rotation = Quaternion.Euler(rot);
    }
}