redhat9i 发表于 2022-4-18 09:13

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

实现了人物基本的走,跑,跳,滞空状态,并有物理模拟
相机通过设置Cinemachine,实现了旋转,锁定,缩放,并且自带摄像机碰撞,不会穿墙
using System.Collections;using System.Collections.Generic;using UnityEngine;using Cinemachine;public class PlayerController : MonoBehaviour{            public float MoveSpeed = 2.0f;        public float SprintSpeed = 5.335f;            public float RotationSmoothTime = 0.12f;        public float SpeedChangeRate = 10.0f;            public float JumpHeight = 1.2f;        public float Gravity = -15.0f;            public float JumpTimeout = 0.50f;        public float FallTimeout = 0.15f;            public bool Grounded = true;        public float GroundedOffset = -0.14f;        public float GroundedRadius = 0.28f;        public LayerMask GroundLayers;            public GameObject CinemachineCameraTarget;        public float TopClamp = 80;        public float BottomClamp = -80;        public float CameraAngleOverride = 0.0f;        public float MouseSensitivity = 200;        public Cinemachine3rdPersonFollow CinemachineVirtualCamera;        public float CameraDistance = 3;        public float CameraDistanceRatio = 5;        public float CameraDistanceMin = 2;        public float CameraDistanceMax = 8;        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
页: [1]
查看完整版本: 【Unity】基于Cinemachine实现的第三人称摄像机