Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

441
Visualizações
Unity Scriptable Object change field's type to new type based on another fields value

Is it possible for a Scriptable Object to have a field of a specific type, and have that type be determined based on the type of another field? For example:

public class ItemObject : ScriptableObject
{
    public ItemType type;
    public Item data = new Item();
}

ItemType is an enum

public enum ItemType 
{
    Physical,
    Magical
}

Item is a basic class, with 2 other classes inheriting from it, MagicalItem and PhysicalItem.

[System.Serializable]
public class MagicalItem : Item {
    public string passive; 

    public MagicalItem() {
        passive = "";
    }
}
ect.

I'd like ItemObject to be able to choose in the editor from either of these options, or many more as I add new item types to my game. I haven't been able to figure out a solution but my thought is to have the data type be set on Awake() based on the type, but I haven't been able to get that to work. IE something like this:

private void Awake() {
    if(type == ItemType.Magical) {
        data = new MagicalItem();
    } else {
        data = new PhysicalItem();
    }
}

Is there a better way to solve this problem?

Thanks!

over 4 years ago · Santiago Trujillo
2 Respostas
Responde à pergunta

0

Had fun solving this one.

The trick to this that I found is actually a new thing, [SerializeReference], which does a few things, one of which lets you do polymorphic serialization, which this question requires.

The major steps to solving this:

  • Add [SerializeReference] to the data field.
  • Add a static method that creates a new instance depending on the desired type
  • Add a custom editor that checks the type field for changes, and if it changes, create a new instance with the above method and assign it to the data field

Altogether, this gives you this kind of result:

example usage gif

[CreateAssetMenu(fileName = "Data", menuName = "Inventory/Item", order = 1)]
public class ItemObject : ScriptableObject
{
    [CustomEditor(typeof(ItemObject))]
    public class ItemObjectInspector : Editor
    {
        SerializedProperty m_type;
        SerializedProperty m_data;

        private void OnEnable()
        {
            m_type = serializedObject.FindProperty("type");
            m_data = serializedObject.FindProperty("data");
        }

        public override void OnInspectorGUI()
        {
            serializedObject.Update();

            EditorGUI.BeginChangeCheck();
            EditorGUILayout.PropertyField(m_type);
            if (EditorGUI.EndChangeCheck())
            {
                m_data.managedReferenceValue = 
                        ItemObject.CreateBlankData((ItemType)m_type.intValue);
            }

            // avoid drawing type a second time
            // avoid drawing annoying Script field
            DrawPropertiesExcluding(serializedObject, new string[] { "type", "m_Script" });
            serializedObject.ApplyModifiedProperties();
        }
    }

    [System.Serializable]
    public enum ItemType
    {
        Physical,
        Magical
    }

    // Determines how to create an Item from each ItemType
    public static Item CreateBlankData(ItemType type)
    {
        switch (type)
        {
            default:
            case ItemType.Physical:
                return new PhysicalItem();

            case ItemType.Magical:
                return new MagicalItem();
        }
    }

    [System.Serializable]
    public class Item
    {
        [SerializeField] public string typeDescription;
    }

    [System.Serializable]
    public class MagicalItem : Item
    {
        [SerializeField] string elementName;

        public MagicalItem() { typeDescription = "Magical"; elementName = "fire"; }
    }

    [System.Serializable]
    public class PhysicalItem : Item
    {
        [SerializeField] float materialHardness;
        public PhysicalItem() { typeDescription = "Physical"; materialHardness = 1f; }

    }

    public ItemType type;
    [SerializeReference] public Item data;
}

Note: all the above classes are only inner classes for the sake of brevity. They can be separate classes or inner classes with no issues.

over 4 years ago · Santiago Trujillo Relatório

0

Just as an addition to Ruzihm's good answer.

For this kind of stuff you could actually skip the overhead of implementing a custom editor and simply use OnValidate

This function is called when the script is loaded or a value is changed in the Inspector (Called in the editor only).

So simply e.g.

private void OnValidate()
{
    switch(type)
    {
        case ItemType.Magical:
            // To avoid losing already filled in data
            // simply check if the current data already has the correct type
            if(data != null && data.GetType != typeof(MagicalItem))
            {
                data = new MagicalItem();
            }
            break;

        case ItemType.Physical:
        default:
            if(data != null && data.GetType != typeof(PhysicalItem))
            {
                data = new PhysicalItem();
            }
            break;
    }
}
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda