Виртуальный метод вызывается вместо переопределения

2

У меня есть четыре класса: 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. Это имя, прежде чем я запустил игру:

Изображение 174551

После того, как я запустил игру, кнопка теперь выглядит так:

Изображение 174551

При запуске:

evt.actions.Add((Action)Activator.CreateInstance(actionType));

Редактор отображает несоответствие типа, даже если вывод actionType и Activator.CreateInstance(actionType) - MoveTo:

Изображение 174551

  • 0
    Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
Теги:
unity3d

1 ответ

1
Лучший ответ

Unity не поддерживает встроенную полиморфную сериализацию.

Когда вы сохраняете 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, что создает свои проблемы.

  • 1
    Исходя из первого абзаца цитаты, это именно то, что я вижу. Похоже, мне придется найти другой способ сделать то, что я хочу.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню