Обнаружить изменения в стоимости других объектов, на которые есть ссылки в единстве? - PullRequest
0 голосов
/ 28 декабря 2018

Я пытаюсь реализовать функцию, которая обнаруживает изменение значения ссылки на объект другого поля.

Например, существует объект A, а объект B содержит A. Объект A являетсяуведомление от OnValidate, когда поле объекта A изменилось.В это время.Есть ли способ, которым «B» может обнаружить изменение «A»?

Эта функция требуется только в редакторе и не включает код во время выполнения.

public class A : ScriptableObject
{
    [SerializeField]
    public string team;

    private void OnValidate()
    {
        Debug.Log($"My team name has been changed to {team}.");
    }
}

public class B : ScriptableObject
{
    [SerializeField]
    public A a;

    private void OnValidate()
    {
        Debug.Log($"A changed.");
    }

    // How can I detect changes to A and call this functions?
    private void OnATeamChanged()
    {
        Debug.Log($"A's team name has been changed to {a.team}.");
    }

    private void OnADestroyed()
    {
        Debug.Log($"A is missing.");
    }
}

Ответы [ 2 ]

0 голосов
/ 29 декабря 2018

Я бы также предложил использовать события, но будьте осторожны, как и когда вы регистрируете и удаляете слушателей!

public class A : ScriptableObject
{
    // You don't need [SerializeField] 
    // public properties are serialized anyway
    public string team;

    public event Action onChanged;
    public event Action onReset;
    public event Action onDestroyed;

    private void OnValidate()
    {
        Debug.Log($"My team name has been changed to {team}.");

        if(onChanged == null) return;
        onChanged.Invoke();
    }

    // This is called on reset
    // But if you override this you have to set
    // The default values yourself!
    private void Reset()
    {
        team = "";

        if (onReset == null) return;
        onReset.Invoke();
    }

    // Called when object is destroyed
    private void OnDestroyed()
    {
        if(onDestroyed == null) return;
        onDestroyed.Invoke();
    }
}

Но теперь в B я бы не добавил слушателя на Awake, так как это добавляетэто после каждой перекомпиляции и несколько раз!Вместо этого не забудьте «очистить» всех слушателей, которых вы когда-либо добавляли, чтобы избежать получения NullReferenceExceptions:

public class B : ScriptableObject
{
    // You don't need [SerializeField] since public properties
    // are serialized anyway
    public A a;

    // Internal for removing listeners later
    private A lastA;

    private void OnValidate()
    {
        // Apparently something else was changed
        if(a == lastA) return;

        Debug.Log("reference a changed");

        // Unregister listeners for old A reference
        if(lastA)
        {
            lastA.onChanged -= OnAChanged;
            lastA.onReset -= OnAReset;
            lastA.onDestroyed -= OnADestroyed;
        }

        // Register listeners for new A reference
        // Note that it is allways possible to remove listeners first
        // even if they are not there yet
        // that makes sure you listen only once and don't add multiple calls
        if(a)
        {
            a.onChanged -= OnAChanged;
            a.onReset -= OnAReset;
            a.onDestroyed -= OnADestroyed;

            a.onChanged += OnAChanged;
            a.onReset += OnAReset;
            a.onDestroyed += OnADestroyed;
        }

        lastA = a;
    }

    // Make allways sure you also remove all listeners on destroy to not get Null reference exceptions
    // Note that it is allways possible to remove listeners even if they are not there yet
    private void OnDestroy()
    {
        if(!a) return;

        a.onChanged -= OnAChanged;
        a.onReset -= OnAReset;
        a.onDestroyed -= OnADestroyed;
    }

    private void OnAChanged()
    {
        Debug.Log("A was changed");
    }

    private void OnAReset()
    {
        Debug.Log("A was reset");
    }

    private void OnADestroyed()
    {
        Debug.Log("a was destroyed");
    }
}

Небольшое необязательное изменение, если вы хотите

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

public event Action xy;

на

public UnityEvent xy;

и удалить проверки if(xy == null).

Вы бы также заменили

a.onChanged -= OnAChanged;
a.onChanged += OnAChanged;

на

a.onChanged.RemoveListener(OnAChanged);
a.onChanged.AddListener(OnAChanged);
0 голосов
/ 29 декабря 2018

Вы можете попытаться добавить событие

public class A : ScriptableObject
{
    [SerializeField]
    public string team;
    public Action RaiseChangeName;
    private void OnValidate()
    {
        Debug.Log($"My team name has been changed to {team}.");
        if(RaiseChangeName != null) { RaiseChangeName(); }
    }
}

public class B : ScriptableObject
{
    [SerializeField]
    public A a;
    void Awake()
    {
         a.RaiseChangeName += OnATeamChanged;
    }

    // How can I detect changes to A and call this functions?
    private void OnATeamChanged()
    {
        Debug.Log($"A's team name has been changed to {a.team}.");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...