Как убедиться, что объект MonoBehaviour загрузил свои данные до того, как другой Актер захочет получить к ним доступ?

У меня есть Inventory: MonoBehaviour, который загружает некоторые фиктивные данные в Start(), включая переменную «selectedWeapon», которая установлена ​​и может быть прочитана.

Допустим, я хочу получить доступ к этой переменной, когда настраиваю другой MonoBehaviour в той же сцене.

Есть ли хороший способ убедиться, что переменная установлена ​​при попытке доступа к ней? Я стремлюсь сделать это в Start() или какой-либо начальной функции, желательно только один раз.

Мое временное решение состоит в том, чтобы использовать функцию 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();
    }
    .
    .
    .    
}

Временное решение кажется неустойчивым, поскольку проверки в обновлении, безусловно, будут расти в моем проекте.


person OG NEO    schedule 01.07.2019    source источник
comment
Рассмотрите возможность явно указать порядок выполнения скрипта в настройках проекта.   -  person Iggy    schedule 01.07.2019
comment
Проверьте и это: stackoverflow.com/questions/56810987/   -  person dreamend    schedule 01.07.2019
comment
Спасибо, ребята, я работал с Unity в течение некоторого времени, но никогда не знал, что вы можете устанавливать заказы на выполнение. @Игги   -  person OG NEO    schedule 01.07.2019
comment
Я думаю, что лучше по возможности избегать использования порядка выполнения скриптов. Это становится утомительным для управления, и это также имеет тенденцию поощрять менее поддерживаемый код, например. усиление временной связи.   -  person sonny    schedule 01.07.2019


Ответы (1)


Мое общее короткое правило всегда

  • Используйте Awake для всего, где вы не зависите от других, поэтому настраивайте свои собственные значения, для настройки всех ссылок между компонентами (но пока не используя их значения), вещи, где вы можете использовать static классы.

    Также используйте это, если возможно, для долгой загрузки/ввода-выводов, чтобы это выполнялось во время загрузки приложения и не отображалось как задержка для пользователя.

  • Используйте Start для всего, где вам нужно, чтобы другие компоненты были уже настроены, например. используя значения ссылок, которые были установлены в Awake.

см. также руководство по пробуждению и запуску


Конечно, у этого есть свои ограничения, поэтому при необходимости вы можете начать манипулировать порядком выполнения скрипта. но я избегаю этого, насколько это возможно.


Если это становится действительно сложным, иногда на самом деле нет способа использовать систему событий, например, например.

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
    }
}

Это выглядит более утомительным и сложным, но намного эффективнее, чем ожидание какого-либо события only-do-it-once в методе Update.

person derHugo    schedule 01.07.2019
comment
Отличное короткое правило, я был немного небрежен во время быстрого прототипирования, за что сейчас расплачиваюсь. Я начну принимать это с этого момента! Я подозревал, что иногда вы не проходите мероприятия для таких задач, очень ценю ваш комментарий по этому поводу. - person OG NEO; 01.07.2019