Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

439
Views
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 answers
Answer question

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 Report

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 Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!