You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,提问作者Артем Шепітько

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 20:09:22