【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]