目录

  • 前言
  • 一、gif演示
  • 二、代码解释部分原理实现
  • 1.围绕旋转
  • 2.完成跟随与旋转
  • 3.速度的插值
  • 4.摄像机避开障碍物
  • 三、完整代码和视频演示
  • 总结



前言

今天给大家分享一下一个简单的人物跟随摄像头,具有跟随,转向,检测障碍、放大缩小这些基本功能


提示:以下是本篇文章正文内容,下面案例可供参考

一、gif演示

unity 怎么添加一个小摄像头 unity如何添加摄像机_ide

二、代码解释部分原理实现

1.围绕旋转

首先是摄像机对人物的追踪,这里补充一下,四元数和向量之间是可以乘除运算的,得到的结果是这个向量进行旋转得到的新向量,详情可看四元数与三维向量相乘运算 这样我们得出的结果就是相机围绕某一位置下的坐标了

Quaternion targetRotation = Quaternion.Euler(yMouse, xMouse, 0);
            //相机移动的目标位置
            CamCheck(out RaycastHit hit, out float dis);
            Vector3 targetPostion = target.position + targetRotation * new Vector3(xOffset, 0, -dis) + target.GetComponent<CapsuleCollider>().center * 1.75f;
2.完成跟随与旋转

在这里我们用插值处理摄像机的跟随,Vector3.Lerp和Quaternion.Lerp通过a+(b-a)t进行计算,完成了跟随与旋转的过度,使之更自然。

//使用Lerp插值,实现相机的跟随
            transform.position = Vector3.Lerp(transform.position, targetPostion, Time.deltaTime * speed);
            transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, Time.deltaTime * 25f);

3.速度的插值

在这里,我对速度也进行了插值,并用三元表达式得出了速度,让摄像机开始的时候慢一些,让人物冲刺更明显

//对速度进行插值,使之更有冲刺感
            speed = target.GetComponent<Rigidbody>().velocity.magnitude > 0.1f ?
   Mathf.Lerp(speed, 5, 5f * Time.deltaTime) : Mathf.Lerp(speed, 25, 5f * Time.deltaTime);

4.摄像机避开障碍物

在这里,我让人物发射射线的方式,利用Physics.Raycast以摄像机的位置作为方向,发射射线,之间如果有碰撞体接触到射线,就获取碰撞点坐标,重新计算距离,得到结果。

private void CamCheck(out RaycastHit raycast, out float dis)
    {
        //用来检测碰撞
#if UNITY_EDITOR
        Debug.DrawLine(target.position + target.GetComponent<CapsuleCollider>().center * 1.75f,
            target.position + target.GetComponent<CapsuleCollider>().center * 1.75f +
            (transform.position - target.position - target.GetComponent<CapsuleCollider>().center * 1.75f).normalized * distanceFromTarget
            , Color.blue);
#endif
        //如果碰撞到物体,获取碰撞点信息,重新计算距离,否则返回默认值
        if (Physics.Raycast(target.position + target.GetComponent<CapsuleCollider>().center * 1.75f,
          (transform.position - target.position - target.GetComponent<CapsuleCollider>().center * 1.75f).normalized, out raycast,
           distanceFromTarget, ~Physics.IgnoreRaycastLayer))
        {
            dis = Vector3.Distance(target.position + target.GetComponent<CapsuleCollider>().center * 1.75f + new Vector3(xOffset, 0, 0), raycast.point);
        }
        else
            dis = distanceFromTarget;
    }

三、完整代码和视频演示

using UnityEngine;

[RequireComponent(typeof(Camera))]
public class SmartCamera : MonoBehaviour
{
    [Header("要跟随的人物")]
    public Transform target = null;

    [Header("鼠标滑动速度")]
    [Range(0, 1)]
    public float linearSpeed = 1;
    [Header("摄像机与玩家距离")]
    [Range(2, 15)]
    public float distanceFromTarget = 5;
    [Header("摄像机速度")]
    [Range(1, 50)]
    public float speed = 5;
    [Header("x轴偏向量")]
    public float xOffset = 0.5f;


    private float yMouse;
    private float xMouse;

    // Start is called before the first frame update
    void Start()
    {
        if (target != null)
        {
            gameObject.layer = target.gameObject.layer = 2;
            Cursor.lockState = CursorLockMode.Locked;
            Cursor.visible = false;
        }
    }
    private void LateUpdate()
    {
        if (target != null)
        {
            xMouse += Input.GetAxis("Mouse X") * linearSpeed;
            yMouse -= Input.GetAxis("Mouse Y") * linearSpeed;
            yMouse = Mathf.Clamp(yMouse, -30, 80);//限制垂直方向的角度

            distanceFromTarget -= Input.GetAxis("Mouse ScrollWheel") * 10;//拉近或拉远人物镜头
            distanceFromTarget = Mathf.Clamp(distanceFromTarget, 2, 15);
            //用户操作的鼠标旋转和相机旋转的切换
            Quaternion targetRotation = Quaternion.Euler(yMouse, xMouse, 0);
            //相机移动的目标位置
            CamCheck(out RaycastHit hit, out float dis);
            Vector3 targetPostion = target.position + targetRotation * new Vector3(xOffset, 0, -dis) + target.GetComponent<CapsuleCollider>().center * 1.75f;
            //对速度进行插值,使之更有冲刺感
            speed = target.GetComponent<Rigidbody>().velocity.magnitude > 0.1f ?
   Mathf.Lerp(speed, 7, 5f * Time.deltaTime) : Mathf.Lerp(speed, 25, 5f * Time.deltaTime);
            //使用Lerp插值,实现相机的跟随
            transform.position = Vector3.Lerp(transform.position, targetPostion, Time.deltaTime * speed);
            transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, Time.deltaTime * 25f);

        }
    }
    private void CamCheck(out RaycastHit raycast, out float dis)
    {
        //用来检测碰撞
#if UNITY_EDITOR
        Debug.DrawLine(target.position + target.GetComponent<CapsuleCollider>().center * 1.75f,
            target.position + target.GetComponent<CapsuleCollider>().center * 1.75f +
            (transform.position - target.position - target.GetComponent<CapsuleCollider>().center * 1.75f).normalized * distanceFromTarget
            , Color.blue);
#endif
        //如果碰撞到物体,获取碰撞点信息,重新计算距离,否则返回默认值
        if (Physics.Raycast(target.position + target.GetComponent<CapsuleCollider>().center * 1.75f,
          (transform.position - target.position - target.GetComponent<CapsuleCollider>().center * 1.75f).normalized, out raycast,
           distanceFromTarget, ~Physics.IgnoreRaycastLayer))
        {
            dis = Vector3.Distance(target.position + target.GetComponent<CapsuleCollider>().center * 1.75f + new Vector3(xOffset, 0, 0), raycast.point);
        }
        else
            dis = distanceFromTarget;
    }
    public void CursorArise()
    {
        if (Input.GetKeyUp(KeyCode.Escape) && Cursor.visible == false)
        {
            Cursor.lockState = CursorLockMode.Confined;
            Cursor.visible = true;
        }
    }
    public void Teleport(Vector3 position, Quaternion rotation)
    {
        transform.position = position;
        transform.rotation = rotation;

        if (target != null)
        {

        }
    }
}


视频


总结

冲刺鼠标的旋转有点不舒服以及射线检测的部分碰撞间隙让摄像机可能会抖动外,没有什么其他问题