Unity中CharacterController velocity始终为0,头部晃动脚本失效求助
问题:HeadBobController无法获取CharacterController的velocity值
我花了两天编写头部晃动(HeadBob)脚本,始终卡在获取CharacterController的velocity环节,该值一直返回(0,0,0)。玩家对象上挂载了两个脚本:PlayerMotor负责角色移动,HeadBobController基于controller velocity计算并应用头部晃动效果。PlayerMotor中调用controller.velocity可正常获取值,但HeadBobController中始终为(0,0,0),怀疑是脚本执行顺序导致的问题?我完全按照教程操作,仅对相机和移动脚本做了细微修改。
相关脚本
PlayerMotor脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerMotor : MonoBehaviour { private CharacterController controller; private Vector3 playerVelocity; private PlayerLook look; private float speed = 5f; public float crouchSpeed = 2.5f; public float walkSpeed = 5f; public float sprintSpeed = 8f; public float jumpHeight = 0.6f; private float crouchTimer = 1f; public float gravity = -9.8f; private float standingHeight = 2; private float crouchingHeight = 1; private float targetHeight = 2; private bool isGrounded; private bool crouching= false; private bool sprinting = false; private bool lerpCrouch=false; Vector3 initialCameraPosition; void Start() { controller = GetComponent<CharacterController>(); look = GetComponent<PlayerLook>(); initialCameraPosition = look.cam.transform.localPosition; } // Update is called once per frame void Update() { isGrounded = controller.isGrounded; if(lerpCrouch) { crouchTimer += Time.deltaTime; float p = crouchTimer / 1; p *= p; if(controller.height<standingHeight && targetHeight == standingHeight) { var castOrigin = transform.position + new Vector3(0, controller.height / 2, 0); if (Physics.Raycast(castOrigin, Vector3.up, out RaycastHit hit, 0.2f)) { var distanceToCeiling = hit.point.y - castOrigin.y; targetHeight = Mathf.Max (controller.height + distanceToCeiling -0.1f,crouchingHeight); } } controller.height = Mathf.Lerp(controller.height, targetHeight, p); if(p>1) { lerpCrouch = false; crouchTimer = 0f; } } } public void ProcessMove(Vector2 input) { Vector3 moveDirection = Vector3.zero; moveDirection.x = input.x; moveDirection.z = input.y; controller.Move(transform.TransformDirection(moveDirection)*speed*Time.deltaTime); playerVelocity.y += gravity * Time.deltaTime; if(isGrounded &&playerVelocity.y <0) { playerVelocity.y = -2f; } if (!Mathf.Approximately(targetHeight, controller.height)) { var halfHeightDifference = new Vector3(0, (standingHeight - controller.height) / 2, 0); var newCameraPosition = initialCameraPosition - halfHeightDifference; look.cam.transform.localPosition = newCameraPosition; } controller.Move(playerVelocity*Time.deltaTime); } public void Jump() { if(isGrounded) { playerVelocity.y = Mathf.Sqrt(jumpHeight*-3.0f * gravity); } } public void Crouch() { crouching = !crouching; sprinting = false; if (crouching) { targetHeight = crouchingHeight; speed = crouchSpeed; } else { targetHeight = standingHeight; speed = walkSpeed; } crouchTimer = 0; lerpCrouch = true; } public void Sprint() { if(!crouching) { sprinting = !sprinting; if (sprinting) { speed = sprintSpeed; } else { speed = walkSpeed; } } } }
HeadBobController脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; public class HeadBobController : MonoBehaviour { [SerializeField] private bool _enable = true; [SerializeField, Range(0, 0.1f)] private float _amplitude = 0.015f; [SerializeField, Range(0, 30)] private float _frequency = 10.0f; [SerializeField] private Transform _camera = null; [SerializeField] private Transform _cameraHolder = null; private float _toggleSpeed = 3.0f; private Vector3 _startPos; private CharacterController _controller; private void Awake() { _controller = GetComponent<CharacterController>(); _startPos = _cameraHolder.localPosition; } private void Update() { if (!_enable) return; CheckMotion(); ResetPosition(); _camera.LookAt(FocusTarget()); } private void CheckMotion() { float speed = new Vector3(_controller.velocity.x, 0, _controller.velocity.z).magnitude; Debug.Log(_controller.velocity.magnitude); if (speed < _toggleSpeed) return; if (!_controller.isGrounded) return; PlayMotion(FootStepMotion()); } private void PlayMotion(Vector3 motion) { _camera.localPosition += motion; } private Vector3 FootStepMotion() { Vector3 pos = Vector3.zero; pos.y += Mathf.Sin(Time.time * _frequency) * _amplitude; pos.x += Mathf.Cos(Time.time * _frequency / 2) * _amplitude * 2; return pos; } private void ResetPosition() { if (_camera.localPosition == _startPos) return; _camera.localPosition = Vector3.Lerp(_camera.localPosition, _startPos, 1 * Time.deltaTime); } private Vector3 FocusTarget() { Vector3 pos = new Vector3(transform.position.x, transform.position.y + _cameraHolder.localPosition.y, transform.position.z); pos += _cameraHolder.forward * 15.0f; return pos; } }
解决方案
1. 调整脚本执行顺序
Unity默认按脚本名称排序执行,若HeadBobController在PlayerMotor之前执行,此时controller.Move()还未调用,velocity尚未更新:
- 打开Edit > Project Settings > Script Execution Order
- 将
HeadBobController拖到PlayerMotor下方,确保PlayerMotor先执行,更新velocity后HeadBobController再读取
2. 直接从PlayerMotor获取移动速度
若调整执行顺序无效,可在PlayerMotor中暴露当前移动速度,让HeadBobController直接读取:
修改PlayerMotor,添加公开属性:
public float CurrentMoveSpeed { get; private set; } public void ProcessMove(Vector2 input) { Vector3 moveDirection = Vector3.zero; moveDirection.x = input.x; moveDirection.z = input.y; Vector3 transformedDir = transform.TransformDirection(moveDirection); controller.Move(transformedDir * speed * Time.deltaTime); // 计算地面移动速度 CurrentMoveSpeed = new Vector3(transformedDir.x, 0, transformedDir.z).magnitude * speed; // 原有代码保留... }
修改HeadBobController:
private PlayerMotor _playerMotor; private void Awake() { _controller = GetComponent<CharacterController>(); _playerMotor = GetComponent<PlayerMotor>(); _startPos = _cameraHolder.localPosition; } private void CheckMotion() { float speed = _playerMotor.CurrentMoveSpeed; Debug.Log(speed); if (speed < _toggleSpeed) return; if (!_controller.isGrounded) return; PlayMotion(FootStepMotion()); }
3. 验证CharacterController引用
确认HeadBobController中的_controller正确获取了玩家对象上的组件,可在Awake中添加校验:
private void Awake() { _controller = GetComponent<CharacterController>(); if(_controller == null) { Debug.LogError("HeadBobController未找到CharacterController组件!"); } _startPos = _cameraHolder.localPosition; }
内容的提问来源于stack exchange,提问作者Spooky_REX
相关产品推荐
相关产品推荐

