如何确保 MonoBehaviour 对象在另一个 Actor 想要访问它之前加载了它的数据?

How to ensure that MonoBehaviour object has loaded its data before another Actor wants to access it?

我有一个 Inventory : MonoBehaviour,它在 Start() 中加载了一些模拟数据,包括一个已设置且可以读取的 "selectedWeapon" 变量。

假设我想在同一场景中设置另一个 MonoBehaviour 时访问此变量。

有什么好的方法可以确保在尝试访问变量时已设置该变量?我的目标是在 Start() 或某些初始函数中最好只执行一次。

我的临时解决方案是在想要从 Inventory 访问 "selectedWeapon" 的实例上使用 Update() 函数,并重复尝试设置它自己的变量,只要它没有设置。

/// This is only an example, illustrating my problem.

public class Inventory : MonoBehaviour
{
    [SerializeField]
    private WeaponItem m_SelectedWeapon = null;
    .
    .
    .
    void Start()
    {
        m_SelectedWeapon = MockDatabase.GetSelectedWeapon();
    }
    .
    .
    .
    public WeaponItem GetSelectedWeapon()
    {
        return m_SelectedWeapon;
    }
}

//--------------------------------------------------------------------

public class Actor : MonoBehaviour
{
    private WeaponItem m_SelectedWeapon = null;

    public Inventory inventory;
    .
    .
    .
    void Start()
    {
       // Would like to set up things here
       // but this can let m_SelectedWeapon be null
       // since it may be null in Inventory
       m_SelectedWeapon = inventory.GetSelectedWeapon();
    }

    void Update()
    {
        // If not yet set, load from inventory
        if(m_SelectedWeapon == null)
            m_SelectedWeapon = inventory.GetSelectedWeapon();
    }
    .
    .
    .    
}

临时解决方案感觉不可持续,因为更新中的检查肯定会在我的项目中增加。

我的总规则总是

  • 对不依赖他人的一切使用Awake,因此设置自己的值,设置组件之间的所有引用(但尚未使用它们的值),其中的东西你可以使用 static 类.

    如果可能的话,也可以使用这个 长时间使用 loading/IO 所以它是在应用程序加载期间完成的,并且不会对用户显示为延迟。

  • 使用 Start 用于您需要已经设置其他组件的所有内容,例如使用已在 Awake.

    中设置的引用值

另见Awake and Start Tutorial


这当然有其局限性,因此在需要时您可以开始操纵 Script Execution Order 但我会尽可能避免这样做。


如果它变得非常复杂,有时就没有办法使用事件系统,例如

public class A : MonoBehaviour
{
    public event Action OnReady;
    public bool isReady;

    private void Awake()
    {
        // do your stuff

        isReady = true;

        // execute whatever was added as callback
        OnReady?.Invoke();
    }
}

然后在需要的地方添加回调,例如

public class B : MonoBehaviour
{
    // either reference it in the Inspector
    public A a;

    private void Awake()
    {
        // or get it somehow on runtime
        a = FindObjectOfType<A>();

        // if ready directly move on otherwise add callbacks
        if(a.isReady)
        {
            OnAReady();
        }
        else
        {
            // it is always good to remove the callback even though
            // it wasn't added yet. Makes sure it is always only added once
            a.OnReady -= OnAReady;
            a.OnReady += OnAReady;
        }
    }

    private void OnDestroy()
    {
        // always remove callbacks when no longer needed
        a.OnReady -= OnAReady;
    }

    private void OnAReady()
    {
        // always remove callbacks when no longer needed
        a.OnReady -= OnAReady;

        // use stuff from A
    }
}

这看起来更烦人和复杂,但比在 Update 方法中等待某些 only-do-it-once 事件更有效。