Unity角色移动异常及空引用错误求助(附相关代码)
问题:Unity角色移动异常与空引用错误排查
一、问题现象
- 按下W键时,角色不向前移动反而向上“跳走”;按下A/D键时,角色左右移动伴随向上偏移
- 触发两条空引用错误:
NullReferenceException: Object reference not set to an instance of an object
Gamekit3D.PlayerController.Awake () (at Assets/3DGamekitLite/Scripts/Game/Player/PlayerController.cs:149)
NullReferenceException: Object reference not set to an instance of an object
Gamekit3D.PlayerController.OnDisable () (at Assets/3DGamekitLite/Scripts/Game/Player/PlayerController.cs:172)
二、相关代码
PlayerCharacter 脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerCharacter : MonoBehaviour { public Animator animator; public Rigidbody playerPhysics; public float jumpPower = 12f; public float groundCheckDistance = 0.3f; private bool isGrounded; public float groundCheckDistanceSaved; private float turnAmount; public float forwardAmount; private void Start() { groundCheckDistanceSaved = groundCheckDistance; } public void Move(Vector3 move, bool jump) { move = transform.InverseTransformDirection(move); move = Vector3.ProjectOnPlane(move, Vector3.up); turnAmount = Mathf.Atan2(move.x, move.z); forwardAmount = move.z; isGrounded = Physics.Raycast(transform.position + (Vector3.up * 0.1f), Vector3.down, out _, groundCheckDistance); if (isGrounded) { HandleGroundedMovement(jump); } else { HandleAirborneMovement(); } ApplyExtraTurnRotation(); UpdateAnimator(); } void UpdateAnimator() { animator.SetFloat("Forward", forwardAmount, 0.1f, Time.deltaTime); animator.SetFloat("Turn", turnAmount, 0.1f, Time.deltaTime); animator.SetBool("OnGround", isGrounded); animator.SetFloat("Jump", playerPhysics.velocity.y); } void HandleAirborneMovement() { playerPhysics.AddForce(Physics.gravity); groundCheckDistance = playerPhysics.velocity.y < 0 ? groundCheckDistanceSaved : 0.01f; } void HandleGroundedMovement(bool jump) { if (!jump || !isGrounded) return; playerPhysics.velocity = new Vector3(playerPhysics.velocity.x, jumpPower, playerPhysics.velocity.z); isGrounded = false; groundCheckDistance = 0.1f; } void ApplyExtraTurnRotation() { var turnSpeed = Mathf.Lerp(180, 360, forwardAmount); transform.Rotate(0, turnAmount * turnSpeed * Time.deltaTime, 0); } public void OnAnimatorMove() { if (!isGrounded || Time.deltaTime < 0.001f) return; var velocity = (animator.deltaPosition) / Time.deltaTime; velocity.y = playerPhysics.velocity.y; playerPhysics.velocity = velocity; } }
PlayerController 脚本(部分代码)
// Called automatically by Unity when the script first exists in the scene. void Awake() { m_Input = GetComponent<PlayerInput>(); m_Animator = GetComponent<Animator>(); m_CharCtrl = GetComponent<CharacterController>(); meleeWeapon.SetOwner(gameObject); s_Instance = this; } // Called automatically by Unity after Awake whenever the script is enabled. void OnEnable() { SceneLinkedSMB<PlayerController>.Initialise(m_Animator, this); m_Damageable = GetComponent<Damageable>(); m_Damageable.onDamageMessageReceivers.Add(this); m_Damageable.isInvulnerable = true; EquipMeleeWeapon(false); m_Renderers = GetComponentsInChildren<Renderer>(); } // Called automatically by Unity whenever the script is disabled. void OnDisable() { m_Damageable.onDamageMessageReceivers.Remove(this); for (int i = 0; i < m_Renderers.Length; ++i) { m_Renderers[i].enabled = true; } } // Called automatically by Unity once every Physics step. void FixedUpdate() { CacheAnimatorState(); UpdateInputBlocking(); EquipMeleeWeapon(IsWeaponEquiped()); m_Animator.SetFloat(m_HashStateTime, Mathf.Repeat(m_Animator.GetCurrentAnimatorStateInfo(0).normalizedTime, 1f)); m_Animator.ResetTrigger(m_HashMeleeAttack); if (m_Input.Attack && canAttack) m_Animator.SetTrigger(m_HashMeleeAttack); CalculateForwardMovement(); CalculateVerticalMovement(); SetTargetRotation(); if (IsOrientationUpdated() && IsMoveInput) UpdateOrientation(); PlayAudio(); TimeoutToIdle(); m_PreviouslyGrounded = m_IsGrounded; }
三、问题修复方案
1. 空引用错误修复
(1)Awake方法报错(第149行)
错误源于meleeWeapon.SetOwner(gameObject);中meleeWeapon未赋值:
- 若
meleeWeapon是挂载在子物体上的组件,在Awake中添加初始化代码:meleeWeapon = GetComponentInChildren<MeleeWeapon>(); // 替换为实际组件类型 - 或在Inspector面板中直接将对应武器物体拖入
meleeWeapon字段。
(2)OnDisable方法报错(第172行)
错误源于m_Damageable为空,需添加空值判断:
void OnDisable() { if (m_Damageable != null) m_Damageable.onDamageMessageReceivers.Remove(this); if (m_Renderers != null) { for (int i = 0; i < m_Renderers.Length; ++i) { m_Renderers[i].enabled = true; } } }
同时检查角色物体上是否挂载了Damageable组件,若没有则添加。
2. 移动异常修复
核心问题是同时使用了Rigidbody和CharacterController控制角色移动,两者逻辑冲突导致Y轴偏移,需统一控制方式:
方案一:统一使用Rigidbody控制
- 移除角色物体上的
CharacterController组件 - 确保
PlayerCharacter中的playerPhysics字段已在Inspector中拖入Rigidbody组件 - 优化
OnAnimatorMove方法,避免动画驱动覆盖Y轴速度:public void OnAnimatorMove() { if (!isGrounded || Time.deltaTime < 0.001f) return; // 仅同步XZ平面的动画移动,保留Y轴的重力/跳跃速度 Vector3 animVelocity = animator.deltaPosition / Time.deltaTime; animVelocity.y = playerPhysics.velocity.y; playerPhysics.velocity = animVelocity; } - 优化地面检测,添加LayerMask避免误检测:
public LayerMask groundLayer; // 在Inspector中选择地面层 // ... isGrounded = Physics.Raycast(transform.position + (Vector3.up * 0.1f), Vector3.down, out _, groundCheckDistance, groundLayer);
方案二:统一使用CharacterController控制
- 移除角色物体上的
Rigidbody组件 - 修改
PlayerCharacter的移动逻辑,改用CharacterController.Move方法替代Rigidbody控制,同时调整PlayerController中的移动计算逻辑,确保两者统一。
内容的提问来源于stack exchange,提问作者Артем Шепітько
相关产品推荐
相关产品推荐

