Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
3
Question by thiezar · Apr 28, 2018 at 03:40 PM · inheritancepropertydrawercustom inspectorpolymorphism

Using custom property drawers with polymorphism

Hello, I can't get the CustomPropertyDrawer to work with polymorphism. Here's a very simple example code:

Models

 [Serializable]
 public class Animal: ScriptableObject
 {
     public string animalName;
 }
 
 [Serializable]
 public class Monkey : Animal
 {
 }
 
 [Serializable]
 public class Lion : Animal
 {
 }
 
 [Serializable]
 [CreateAssetMenu(fileName = "myZoo", menuName = "Zoo", order = 0)]
 public class Zoo : ScriptableObject
 {
     public string title;
     public List<Animal> animals = new List<Animal>();
 
     private void OnEnable()
    {
    }

    public void AddLion()
    {
        Lion lion = ScriptableObject.CreateInstance<Lion>();
        AssetDatabase.AddObjectToAsset(lion, this);
        AssetDatabase.SaveAssets();
        animals.Add(lion);
    }

    public void AddMonkey()
    {
        Monkey monkey = ScriptableObject.CreateInstance<Monkey>();
        AssetDatabase.AddObjectToAsset(monkey, this);
        AssetDatabase.SaveAssets();
        animals.Add(monkey);
    }
 }

Zoo Editor

 [CustomEditor(typeof(Zoo))]
 [CanEditMultipleObjects]
 public class ZooEditor : Editor
 {
     public override void OnInspectorGUI()
     {
         base.OnInspectorGUI();
 
         if(GUILayout.Button("Add lion"))
         {
             (serializedObject.targetObject as Zoo).AddLion();
         }
 
         if (GUILayout.Button("Add monkey"))
         {
             (serializedObject.targetObject as Zoo).AddMonkey();
         }
     }
 }

Property Drawers

 [CustomPropertyDrawer(typeof(Animal))]
 public class AnimalPropertyDrawer : PropertyDrawer
 {
     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
     {
         EditorGUI.LabelField(new Rect(position.x, position.y, position.width, 20), "Hello, I'm an animal");
         SerializedObject serializedObject = new SerializedObject(property.objectReferenceValue as Animal);
         EditorGUI.PropertyField(new Rect(position.x, position.y + 20, position.width, 20), serializedObject.FindProperty("animalName"));
     }
 }
 
 [CustomPropertyDrawer(typeof(Lion))]
 public class LionPropertyDrawer : PropertyDrawer
 {
     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
     {
         EditorGUI.LabelField(new Rect(position.x, position.y, position.width, 20), "Hello, I'm a lion");
         SerializedObject serializedObject = new SerializedObject(property.objectReferenceValue as Lion);
         EditorGUI.PropertyField(new Rect(position.x, position.y + 20, position.width, 20), serializedObject.FindProperty("animalName"));
     }
 }
 
 [CustomPropertyDrawer(typeof(Monkey))]
 public class MonkeyPropertyDrawer : PropertyDrawer
 {
     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
     {
         EditorGUI.LabelField(new Rect(position.x, position.y, position.width, 20), "Hello, I'm a monkey");
         SerializedObject serializedObject = new SerializedObject(property.objectReferenceValue as Monkey);
         EditorGUI.PropertyField(new Rect(position.x, position.y + 20, position.width, 20), serializedObject.FindProperty("animalName"));
     }
 }

alt text

Now, as you can see, even if the zoo has a Lion and a Monkey in the animals list, only the AnimalPropertyDrawer is used to draw both the list items. I know this is a common problem in Unity.

Is there a way to let the child classes use their own property drawer?

What's your best solution to this problem?

property-drawer.png (5.7 kB)
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

3 Replies

· Add your reply
  • Sort: 
avatar image
3
Best Answer

Answer by toxicFork · Apr 29, 2018 at 11:03 AM

The problem is that the Zoo class has a List<Animal>. So it seems that it will do the property drawing using "the property drawer for the Animal type", rather than trying to look deeper into the type specified for each instance.


What you can do is implement the polymorphism handling within the AnimalPropertyDrawer, like this:


 using System;
 using System.Collections.Generic;
 using UnityEditor;
 using UnityEngine;
 
 [CustomPropertyDrawer(typeof(Animal))]
 public class AnimalPropertyDrawer : PropertyDrawer
 {
     private static Dictionary<Type, PropertyDrawer> _polyPropertyDrawers;
 
     public AnimalPropertyDrawer()
     {
         if (_polyPropertyDrawers == null)
         {   // creating the static variable in the constructor so that it can be cached and reused
             _polyPropertyDrawers = new Dictionary<Type, PropertyDrawer>();
 
             _polyPropertyDrawers[typeof(Lion)] = new LionPropertyDrawer();
             _polyPropertyDrawers[typeof(Monkey)] = new MonkeyPropertyDrawer();
             // add other types here
         }
     }
 
     public PropertyDrawer GetPolyPropertyDrawer(SerializedProperty property)
     {
         PropertyDrawer drawer;
 
         if (!_polyPropertyDrawers.TryGetValue(property.objectReferenceValue.GetType(), out drawer))
         {
             return null;
         }
 
         return drawer;
     }
 
     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
     {
         var polyDrawer = GetPolyPropertyDrawer(property);
 
         if (polyDrawer == null)
         {
             return base.GetPropertyHeight(property, label);
         }
 
         return polyDrawer.GetPropertyHeight(property, label);
     }
 
     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
     {
         var polyDrawer = GetPolyPropertyDrawer(property);
 
         if (polyDrawer == null)
         {
             EditorGUI.HelpBox(position, "Cannot find drawer for type " + property.objectReferenceValue.GetType(), MessageType.Error);
         }
         else
         {
             polyDrawer.OnGUI(position, property, label);   
         }
     }
 }

alt text


prop-drawer-poly.png (21.2 kB)
Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image thiezar · Apr 29, 2018 at 02:49 PM 0
Share

I was hoping in a way to let Unity use the specialized type but, if there's no way, this is actually a very good solution. Thank you.

avatar image
0

Answer by Bunny83 · Apr 28, 2018 at 04:09 PM

This has nothing to do with property drawers at all. Your data classes can't be serialized by Unity's serializer since it doesn't support polymorphism for custom serializable classes. To support polymorphism your classes need to be derived either from MonoBehaviour or ScriptableObject.


PropertyDrawers do not influence what get serialized and what not. They just change the way how the serialized data is presented / visualized.


Make sure you read this page carefully

Comment
Add comment · Show 9 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image thiezar · Apr 28, 2018 at 05:38 PM 0
Share

I let Animal to inherits from ScriptableObject (and edited my question acoordingly) but still I have the same behaviour.

Anyway I read the page you linked and noticed this sentence:

The downside of this is that you need to store that $$anonymous$$onobehaviour or scriptable object somewhere, and that you cannot serialize it inline efficiently.

Does that mean that every animal should be saved as an asset inside the Zoo asset?

avatar image Bunny83 thiezar · Apr 28, 2018 at 05:46 PM 0
Share

Yes, if you derive your Animal class from ScriptableObject and want it to be serialized you have to actually store it as asset. If you don't want each animal to be a seperate asset file you would need some editor code to add the instances to your zoo asset.

avatar image Glurth Bunny83 · Apr 28, 2018 at 06:54 PM 0
Share

" if you derive your XXX class from ScriptableObject and want it to be serialized you have to actually store it as asset." Just saw this post. Can you confirm this? I'm pretty sure I can instantiate a ScriptableObject derived class, then simply reference it from a scene object/monobehavior for it to be serialized with the scene, rather than as an asset file.

Show more comments
Show more comments
Show more comments
avatar image
0

Answer by Chazmundo · Mar 15, 2021 at 11:50 PM

Hi, I know I'm a little late to the party, but inspired by this answer and my own frustration:

I've created a generic and easy to use solution for Polymorphic PropertyDrawers in Unity.

I've posted the full details here (ready to Copy & Paste into your own project): https://forum.unity.com/threads/custompropertydrawer-for-polymorphic-class.824667/#post-6702670

P.S. I am posting here as it's one of the top search results when looking for "unity polymorphic property drawer" so hopefully it'll help more people

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

88 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

An OS design issue: File types associated with their appropriate programs 1 Answer

Custom Inspector not working with inheritance. 2 Answers

How to change variables from another script? 1 Answer

Accessing subclass properties in inspector based on enum 1 Answer

How can I access an inherited method from a separate (collided) object? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges