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

Unity AR:运行时实例化预制体后如何正确获取Script Component?

运行时实例化组件的NullReferenceException问题解决

问题场景

我需要引用仅在运行时而非提前实例化的Component:

  • 场景中有个名为GroundPlacement的游戏对象,挂载了ARTapToPlaceObjects脚本,负责在运行时把地面预制体(ground prefab)实例化到场景里;
  • 这个地面预制体带有子物体,子物体上挂载了Spawner脚本;
  • ARTapToPlaceObjects里的objectToPlace变量存的是地面预制体,placedObject是实例化后的地面对象。

现在多个脚本要调用Spawner的功能,但都抛出了NullReferenceException:

  1. ARTapToPlaceObjects的Awake方法里调用FindObjectOfType<Spawner>(),因为预制体还没实例化,得到空引用,导致ResetGround调用spawnerRef的方法时出错;
  2. GamePiece脚本的Awake方法里,尝试通过groundPlacementRef.PlacedObject.GetComponentInChildren<Spawner>()获取引用,同样因为地面还没实例化,PlacedObject为空,导致OnMouseDown无法执行。

问题原因

  1. Unity的Awake方法是场景加载完成后立刻执行的,这时候地面预制体还没被实例化,Spawner组件根本不在场景里,FindObjectOfType自然找不到东西;
  2. GamePiece的Awake执行时机早于地面实例化的时机,groundPlacementRef.PlacedObject还没被赋值,调用GetComponentInChildren肯定会空引用。

解决方法

1. 修复ARTapToPlaceObjects脚本

别在Awake里提前拿Spawner引用,等地面实例化完成后再获取:

// 实例化地面的方法示例
public void PlaceObject()
{
    placedObject = Instantiate(objectToPlace, transform.position, transform.rotation);
    // 实例化完成后立即获取Spawner
    spawnerRef = placedObject.GetComponentInChildren<Spawner>();
}

// 调用Spawner方法前先判空
public void ResetGround()
{
    spawnerRef?.YourSpawnerMethod(); // 替换成你要调用的实际方法
}

如果调用Spawner的频率不高,也可以每次调用时实时获取,省得存引用:

public void ResetGround()
{
    if (placedObject == null) return;
    var spawner = placedObject.GetComponentInChildren<Spawner>();
    spawner?.YourSpawnerMethod();
}

2. 修复GamePiece脚本

不要在Awake里拿引用,改成在需要用的时候再获取,或者用事件监听地面完成放置的时机:

方法一:在OnMouseDown里实时获取

void OnMouseDown()
{
    if (groundPlacementRef.PlacedObject == null) return;
    var spawner = groundPlacementRef.PlacedObject.GetComponentInChildren<Spawner>();
    spawner?.DoYourThing(); // 替换成实际要调用的方法
}

方法二:用事件通知(更适合频繁调用的场景)

先在ARTapToPlaceObjects里添加事件:

using System;
using UnityEngine;

public class ARTapToPlaceObjects : MonoBehaviour
{
    // 定义事件,地面放置完成时触发
    public event Action<Spawner> OnGroundPlaced;
    public GameObject objectToPlace;
    public GameObject placedObject;

    public void PlaceObject()
    {
        placedObject = Instantiate(objectToPlace, transform.position, transform.rotation);
        var spawner = placedObject.GetComponentInChildren<Spawner>();
        // 触发事件,把Spawner引用传出去
        OnGroundPlaced?.Invoke(spawner);
    }
}

然后在GamePiece里订阅事件:

public class GamePiece : MonoBehaviour
{
    public ARTapToPlaceObjects groundPlacementRef;
    private Spawner spawnerRef;

    void Start()
    {
        // 订阅事件
        groundPlacementRef.OnGroundPlaced += OnGroundIsPlaced;
    }

    void OnGroundIsPlaced(Spawner spawner)
    {
        // 拿到Spawner引用
        spawnerRef = spawner;
    }

    void OnMouseDown()
    {
        spawnerRef?.DoYourThing();
    }

    void OnDestroy()
    {
        // 销毁前取消订阅,避免内存泄漏
        groundPlacementRef.OnGroundPlaced -= OnGroundIsPlaced;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 01:13:16