Untiy 第三人称旋转相机

之前写过一个旋转相机的脚本,这个比那个增加了相机会移动到遮挡物体的前面。

TODO:如果距离物体过近的话,物体会改变成半透明。

using UnityEngine;

//第三人称相机.
public class ThirdPersonOrbitCamBasic : MonoBehaviour 
{
    public Transform player;                                           // 玩家transform.
    public Vector3 pivotOffset = new Vector3(0.0f, 1.0f,  0.0f);       // 相机锚点.
    public Vector3 camOffset   = new Vector3(0.4f, 0.5f, -2.0f);       // 相机相对玩家位置.
    public float smooth = 10f;                                         // 相机平滑值.
    public float horizontalAimingSpeed = 6f;                           // 水平旋转速度.
    public float verticalAimingSpeed = 6f;                             // 垂直旋转速度.
    public float maxVerticalAngle = 30f;                               // 相机最大垂直角度. 
    public float minVerticalAngle = -60f;                              // 相机最小垂直角度.
    public string XAxis = "Analog X";                                  // 水平轴名称.
    public string YAxis = "Analog Y";                                  // 垂直轴名称.

    private float angleH = 0;                                          
    private float angleV = 0;                                          
    private Transform cam;                                             // 相机Transform.
    private Vector3 relCameraPos;                                      // 相机当前相对于物体的坐标.
    private float relCameraPosMag;                                     // 相机当前与物体的距离.
    private Vector3 smoothPivotOffset;                                 
    private Vector3 smoothCamOffset;                                   
    private Vector3 targetPivotOffset;                                 
    private Vector3 targetCamOffset;                                   
    private float defaultFOV;                                          // 相机默认FOV.
    private float targetFOV;                                           // 相机要修改成的目标FOV.
    private float targetMaxVerticalAngle;                              // 自定义相机最大垂直角度.

    // 获取相机垂直旋转角度值.
    public float GetH { get { return angleH; } }

    void Awake()
    {
        cam = transform;

        // 设置相机默认位置.
        cam.position = player.position + Quaternion.identity * pivotOffset + Quaternion.identity * camOffset;
        cam.rotation = Quaternion.identity;

        // 获取相机相对物体位置, 准备做射线检测.
        relCameraPos = transform.position - player.position;
        relCameraPosMag = relCameraPos.magnitude - 0.5f;

        // 设置默认值.
        smoothPivotOffset = pivotOffset;
        smoothCamOffset = camOffset;
        defaultFOV = cam.GetComponent<Camera>().fieldOfView;
        angleH = player.eulerAngles.y;

        ResetTargetOffsets ();
        ResetFOV ();
        ResetMaxVerticalAngle();
    }

    void Update()
    {
        // 相机环绕输入.
        // 鼠标输入:
        angleH += Mathf.Clamp(Input.GetAxis("Mouse X"), -1, 1) * horizontalAimingSpeed;
        angleV += Mathf.Clamp(Input.GetAxis("Mouse Y"), -1, 1) * verticalAimingSpeed;
        // 摇杆输入:
        angleH += Mathf.Clamp(Input.GetAxis(XAxis), -1, 1) * 60 * horizontalAimingSpeed * Time.deltaTime;
        angleV += Mathf.Clamp(Input.GetAxis(YAxis), -1, 1) * 60 * verticalAimingSpeed * Time.deltaTime;

        // 垂直旋转限制.
        angleV = Mathf.Clamp(angleV, minVerticalAngle, targetMaxVerticalAngle);

        // 设置相机旋转.
        Quaternion camYRotation = Quaternion.Euler(0, angleH, 0);
        Quaternion aimRotation = Quaternion.Euler(-angleV, angleH, 0);
        cam.rotation = aimRotation;

        // 设置相机FOV.
        cam.GetComponent<Camera>().fieldOfView = Mathf.Lerp (cam.GetComponent<Camera>().fieldOfView, targetFOV,  Time.deltaTime);

        // 碰撞检测 检查是否有物体夹在相机和物体中间.
        // 如果碰到障碍物,就向物体方向移动0.5再进行一次检测,只到中间没有障碍物为止.
        Vector3 baseTempPosition = player.position + camYRotation * targetPivotOffset;
        Vector3 noCollisionOffset = targetCamOffset;
        for(float zOffset = targetCamOffset.z; zOffset <= 0; zOffset += 0.5f)
        {
            noCollisionOffset.z = zOffset;
            if (DoubleViewingPosCheck (baseTempPosition + aimRotation * noCollisionOffset, Mathf.Abs(zOffset)) || zOffset == 0) 
            {
                break;
            } 
        }

        // 设置相机位置.
        smoothPivotOffset = Vector3.Lerp(smoothPivotOffset, targetPivotOffset, smooth * Time.deltaTime);
        smoothCamOffset = Vector3.Lerp(smoothCamOffset, noCollisionOffset, smooth * Time.deltaTime);

        cam.position =  player.position + camYRotation * smoothPivotOffset + aimRotation * smoothCamOffset;
    }

    // 把相机设置到自定义位置.
    public void SetTargetOffsets(Vector3 newPivotOffset, Vector3 newCamOffset)
    {
        targetPivotOffset = newPivotOffset;
        targetCamOffset = newCamOffset;
    }

    // 重置相机到初始值.
    public void ResetTargetOffsets()
    {
        targetPivotOffset = pivotOffset;
        targetCamOffset = camOffset;
    }

    // 设置相机的FOV.
    public void SetFOV(float customFOV)
    {
        this.targetFOV = customFOV;
    }

    // 重置相机FOV.
    public void ResetFOV()
    {
        this.targetFOV = defaultFOV;
    }

    // 设置相机最大垂直旋转角度.
    public void SetMaxVerticalAngle(float angle)
    {
        this.targetMaxVerticalAngle = angle;
    }

    // 重置相机最大旋转角度.
    public void ResetMaxVerticalAngle()
    {
        this.targetMaxVerticalAngle = maxVerticalAngle;
    }

    // 两个方向碰撞检测.
    bool DoubleViewingPosCheck(Vector3 checkPos, float offset)
    {
        float playerFocusHeight = player.GetComponent<CapsuleCollider> ().height * 0.75f;
        return ViewingPosCheck (checkPos, playerFocusHeight) && ReverseViewingPosCheck (checkPos, playerFocusHeight, offset);
    }

    // 相机到物体碰撞检测.
    bool ViewingPosCheck (Vector3 checkPos, float deltaPlayerHeight)
    {
        Vector3 target = player.position + (Vector3.up * deltaPlayerHeight);
        if (Physics.SphereCast(checkPos, 0.2f, target - checkPos, out RaycastHit hit, relCameraPosMag))
        {
            if(hit.transform != player && !hit.transform.GetComponent<Collider>().isTrigger)
            {
                return false;
            }
        }
        return true;
    }

    // 物体到相机碰撞检测.
    bool ReverseViewingPosCheck(Vector3 checkPos, float deltaPlayerHeight, float maxDistance)
    {
        Vector3 origin = player.position + (Vector3.up * deltaPlayerHeight);
        if (Physics.SphereCast(origin, 0.2f, checkPos - origin, out RaycastHit hit, maxDistance))
        {
            if(hit.transform != player && hit.transform != transform && !hit.transform.GetComponent<Collider>().isTrigger)
            {
                return false;
            }
        }
        return true;
    }

    public float GetCurrentPivotMagnitude(Vector3 finalPivotOffset)
    {
        return Mathf.Abs ((finalPivotOffset - smoothPivotOffset).magnitude);
    }
}

 

点赞

发表回复

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像