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

标签 c# unity3d synchronization data-access

我有一个 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();
    }
    .
    .
    .    
}

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

最佳答案

我的一般捷径总是

  • 使用 Awake对于您不依赖他人的所有内容,因此设置您自己的值,设置组件之间的所有引用(但尚未使用它们的值),您可以使用 static 类的东西。

    对于长时间加载/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 方法中等待一些只做一次事件要高效得多。

关于c# - 如何确保 MonoBehaviour 对象在另一个 Actor 想要访问它之前加载了它的数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56832509/

相关文章:

user-interface - 在 Unity 中创建一个螺旋形 UI 栏

java - 关于线程同步的几个问题

java - 同步块(synchronized block)如何工作?

c# - DataGridView 自动高度 - 如何自动调整 DataGridView 高度?

c# - 创建一个查询并将结果放入数据网格

c# - 夹紧旋转/四元数

c# - 在运行时将 .OBJ 加载到 Unity

linux - 添加 DEFINE_WAIT 无法在 linux 模块中编译

c# - ComboBox - 自动完成 + 自由打字

c# - 了解 LINQ 如何编译为 ADO.NET SQL 语句