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

基于Unity WalkerAgent训练ragdoll时碰撞触发Agent为空问题

Unity ML-Agents 布偶WalkerAgent碰撞时Agent为空的NullReferenceException修复

我在基于Unity的WalkerAgent训练布偶行走时做了自定义修改,现在布偶任意身体部位碰撞地面时会触发NullReferenceException,报错指向ObjectContact.OnCollisionStay方法中Agent对象为空,无法应用奖励。已确认脚本执行顺序无误,Agent应该在调用前完成初始化。

报错信息

nullreferenceexception: Object reference not set to an instance of an objectUnity.MLAgentsExamples.ObjectContact.OnCollisionStay(UnityEngine.Collision col) (at Assets/Scripts/ObjectContact.cs:55)UnityEngine.Physics:OnSceneContact(PhysicsScene, IntPtr, Int32) (at /Users/bokken/build/output/unity/unity/Modules/Physics/ScriptBindings/PhysicsContact.bindings.cs:49)

代码问题分析

查看JointDriveController.SetupBodyPart方法的逻辑,存在先给未创建的ObjectContact赋值Agent,再判断是否需要创建ObjectContact的顺序错误:

  1. 先执行bp.objectContact = t.GetComponent<ObjectContact>();,如果物体上没有该组件,bp.objectContact会是null
  2. 紧接着执行bp.objectContact.agent = agent;——此时若bp.objectContact为null,已经会触发隐藏的空引用,后续创建组件后的赋值逻辑无法覆盖这个错误状态
  3. 重复赋值Agent的冗余逻辑也增加了出错概率

修复方案

1. 修正JointDriveController.SetupBodyPart逻辑顺序

确保先获取/创建ObjectContact组件,再赋值Agent:

public void SetupBodyPart(Transform t)
{
    var bp = new BodyPart
    {
        rb = t.GetComponent<Rigidbody>(),
        joint = t.GetComponent<CharacterJoint>(),
        startingPos = t.position,
        startingRot = t.rotation
    };
    bp.rb.maxAngularVelocity = k_MaxAngularVelocity;

    // 先确保ObjectContact组件存在
    bp.objectContact = t.GetComponent<ObjectContact>();
    if (!bp.objectContact)
    {
        bp.objectContact = t.gameObject.AddComponent<ObjectContact>();
    }

    // 获取或创建Agent(注意:gameObject为JointDriveController所在物体,需是WalkerAgent的载体)
    var agent = gameObject.GetComponent<Agent>();
    if (agent == null)
    {
        agent = gameObject.AddComponent<Agent>();
        Debug.LogWarning($"自动给{gameObject.name}添加了Agent组件");
    }

    // 赋值Agent
    bp.objectContact.agent = agent;

    if (bp.objectContact.agent == null)
        Debug.LogError($"Agent not found for {t.name}");
   
    bp.thisJdController = this;
    bodyPartsDict.Add(t, bp);
    bodyPartsList.Add(bp);
}

2. 调整ObjectContact.OnCollisionStay的空判断顺序

先检查Agent是否为空,再执行奖励操作,避免空引用:

void OnCollisionStay(Collision col)
{
    // 先检查Agent状态,提前退出
    if (agent == null)
    {
        Debug.LogError($"Agent is null during OnCollisionStay on {gameObject.name}");
        return;
    }

    Debug.Log($"OnCollisionStay called for {gameObject.name} with {col.gameObject.name}");

    if (col.transform.CompareTag(k_Ground))
        agent.AddReward(groundContactPenalty);

    if (col.transform.CompareTag(k_Wall))
        agent.AddReward(wallContactPenalty);
}

3. 增加兜底逻辑(可选)

在ObjectContact的Awake方法中添加兜底的Agent查找逻辑,防止Setup阶段赋值失败:

void Awake()
{
    if (agent == null)
    {
        agent = GetComponentInParent<Agent>();
        if (agent == null)
        {
            Debug.LogError($"ObjectContact on {gameObject.name} 无法找到Agent组件");
        }
    }
}

额外验证点

  • 确认WalkerAgent与JointDriveController挂载在同一物体上,确保gameObject.GetComponent<Agent>()能正确获取到WalkerAgent(WalkerAgent继承自Agent)
  • 检查布偶所有身体部位的Rigidbody碰撞检测是否启用,确保碰撞事件能正常触发

内容的提问来源于stack exchange,提问作者Matt Clarke

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 19:05:16