¿Es posible que un objeto programable tenga un campo de un tipo específico y que ese tipo se determine en función del tipo de otro campo? Por ejemplo:
public class ItemObject : ScriptableObject { public ItemType type; public Item data = new Item(); }ItemType es una enumeración
public enum ItemType { Physical, Magical }Item es una clase básica, con otras 2 clases heredadas de ella, MagicalItem y PhysicalItem.
[System.Serializable] public class MagicalItem : Item { public string passive; public MagicalItem() { passive = ""; } } ect.Me gustaría que ItemObject pudiera elegir en el editor entre cualquiera de estas opciones, o muchas más, a medida que agrego nuevos tipos de elementos a mi juego. No he podido encontrar una solución, pero mi idea es establecer el tipo de datos en Awake() según el tipo, pero no he podido hacer que funcione. IE algo como esto:
private void Awake() { if(type == ItemType.Magical) { data = new MagicalItem(); } else { data = new PhysicalItem(); } }¿Hay una mejor manera de resolver este problema?
¡Gracias!
Me divertí resolviendo este.
El truco que encontré para esto es en realidad algo nuevo, [SerializeReference] , que hace algunas cosas, una de las cuales te permite hacer serialización polimórfica, que requiere esta pregunta.
Los principales pasos para resolver esto:
[SerializeReference] al campo de data .type y, si cambia, cree una nueva instancia con el método anterior y asígnela al campo de dataEn total, esto le da este tipo de resultado:

[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; }Nota: todas las clases anteriores son solo clases internas en aras de la brevedad. Pueden ser clases separadas o clases internas sin problemas.
Solo como una adición a la buena respuesta de Ruzihm .
Para este tipo de cosas, podría omitir la sobrecarga de implementar un editor personalizado y simplemente usar OnValidate
Esta función se llama cuando se carga el script o se cambia un valor en el Inspector (solo se llama en el editor).
Así que simplemente por ejemplo
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; } }