У меня есть четыре класса: Event
и Action
которые являются базовыми классами, а затем у меня есть два дочерних класса Create: Event
и MoveTo: Action
.
Event
содержит список экземпляров Action
, а при вызове функции Trigger()
в дочернем Event.Trigger()
Create
он вызывает Event.Trigger()
, который перебирает список действий и вызывает Action.Run()
для каждого действия, которое вызывает Called()
,
Проблема, с которой я сталкиваюсь, - это virtual
метод, вызываемый, а не метод override
внутри MoveTo
.
[Serializable]
public abstract class Event : MonoBehaviour {
[SerializeField] public List<Action> actions = new List<Action>();
protected void Trigger() {
foreach (Action action in actions) {
action.Run();
}
}
}
Событие
public class Create : Event {
void Start() {
Trigger();
}
}
действие
[Serializable]
public class Action {
public virtual void Called() {
Debug.Log("Virtual");
}
public void Run() {
Called();
}
}
MoveTo
public class MoveTo : Action {
public override void Called() {
Debug.Log("Called");
}
}
Я добавляю действие MoveTo
в список событий из Unity Editor на сборку. Я не уверен, как единство справляется с ними во время выполнения, делает ли это их инициализацию или делает? Что я не уверен. Вот что может вызвать мою проблему...
private Event GetCurrentEvent(){}
void AddActionCallback(Type actionType) {
// actionType is MoveTo
var prefab = GetCurrentPrefabItem().Value;
var evt = GetCurrentEvent();
evt.actions.Add((Action)Activator.CreateInstance(actionType));
Undo.RecordObject(prefab.gameObject, "Added actions");
PrefabUtility.RecordPrefabInstancePropertyModifications(prefab.gameObject);
}
Вот как это выглядит, прежде чем я запустил игру. Он показывает MoveTo
, кнопка в красном столбце показывает действие с помощью action.GetType().Name
. Это имя, прежде чем я запустил игру:
После того, как я запустил игру, кнопка теперь выглядит так:
При запуске:
evt.actions.Add((Action)Activator.CreateInstance(actionType));
Редактор отображает несоответствие типа, даже если вывод actionType
и Activator.CreateInstance(actionType)
- MoveTo
:
Когда вы сохраняете prefab, он сериализует List как список чистых Action
s и стирает любую информацию, которую имеет только дочерний класс MoveTo
.
Из документов Unity по сериализации:
Нет поддержки полиморфизма
Если у вас есть
public Animal[] animals
и вы помещаете экземплярDog
,Cat
иGiraffe
, после сериализации вы имеете три экземпляраAnimal
.Один из способов борьбы с этим ограничением состоит в том, чтобы понять, что он применяется только к пользовательским классам, которые становятся серийными. Ссылки на другие
UnityEngine.Objects
сериализуются как фактические ссылки, а для них полиморфизм действительно работает. Вы бы сделатьScriptableObject
производного класса или другогоMonoBehaviour
производного класса, и ссылаться на это. Недостатком этого является то, что вам нужно сохранить этотMonobehaviour
илиscriptable
объект где-то и что вы не можете его сериализовать эффективно.Причина этих ограничений заключается в том, что одним из основных оснований системы сериализации является то, что компоновка потока данных для объекта известна заранее; это зависит от типов полей класса, а не от того, что будет храниться внутри полей.
Вот почему его класс проявляется как Action
.
Однако он не может сериализоваться как Action
потому что:
Как обеспечить, чтобы пользовательский класс мог быть сериализован
Обеспечьте это:
Имеет атрибут Serializable
Не абстрактно
Не является статическим
Не является общим, хотя он может наследоваться от общего класса
Action
- это абстрактный класс, поэтому он даже не будет сериализоваться частично правильно. Я предполагаю, что это основная причина проблемы Type Mismatch
, поскольку Unity пытается десериализовать что-то неподдерживаемое.
Короче говоря, если вы хотите сериализовать данные в MoveTo
, вам нужно будет иметь [SerializeField]
List<MoveTo>
, чтобы не потерять информацию, или вы можете наследовать действие ActionScript из ScriptableObject
, что создает свои проблемы.