• The same field name is serialized multiple times in the class or its parent class. Как исправить ошибку?

    @Gwinterion Автор вопроса
    Я решил всё-таки немного изменить архитектуру, так как основная проблема была в ней, а всё остальное и, в том числе ошибка в юнити возникало уже как следствие. Может кому-то окажется полезным, не уверен насколько эта идея лучше, но для логики которую я задумал рабочим вариантом стало такое решение:

    Я избавился от наследования классов ScriptableObject, так что теперь иерархию наследования имеют только объекты Entity, Item и Document.

    public class EntityScriptableObject : ScriptableObject
    {
        [SerializeField] private EntityType _type;
        [SerializeField] private string _name;
        [SerializeField] private string _raycastFeedbackText;
        [SerializeField] private GameObject _prefab;
    
        public EntityType Type => _type;
        public string Name => _name;
        public string RaycastFeedbackText => _raycastFeedbackText;
        public GameObject Prefab => _prefab;
    }


    public class ItemScriptableObject : ScriptableObject
    {
        [SerializeField] private ItemType _type;
        [SerializeField] private string _description;
        [SerializeField] private string _raycastFeedbackText;
        [SerializeField] private Sprite _sprite;
    
        public ItemType Type => _type;
        public string Description => _description;
        public string RaycastFeedbackText => _raycastFeedbackText;
        public Sprite Sprite => _sprite;
    }


    public class DocumentScriptableObject : ScriptableObject
    {
        [SerializeField] private DocumentType _type;
        [SerializeField] private string _text;
        [SerializeField] private string _raycastFeedbackText;
    
        public DocumentType Type => _type;
        public string Text => _text;
        public string RaycastFeedbackText => _raycastFeedbackText;
    }


    public class Entity : MonoBehaviour
    {
        [SerializeField] private EntityScriptableObject _entityScriptableObject;
        public EntityScriptableObject EntityScriptableObject => _entityScriptableObject;
    
        public virtual void Interact(){}
        public void StopInteraction(){}
    }


    public class Item : Entity
    {
        [SerializeField] private ItemScriptableObject _itemScriptableObject;
        public ItemScriptableObject ItemScriptableObject => _itemScriptableObject;
    
        public Item (ItemScriptableObject scriptableObject)
        {
            _itemScriptableObject = scriptableObject;
        }
    
        public void Use(){}
        public override void Interact(){}
    }


    public class Document : Entity
    {
        [SerializeField] private DocumentScriptableObject _documentScriptableObject;
        public DocumentScriptableObject DocumentScriptableObject => _documentScriptableObject;
    
        public void Read(){}
        public override void Interact(){}
    }


    Метод Interact в наследниках переопределяю так как по задумке объект одного и того же типа (например ключ) может как быть предметом, и его можно будет подобрать и использовать, так может и просто лежать где-нибудь, но подобрать его уже нельзя, хотя всё ещё можно как-либо взаимодействовать, например осмотреть.
    Написано
  • The same field name is serialized multiple times in the class or its parent class. Как исправить ошибку?

    @Gwinterion Автор вопроса
    #, Вот код классов ScriptableObject.

    public class EntityScriptableObject : ScriptableObject
    {
        [SerializeField] private EntityType _type;
        [SerializeField] private string _displayName;
        [SerializeField] private string _description;
        [SerializeField] private string _raycastFeedbackText;
        [SerializeField] private GameObject _prefab;
    
        public EntityType Type => _type;
        public string DisplayName => _displayName;
        public string Description => _description;
        public string RaycastFeedbackText => _raycastFeedbackText;
        public GameObject Prefab => _prefab;
    }


    public class ItemScriptableObject : EntityScriptableObject
    {
        [SerializeField] private Sprite _sprite;
    
        public Sprite Sprite => _sprite;
    }


    public class DocumentScriptableObject : EntityScriptableObject
    {
        [SerializeField] private string _text;
    
        public string Text => _text;
    }


    По идее данная реализация не предполагает что допустим в объект типа Document может попасть ссылка на ItemScriptableObject. Так что я пытался явно указать ссылки каких типов может принимать тот или иной объект но столкнулся с проблемами описанными выше
    Написано
  • The same field name is serialized multiple times in the class or its parent class. Как исправить ошибку?

    @Gwinterion Автор вопроса
    #, постараюсь объяснить логику которую задумал :)

    Все объекты в сцене с которыми может взаимодействовать игрок - это некие сущности Entity. Есть также наследуемые объекты Item и Document.

    public class Entity : MonoBehaviour
    {
        [field: SerializeField]
        public virtual EntityScriptableObject ScriptableObject
        {
            get;
            private set;
        }
    
        public void Interact()
        {
            Transform interactionPoint;
    
            switch (ScriptableObject.Type)
            {
                case EntityType.Key:
                    Player.Inventory.Add(this as Item);
                    Destroy(gameObject);
    
                    break;
                case EntityType.VideoTape:
                    Player.Inventory.Add(this as Item);
                    Destroy(gameObject);
    
                    break;
                case EntityType.Note:
                    Player.Journal.Add(this as Document);
    
                    Destroy(gameObject);
    
                    break;
                case EntityType.Painting:
                    GameManager.ChangeTypeOfControll(TypesOfControl.InspectionControll);
                    ObjectInspectorController.Instance.enabled = true;
                    ObjectInspectorController.Instance.Initialize(this);
    
                    break;
                case EntityType.TV:
                    GameManager.PauseGame();
    
                    interactionPoint = InteractionPoints.Instance.TV.transform;
                    InteractionController.Instance.InteractionPoint = interactionPoint;
                    InteractionController.Instance.CurrentInteraction = InteractionType.TV;
                    InteractionController.Instance.MoveToInteractionPoint();
    
                    InteractionController.Instance.OnStopInteraction += StopInteraction;
    
                    UIController.Instance.OpenInventory();
                    PlayerController.Instance.enabled = false;
    
                    break;
                case EntityType.Door:
                    DoorController door = gameObject.GetComponent<DoorController>();
    
                    if (door.IsOpen)
                        door.CloseDoor();
                    else
                        door.OpenDoor();
    
                    break;
                case EntityType.SafeDigital:
                    GameManager.PauseGame();
                    GameManager.ChangeTypeOfControll(TypesOfControl.InteractionControl);
    
                    interactionPoint = InteractionPoints.Instance.SafeDigital.transform;
                    InteractionController.Instance.InteractionPoint = interactionPoint;
                    InteractionController.Instance.CurrentInteraction = InteractionType.SafeNumeric;
                    InteractionController.Instance.MoveToInteractionPoint();
    
                    InteractionController.Instance.OnStopInteraction += StopInteraction;
    
                    PlayerController.Instance.enabled = false;
                    CameraController.Instance.enabled = false;
    
                    Cursor.lockState = CursorLockMode.None;
                    Cursor.visible = true;
    
                    gameObject.GetComponentInChildren<SafeDoor>().enabled = true;
    
                    break;
                case EntityType.SafePadlock:
                    GameManager.PauseGame();
                    GameManager.ChangeTypeOfControll(TypesOfControl.InteractionControl);
    
                    interactionPoint = InteractionPoints.Instance.SafePadlock.transform;
                    InteractionController.Instance.InteractionPoint = interactionPoint;
                    InteractionController.Instance.CurrentInteraction = InteractionType.SafePadlock;
                    InteractionController.Instance.MoveToInteractionPoint();
    
                    InteractionController.Instance.OnStopInteraction += StopInteraction;
    
                    PlayerController.Instance.enabled = false;
                    CameraController.Instance.enabled = false;
    
                    Cursor.lockState = CursorLockMode.None;
                    Cursor.visible = true;
    
                    gameObject.GetComponentInChildren<Padlock>().enabled = true;
    
                    break;
                default:
                    break;
            }
        }
        public void StopInteraction()
        {
            switch (ScriptableObject.Type)
            {
                case EntityType.Key:
                    break;
                case EntityType.VideoTape:
                    break;
                case EntityType.Note:
                    break;
                case EntityType.Painting:
                    break;
                case EntityType.TV:
                    InteractionController.Instance.OnStopInteraction -= StopInteraction;
    
                    InteractionController.Instance.ReturnToInteractionPosition();
                    InteractionController.Instance.CurrentInteraction = InteractionType.None;
    
                    GameManager.ResumeGame();
    
                    break;
                case EntityType.Door:
                    break;
                case EntityType.SafeDigital:
                    gameObject.GetComponentInChildren<SafeDoor>().enabled = false;
    
                    Cursor.lockState = CursorLockMode.Locked;
                    Cursor.visible = false;
    
                    PlayerController.Instance.enabled = true;
                    CameraController.Instance.enabled = true;
    
                    InteractionController.Instance.OnStopInteraction -= StopInteraction;
    
                    InteractionController.Instance.CurrentInteraction = InteractionType.None;
                    InteractionController.Instance.ReturnToInteractionPosition();
    
                    GameManager.ChangeTypeOfControll(TypesOfControl.PlayerControl);
                    GameManager.ResumeGame();
    
                    break;
                case EntityType.SafePadlock:
                    Cursor.lockState = CursorLockMode.Locked;
                    Cursor.visible = false;
    
                    PlayerController.Instance.enabled = true;
                    CameraController.Instance.enabled = true;
    
                    InteractionController.Instance.OnStopInteraction -= StopInteraction;
    
                    InteractionController.Instance.CurrentInteraction = InteractionType.None;
                    InteractionController.Instance.ReturnToInteractionPosition();
    
                    GameManager.ChangeTypeOfControll(TypesOfControl.PlayerControl);
                    GameManager.ResumeGame();
    
                    break;
                default:
                    break;
            }
        }
    }


    public class Item : Entity
    {
        [SerializeField] private ItemScriptableObject _scriptableObject;
        public override EntityScriptableObject ScriptableObject => _scriptableObject;
    
        public void Use()
        {
            switch (_scriptableObject.Type)
            {
                case EntityType.Key:
                    Player.Inventory.Remove(this);
                    Debug.Log("Key used.");
    
                    break;
                case EntityType.VideoTape:
                    if (InteractionController.Instance.CurrentInteraction != InteractionType.TV)
                        return;
    
                    Player.Inventory.Remove(this);
                    Debug.Log("Tape inserted.");
    
                    break;
                case EntityType.Note:
                    break;
                case EntityType.Painting:
                    break;
                case EntityType.TV:
                    break;
                case EntityType.Door:
                    break;
                case EntityType.SafeDigital:
                    break;
                case EntityType.SafePadlock:
                    break;
                default:
                    break;
            }
        }
    }


    public class Document : Entity
    {
        [SerializeField] private DocumentScriptableObject _scriptableObject;
        public override EntityScriptableObject ScriptableObject => _scriptableObject;
    
        public void Read()
        {
            switch (ScriptableObject.Type)
            {
                case EntityType.Key:
                    break;
                case EntityType.VideoTape:
                    break;
                case EntityType.Note:
                    DocumentInspectorController.Instance.enabled = true;
                    DocumentInspectorController.Instance.Initialize(_scriptableObject.Text);
    
                    break;
                case EntityType.Painting:
                    break;
                case EntityType.TV:
                    break;
                case EntityType.Door:
                    break;
                case EntityType.SafeDigital:
                    break;
                case EntityType.SafePadlock:
                    break;
                default:
                    break;
            }
        }
    }


    Так как Item содержит метод Use, а Document содержит метод Read, читать можно только объекты типа Document, использовать только Item, а взаимодействовать с любыми Entity. Изначально классы этих объектов были абстрактными и содержали свойства которые описывали эти объекты (название, описание, спрайт и т.д.), и от каждого объекта наследовали уже конкретные классы. Например от класса Item наследовали классы Key и VidoTape. Для хранения данных всех сущностей я изначально использовал использовал статический класс единственным свойством, которое являлось Dictionary. В нём я создавал экземпляры классов и через конструкторы задавал им свойства. Но сейчас для удобства решил вместо этого как хранилище данных использовать ScriptableObject-ы. А весь функционал реализовать в классах которые до этого были абстрактными используя switch по типам сущностей.
    Написано
  • The same field name is serialized multiple times in the class or its parent class. Как исправить ошибку?

    @Gwinterion Автор вопроса
    Nik Faraday, Так то что вы выделили это вообще тип а не имя
    Написано
  • The same field name is serialized multiple times in the class or its parent class. Как исправить ошибку?

    @Gwinterion Автор вопроса
    Логичным решением кажется не использовать поля а оставить только свойства и уже их переопределять в дочерних классах, но на сколько мне известно в юнити нет способа отобразить свойство в инспекторе
    Написано
  • The same field name is serialized multiple times in the class or its parent class. Как исправить ошибку?

    @Gwinterion Автор вопроса
    Если вы хотели привести в пример два поля, то привели в пример поле и свойство. Это разные вещи... И ошибка не в этом, у них даже имена разные
    Написано
  • The same field name is serialized multiple times in the class or its parent class. Как исправить ошибку?

    @Gwinterion Автор вопроса
    Александр Ананьев, если изменить имена , то появляется другая проблема.

    KHe2S7o.png

    Теперь из-за того что поле базового класса также сериализуется - в инспекторе появляются оба поля из базового класса и из класса компонента. Мой код не предполагает что в компоненте Document я могу использовать ссылку другого типа кроме DocumentScriptableObject. А если просто оставить поле пустым, то при взаимодействии с объектом летят NullReferenceException-ы. Я пробовал скрывать поле базового класса делая его protected, а в наследуемом классе использовал модификатор new. Но тогда возвращается первая проблема с ошибкой сериализации
    Написано
  • Возможно ли передать значение в обработчик события в windows forms?

    @Gwinterion Автор вопроса
    Правильно ли я понимаю, что, в моём случае, Tag это свойство создаваемой кнопки? Как в таком случае мне получить доступ к этому свойству из обработчика?
  • Можно ли в инспекторе полю типа string присвоить составное значение?

    @Gwinterion Автор вопроса
    К сожалению это не является решением моей проблемы, так как мне необходимо что-бы в инспекторе можно было сразу форматировать значение поля. В предложенном вами решении придётся в скрипте делать проверку данных, которые в себе хранит поле и, при этом хранить уже готовые значения, на которые будет производится замена. С таким же успехом я могу эту проверку делать в методе OnPointerEnter класса TooltipTrigger и уже потом в метод Show передавать необходимые значения.
    В идеале мне хотелось бы иметь возможность напрямую через инспектор передавать в поле content отформатированное значение строки, но я подозреваю что в юнити такое реализовать невозможно.