Во время реализации моей игры с использованием Unity я столкнулся со следующей настройкой:
- У меня есть
ScriptableObject
(в качестве актива) с делегатом ac # event
. - У меня есть
MonoBehaviour
, в котором есть сериализованная ссылка на ScriptableObject
, в которой есть делегат.
Я хочу "подписать" MonoBehaviour
на этот ScriptableObject
's событие, правильно обрабатывая событие, чтобы избежать утечек памяти. Первоначально я предполагал, что подписки на событие с помощью обратного вызова OnEnable
и отказа от подписки на OnDisable
было достаточно. Однако происходит утечка памяти, когда разработчик с помощью Unity Inspector меняет значение сериализованной ссылки на ScriptableObject
во время воспроизведения.
Существует ли канонический способ безопасной подписки и отпискик событиям c # в сериализованной ссылке на ScriptableObject
, учитывая, что я хочу, чтобы разработчики игры могли менять значение в инспекторе во время игры?
Чтобы проиллюстрировать это,Я написал простой код для этого сценария:
SubjectSO.cs (ScriptableObject
с событием)
using UnityEngine;
using System;
[CreateAssetMenu]
public class SubjectSO : ScriptableObject
{
public event Action<string> OnTrigger;
public void Invoke()
{
this.OnTrigger?.Invoke(this.name);
}
}
ObserverMB .cs (MonoBehaviour
, который хочет подписаться на событие в ScriptableObject
)
using UnityEngine;
public class ObserverMB : MonoBehaviour
{
public SubjectSO subjectSO;
public void OnEnable()
{
if(this.subjectSO != null)
{
this.subjectSO.OnTrigger += this.OnTriggerCallback;
}
}
public void OnDisable()
{
if(this.subjectSO != null)
{
this.subjectSO.OnTrigger -= this.OnTriggerCallback;
}
}
public void OnTriggerCallback(string value)
{
Debug.Log("Callback Received! Value = " + value);
}
}
InvokesSubjectSOEveryUpdate .cs (вспомогательный MonoBehaviour
, для тестирования)
using UnityEngine;
public class InvokesSubjectSOEveryUpdate : MonoBehaviour
{
public SubjectSO subjectSO;
public void Update()
{
this.subjectSO?.Invoke();
}
}
Для тестирования я создал два ресурса типа SubjectSO
с именем:
Затем я создал GameObject
в сцене и прикрепил следующие компоненты:
ObserverMB
, ссылка Тема A InvokesSubjectSOEveryUpdate
, ссылка Тема A InvokesSubjectSOEveryUpdate
, ссылка SubjectB
При нажатии на воспроизведение сообщение Callback Received! Value = SubjectA
печатается в консоли при каждом обновлении, что ожидается.
Затем, когда я использую инспектор дляизмените ссылку в ObserverMB
с SubjectA на SubjectB , пока игра еще продолжается, сообщение Callback Received! Value = SubjectA
по-прежнему печатается.
Если я отключуи включите ObserverMB
в инспекторе, оба сообщения Callback Received! Value = SubjectA
и Callback Received! Value = SubjectB
начинают печататься при каждом обновлении.
Начальная подписка обратного вызова все еще действует, но, как подписчик, ObserverMB
потерялссылка на это событие.
Как мне избежать этой ситуации?
Я действительно считаю, что это, кажется, сценарий общего использования для использования c # event
делегаты и ScriptableObjects
, и мне кажется странным, что OnEnable
и OnDisable
неправильно обрабатываютСлучай сериализации разработчика, настраивающего инспектора.