Unity 3D中为角色控制器添加相机相对移动机制求助
相机相对移动实现方案
针对你的需求,核心是让角色的移动方向基于相机的水平朝向,而非自身朝向。以下是修复后的完整代码及关键修改说明:
问题根源
- 原代码移动逻辑直接使用角色自身的
transform.forward,未关联相机朝向 - 未正确保存相机引用,且之前尝试的四元数旋转逻辑可能错误控制了相机(Cinemachine自由相机应独立处理旋转,角色无需干预)
- 移动与冲刺逻辑分离,导致冲刺时未遵循相机相对规则
修复后的完整代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { [SerializeField] private float _playerSpeed = 5f; [SerializeField] private float _sprintMultiplier = 1.8f; [SerializeField] private Rigidbody rb; [SerializeField] private float _jumpforce = 100f; [SerializeField] private float fallMultiplier = 2.5f; [SerializeField] private float lowJumpMultiplier = 2f; public bool isGrounded; public bool jumpReady; public float jumpcoolDownTimer = 1.5f; public float jumpcoolDownCurrent = 0.0f; private Camera _mainCamera; private Vector3 _moveDirection; int isJumpingHash; int isFallingHash; int isLandingHash; public Animator animator; Vector3 vel; void Start() { jumpcoolDownCurrent = jumpcoolDownTimer; Cursor.visible = false; Cursor.lockState = CursorLockMode.Locked; animator = GetComponent<Animator>(); _mainCamera = Camera.main; // 哈希值优化动画参数 isJumpingHash = Animator.StringToHash("isJumping"); isFallingHash = Animator.StringToHash("isFalling"); isLandingHash = Animator.StringToHash("isGrounded"); } void Update() { Move(); Jump(); } private void OnCollisionEnter(Collision collision){ if(collision.gameObject.tag == "Surface"){ isGrounded = true; } } private void Move() { // 获取输入轴 float horizontalInput = Input.GetAxis("Horizontal"); float verticalInput = Input.GetAxis("Vertical"); // 基于相机朝向计算移动方向,忽略Y轴保持水平 Vector3 cameraForward = _mainCamera.transform.forward; Vector3 cameraRight = _mainCamera.transform.right; cameraForward.y = 0f; cameraRight.y = 0f; cameraForward.Normalize(); cameraRight.Normalize(); // 计算最终移动方向 _moveDirection = (cameraForward * verticalInput + cameraRight * horizontalInput).normalized; // 应用移动速度,区分冲刺状态 float currentSpeed = Input.GetKey(KeyCode.LeftShift) && verticalInput > 0 ? _playerSpeed * _sprintMultiplier : _playerSpeed; vel = _moveDirection * currentSpeed; // 当有移动输入时,让角色朝向移动方向 if (_moveDirection.magnitude > 0.1f) { Quaternion targetRotation = Quaternion.LookRotation(_moveDirection); transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, Time.deltaTime * 10f); } } private void Jump(){ // 保留Y轴速度,避免覆盖跳跃/下落速度 vel.y = rb.velocity.y; rb.velocity = vel; // 跳跃冷却逻辑 jumpcoolDownCurrent += Time.deltaTime; jumpReady = jumpcoolDownCurrent >= jumpcoolDownTimer; bool isJumping = animator.GetBool(isJumpingHash); bool isFalling = animator.GetBool(isFallingHash); // 跳跃触发 if(Input.GetKeyDown(KeyCode.Space) && isGrounded && jumpReady){ rb.AddForce(Vector3.up * _jumpforce, ForceMode.Impulse); isGrounded = false; jumpcoolDownCurrent = 0.0f; animator.SetBool(isJumpingHash, true); } // 下落加速 if(rb.velocity.y < 0 && !isGrounded){ rb.velocity += Vector3.up * Physics.gravity.y * (fallMultiplier - 1) * Time.deltaTime; animator.SetBool(isFallingHash, true); } // 落地重置动画 if(isGrounded){ animator.SetBool(isJumpingHash, false); animator.SetBool(isFallingHash, false); animator.SetBool(isLandingHash, true); // 延迟重置落地动画(根据你的动画长度调整) Invoke(nameof(ResetLandingAnim), 0.3f); } } private void ResetLandingAnim() { animator.SetBool(isLandingHash, false); } }
关键修改说明
- 相机引用保存:将
Camera.main保存为成员变量_mainCamera,避免重复调用的性能损耗 - 相机相对方向计算:
- 提取相机的前向和右向向量,强制Y轴为0确保水平移动
- 通过输入轴与相机向量的组合,得到相机相对的移动方向
- 角色旋转逻辑:当有移动输入时,让角色平滑转向移动方向,符合第三人称游戏的常规表现
- 冲刺逻辑统一:冲刺状态下同样使用相机相对的移动方向,避免逻辑分裂
- 动画状态优化:修复落地动画的重置逻辑,避免状态异常
额外注意事项
- 确保Cinemachine自由相机的Body设置为
Framing Transposer,Aim设置为POV,让相机独立处理旋转,角色无需控制相机 - 检查地面检测逻辑,建议使用
OnCollisionStay或射线检测替代OnCollisionEnter,避免因碰撞检测丢失导致isGrounded状态错误
内容的提问来源于stack exchange,提问作者PrimoCop
相关产品推荐
相关产品推荐

