找回密码
 立即注册
查看: 347|回复: 0

【Unity】基于Cinemachine实现的第三人称摄像机

[复制链接]
发表于 2022-4-18 09:13 | 显示全部楼层 |阅读模式
实现了人物基本的走,跑,跳,滞空状态,并有物理模拟
相机通过设置Cinemachine,实现了旋转,锁定,缩放,并且自带摄像机碰撞,不会穿墙
using System.Collections;using System.Collections.Generic;using UnityEngine;using Cinemachine;public class PlayerController : MonoBehaviour{    [Header("Player")]    [Tooltip("移动速度")]    public float MoveSpeed = 2.0f;    [Tooltip("加速移动速度")]    public float SprintSpeed = 5.335f;    [Tooltip("旋转速度")]    [Range(0.0f, 0.3f)]    public float RotationSmoothTime = 0.12f;    [Tooltip("加速度")]    public float SpeedChangeRate = 10.0f;    [Space(10)]    [Tooltip("跳跃高度")]    public float JumpHeight = 1.2f;    [Tooltip("重力,默认为 -9.81f")]    public float Gravity = -15.0f;    [Space(10)]    [Tooltip("跳跃间隔时间")]    public float JumpTimeout = 0.50f;    [Tooltip("Time required to pass before entering the fall state. Useful for walking down stairs")]    public float FallTimeout = 0.15f;    [Header("Player Grounded")]    [Tooltip("当前是否在地面")]    public bool Grounded = true;    [Tooltip("粗糙地面偏移量")]    public float GroundedOffset = -0.14f;    [Tooltip("地面检测的半径,应该和CharacterController的半径匹配")]    public float GroundedRadius = 0.28f;    [Tooltip("地面有哪些层")]    public LayerMask GroundLayers;    [Header("CinemachineTarget")]    [Tooltip("虚拟相机目标")]    public GameObject CinemachineCameraTarget;    [Tooltip("最大仰角")]    public float TopClamp = 80;    [Tooltip("最小俯角")]    public float BottomClamp = -80;    [Tooltip("额外的角度来调整摄像机,用与当相机锁住的时候")]    public float CameraAngleOverride = 0.0f;    [Tooltip("相机灵敏度")]    public float MouseSensitivity = 200;    [Tooltip("虚拟相机")]    public Cinemachine3rdPersonFollow CinemachineVirtualCamera;    [Tooltip("相机缩放")]    public float CameraDistance = 3;    [Tooltip("相机缩放")]    public float CameraDistanceRatio = 5;    [Tooltip("相机缩放最小距离")]    public float CameraDistanceMin = 2;    [Tooltip("相机缩放最大距离")]    public float CameraDistanceMax = 8;    [Tooltip("相机锁")]    public bool LockCameraPosition = false;    // cinemachine    private float _cinemachineTargetYaw;    private float _cinemachineTargetPitch;    private float _cinemachineTargetDistance;    // player    private float _speed;    private float _animationBlend;    private float _targetRotation = 0.0f;    private float _rotationVelocity;    private float _verticalVelocity;    private float _terminalVelocity = 53.0f;    // state    private bool isRun = false;    private bool isJump = false;    // timeout deltatime    private float _jumpTimeoutDelta;    private float _fallTimeoutDelta;    // animation IDs    private int _animIDSpeed;    private int _animIDGrounded;    private int _animIDJump;    private int _animIDFreeFall;    private int _animIDMotionSpeed;    private Animator _animator;    private CharacterController _controller;    private GameObject _mainCamera;    private bool _hasAnimator;    private void Awake()    {        // get a reference to our main camera        if (_mainCamera == null)        {            _mainCamera = GameObject.FindGameObjectWithTag("MainCamera");        }    }    private void Start()    {        _hasAnimator = TryGetComponent(out _animator);        _controller = GetComponent<CharacterController>();        var cinemachine = FindObjectOfType<CinemachineVirtualCamera>();        CinemachineVirtualCamera = cinemachine.GetCinemachineComponent<Cinemachine3rdPersonFollow>();        AssignAnimationIDs();        // reset our timeouts on start        _jumpTimeoutDelta = JumpTimeout;        _fallTimeoutDelta = FallTimeout;        Cursor.lockState = CursorLockMode.Locked;    }    private void Update()    {        if (Input.GetKey(KeyCode.LeftShift))        {            isRun = true;        }        if (Input.GetKeyUp(KeyCode.LeftShift))        {            isRun = false;        }        if (Input.GetKeyDown(KeyCode.Space))        {            isJump = true;        }        if (Input.GetKeyDown(KeyCode.L))        {            LockCameraPosition = !LockCameraPosition;        }        _cinemachineTargetDistance -= Input.GetAxis("Mouse ScrollWheel") * CameraDistanceRatio;        _cinemachineTargetDistance = Mathf.Clamp(_cinemachineTargetDistance, CameraDistanceMin, CameraDistanceMax);    }    private void FixedUpdate()    {        _hasAnimator = TryGetComponent(out _animator);        JumpAndGravity();        GroundedCheck();        Move();    }    public float cameraDistanceLerpSpeed = 1;    private void LateUpdate()    {        CameraPosition();        CameraRotation();    }    private void AssignAnimationIDs()    {        _animIDSpeed = Animator.StringToHash("Speed");        _animIDGrounded = Animator.StringToHash("Grounded");        _animIDJump = Animator.StringToHash("Jump");        _animIDFreeFall = Animator.StringToHash("FreeFall");        _animIDMotionSpeed = Animator.StringToHash("MotionSpeed");    }    /// <summary>    /// 地面检测    /// </summary>    private void GroundedCheck()    {        // 得到检测点位置        Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z);        // 检测结果        Grounded = Physics.CheckSphere(spherePosition, GroundedRadius, GroundLayers, QueryTriggerInteraction.Ignore);        // 设置Animator        if (_hasAnimator)        {            _animator.SetBool(_animIDGrounded, Grounded);        }    }    private void CameraPosition()    {        CameraDistance = Mathf.Lerp(CameraDistance, _cinemachineTargetDistance, Time.deltaTime * cameraDistanceLerpSpeed);        CinemachineVirtualCamera.CameraDistance = CameraDistance;    }    private void CameraRotation()    {        var mouseX = Input.GetAxis("Mouse X") * MouseSensitivity;        var mouseY = Input.GetAxis("Mouse Y") * MouseSensitivity;        _cinemachineTargetYaw += mouseX * Time.deltaTime;        _cinemachineTargetPitch -= mouseY * Time.deltaTime;        _cinemachineTargetYaw = ClampAngle(_cinemachineTargetYaw, float.MinValue, float.MaxValue);        _cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, BottomClamp, TopClamp);        var targetRot = Quaternion.Euler(_cinemachineTargetPitch, _cinemachineTargetYaw, 0);        CinemachineCameraTarget.transform.rotation = targetRot;    }    /// <summary>    /// 角色移动    /// </summary>    private void Move()    {        // 获取到当前的输入向量        Vector3 curInput = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));        // 根据是否按下加速键,获得最终速度值        float targetSpeed = isRun ? SprintSpeed : MoveSpeed;        // 如果输入的数值太小则不计算        if (curInput == Vector3.zero) targetSpeed = 0.0f;        // 获取玩家当前在水平面上的单位速度        float currentHorizontalSpeed = new Vector3(_controller.velocity.x, 0.0f, _controller.velocity.z).magnitude;        float speedOffset = 0.1f;        //float inputMagnitude = _input.analogMovement ? _input.move.magnitude : 1f;        // 模拟加速过程        if (currentHorizontalSpeed < targetSpeed - speedOffset || currentHorizontalSpeed > targetSpeed + speedOffset)        {            // 模拟一个非线性的加速过程            _speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * curInput.magnitude, Time.fixedDeltaTime * SpeedChangeRate);            // 精确到小数点后3位            _speed = Mathf.Round(_speed * 1000f) / 1000f;        }        else        {            _speed = targetSpeed;        }        // 设置动画的速度        _animationBlend = Mathf.Lerp(_animationBlend, targetSpeed, Time.fixedDeltaTime * SpeedChangeRate);        // 获取玩家的输入单位水平向量        Vector3 inputDirection = new Vector3(curInput.x, 0.0f, curInput.z).normalized;        // Vector2's != 更节省性能        if (curInput != Vector3.zero)        {            _targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg + _mainCamera.transform.eulerAngles.y;            float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, _targetRotation, ref _rotationVelocity, RotationSmoothTime);            // 旋转到相对于摄像机的方向            transform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);        }        // 获取移动的目标方向        Vector3 targetDirection = Quaternion.Euler(0.0f, _targetRotation, 0.0f) * Vector3.forward;        // 移动玩家,水平面的移动 + 垂直方向的移动        _controller.Move(targetDirection.normalized * (_speed * Time.fixedDeltaTime) + new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime);        // 设置Animator        if (_hasAnimator)        {            _animator.SetFloat(_animIDSpeed, _animationBlend);            _animator.SetFloat(_animIDMotionSpeed, curInput.magnitude);        }    }    /// <summary>    /// 角色跳跃    /// </summary>    private void JumpAndGravity()    {        // 在地面上        if (Grounded)        {            // 重置下落时间            _fallTimeoutDelta = FallTimeout;            // 设置Animator            if (_hasAnimator)            {                _animator.SetBool(_animIDJump, false);                _animator.SetBool(_animIDFreeFall, false);            }            // 快速下落            if (_verticalVelocity < 0.0f)            {                _verticalVelocity = -2f;            }            // 如果还在跳跃            if (isJump && _jumpTimeoutDelta <= 0.0f)            {                // 求出垂直速度                // the square root of H * -2 * G = how much velocity needed to reach desired height                _verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity);                // 设置Animator                if (_hasAnimator)                {                    _animator.SetBool(_animIDJump, true);                }            }            // 跟新跳跃的状态数值            if (_jumpTimeoutDelta >= 0.0f)            {                _jumpTimeoutDelta -= Time.fixedDeltaTime;            }        }        //在半空中        else        {            // 重置跳跃时间            _jumpTimeoutDelta = JumpTimeout;            // 如果还在下落            if (_fallTimeoutDelta >= 0.0f)            {                _fallTimeoutDelta -= Time.fixedDeltaTime;            }            else            {                // 设置Animator                if (_hasAnimator)                {                    _animator.SetBool(_animIDFreeFall, true);                }            }            // 在半空中不能跳跃            isJump = false;        }        // 跟新速度为 Vt = V0 + a * t        if (_verticalVelocity < _terminalVelocity)        {            _verticalVelocity += Gravity * Time.fixedDeltaTime;        }    }    private static float ClampAngle(float lfAngle, float lfMin, float lfMax)    {        if (lfAngle < -360f) lfAngle += 360f;        if (lfAngle > 360f) lfAngle -= 360f;        return Mathf.Clamp(lfAngle, lfMin, lfMax);    }    private void OnDrawGizmosSelected()    {        Color transparentGreen = new Color(0.0f, 1.0f, 0.0f, 0.35f);        Color transparentRed = new Color(1.0f, 0.0f, 0.0f, 0.35f);        if (Grounded) Gizmos.color = transparentGreen;        else Gizmos.color = transparentRed;        // when selected, draw a gizmo in the position of, and matching radius of, the grounded collider        Gizmos.DrawSphere(new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z), GroundedRadius);    }}
改编自Unity商城的官方第三人称视角
https://assetstore.unity.com/packages/essentials/starter-assets-third-person-character-controller-196526
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-16 20:30 , Processed in 0.093432 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表