如何用unity制作一个简易好用的第三人称摄像机跟随


前言

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


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

一、gif演示

请添加图片描述

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

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.LerpQuaternion.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)
        {

        }
    }
}

视频

总结

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


  • 30
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值