Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 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
13
Question by esalczynski · Mar 26, 2013 at 11:03 AM · propertydrawer

Get the instance the SerializedProperty belongs to in a CustomPropertyDrawer

Here's what my PropertyDrawer class looks like.

 [CustomPropertyDrawer (typeof(OnChangeAttribute))]
 public class OnChangePropertyDrawer : PropertyDrawer
 {
   public override void OnGUI (Rect position, SerializedProperty prop, GUIContent label)
   {
     EditorGUI.BeginChangeCheck ();
     string val = EditorGUI.TextField (position, label, prop.stringValue);
     if (EditorGUI.EndChangeCheck ()) {
       // ... get instance of object that I'm changing (an instance of ObjectB)
       // ... call a method on that instance

       prop.stringValue = val;
     }
   }
 }

When there's a change to a property in the GUI, I'd like to be able to get the instance of the object that has changed.

Is there any way to get the reference to that instance?

EDIT:

The problem seems to be that the property my PropertyDrawer is drawing is part of an array of objects in another class.

 public class ObjectA : MonoBehaviour {
   public ObjectB[] objects;
 }

 [System.Serializable]
 public class ObjectB {
   [OnChange ("StringValue")] public int myProperty
 }

So when I change myProperty in the inspector, the value of prop.serializedObject.targetObject in the PropertyDrawer class is an instance of type ObjectA. I want the instance of ObjectB that I've changed.

EDIT:

ObjectA is a MonoBehaviour class that I've added as a component to a GameObject. ObjectB is a generic, serializable class I defined, the instances of which are created in the inspector. I set the size of the array, and enter the properties of each instance shown in the inspector.

Comment
Add comment · Show 7
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 whydoidoit · Mar 26, 2013 at 11:28 AM 0
Share

I haven't tried - but can't you use the reference to the SerializedObject and that path, parsing out the parent's path value?

avatar image whydoidoit · Mar 26, 2013 at 11:29 AM 0
Share

Probably better to keep going here rather that that other Q you commented on...

avatar image esalczynski · Mar 26, 2013 at 11:35 AM 0
Share

@whydoidoit I'm pretty new to Unity, and I'm not quite sure what you mean. Would you be able to provide a code (or pseudo code) example of what you mean?

avatar image esalczynski · Mar 26, 2013 at 11:53 AM 0
Share

@whydoidoit I think you're on to something. prop.propertyPath outputs objects.Array.data[0].myProperty. However, I'm not really sure what to do with that information. I know I want to get to the property at objects.Array.data[0] (which would be an instance of ObjectB), but prop.FindPropertyRelative ("objects.Array.data[0]") returns Null.

avatar image whydoidoit · Mar 26, 2013 at 12:40 PM 0
Share

So the serialized property has a reference to its serializedObject and then you'd use FindProperty on that, with the parsed out parent name, to get the value.

Show more comments

3 Replies

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

Answer by whydoidoit · Mar 27, 2013 at 01:19 AM

Here you go - this will use reflection to descend to the parent of the object you are looking with a SerializedProperty

 using UnityEngine;
 using System.Collections;
 using UnityEditor;
 using System.Linq;
 using System;
 using System.Reflection;

 ...

 public object GetParent(SerializedProperty prop)
 {
     var path = prop.propertyPath.Replace(".Array.data[", "[");
     object obj = prop.serializedObject.targetObject;
     var elements = path.Split('.');
     foreach(var element in elements.Take(elements.Length-1))
     {
         if(element.Contains("["))
         {
             var elementName = element.Substring(0, element.IndexOf("["));
             var index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[","").Replace("]",""));
             obj = GetValue(obj, elementName, index);
         }
         else
         {
             obj = GetValue(obj, element);
         }
     }
     return obj;
 }
 
 public object GetValue(object source, string name)
 {
     if(source == null)
         return null;
     var type = source.GetType();
     var f = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
     if(f == null)
     {
         var p = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
         if(p == null)
             return null;
         return p.GetValue(source, null);
     }
     return f.GetValue(source);
 }
 
 public object GetValue(object source, string name, int index)
 {
     var enumerable = GetValue(source, name) as IEnumerable;
     var enm = enumerable.GetEnumerator();
     while(index-- >= 0)
         enm.MoveNext();
     return enm.Current;
 }

Here's a test project showing how it works by just inspecting all of the properties of a particular type and checking their parents: Example


serializedproperty.unitypackage.zip (6.4 kB)
Comment
Add comment · Show 18 · 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 whydoidoit · Mar 27, 2013 at 01:35 AM 1
Share

Sorry I dumbly had it finding the actual object the first time I posted :S Fixed it now.

avatar image whydoidoit · May 17, 2013 at 03:47 PM 2
Share

Thanks, I really appreciate it. No, I don't work for Unity - I'm just a user :)

avatar image vexe · Mar 17, 2014 at 10:40 PM 5
Share

@whydoidoit there's actually a FieldInfo instance in PropertyDrawer that gives you reflection access to the property being drawn ;) - so just fieldInfo.GetValue(property.serializedObject); - from the 4.3 notes:

Editor: PropertyDrawers now have a fieldInfo property that can be used to obtain reflection data about the member that the property represents.

avatar image arthur_cottos vexe · Aug 23, 2018 at 03:47 PM 0
Share

Omg you saved my life. I was looking for a solution to my problem for an hour now, when I saw this. Thank you so much!

avatar image aresgameshk vexe · Dec 31, 2020 at 11:01 AM 0
Share

Just FYI, property.serializedObject.targetObject returns the Component being inspected. Which means fieldInfo.GetValue(property.serializedObject.targetObject) will not work for any nested fields that is not declared on the component.

If you have a field in Serializable Class C, inside Serializable Class B, defined inside $$anonymous$$onoBehaviour Class A, you will end up needing whydoidoit's GetParent function as well in order to call fieldInfo Get/Set functions.

avatar image codingChris · Nov 07, 2014 at 03:06 PM 5
Share

@vexe thank you for that hint. This is really awesome. Just a small correction though: it should read

 fieldInfo.GetValue(property.serializedObject.targetObject)


avatar image UsefulYdyot · Jun 18, 2021 at 10:47 AM 1
Share

Best answer that works for me. Thank you so much! Based upon this, another functionality of GetValue() was what I desired:

 SomeSettings settings = (SomeSettings)PropertyDrawerUtility.GetValue(property);

It is just based on GetParent(), but does not skip the last element.

 static public object GetValue(SerializedProperty prop)
 {
         string path = prop.propertyPath.Replace(".Array.data[", "[");
         object obj = prop.serializedObject.targetObject;
         string[] elements = path.Split('.');
 
         foreach (string element in elements.Take(elements.Length))
         {
                 if (element.Contains("["))
                 {
                         string elementName = element.Substring(0, element.IndexOf("["));
                         int index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
                         obj = GetValue(obj, elementName, index);
                 }
                 else
                 {
                         obj = GetValue(obj, element);
                 }
         }
 
         return obj;
 }
avatar image c8theino UsefulYdyot · Mar 22 at 12:02 PM 0
Share

Thank you this was the only solution which worked correctly with lists!

Show more comments
avatar image
2

Answer by idbrii · Feb 21, 2018 at 10:20 PM

If your class is a SerializedObject (or any child of Object), you can use property.objectReferenceValue to get a reference to it.

 GameObject ob = property.objectReferenceValue as GameObject;


As vexe & codingChris point out, you can use PropertyDrawer.fieldInfo for all Serializable types:

 GameObject ob = fieldInfo.GetValue(property.serializedObject.targetObject) as GameObject;
 ObjectB ob = fieldInfo.GetValue(property.serializedObject.targetObject) as ObjectB;

All of these methods work even if your object is inside an array.


Demonstration of using this code:

 [Demo]
 public GameObject thing;

 [CustomPropertyDrawer(typeof(DemoAttribute))]
 public class DemoDrawer : PropertyDrawer {
     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
         EditorGUI.BeginProperty(position, label, property);
         {
             Debug.Log(string.Format("objectReferenceValue={0} fieldInfo={1}", property.objectReferenceValue, fieldInfo.GetValue(property.serializedObject.targetObject) as GameObject));
             EditorGUI.PropertyField(position, property);
         }
         EditorGUI.EndProperty();
     }
 }
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 WoofyWyzpets · Mar 12, 2019 at 01:05 AM 1
Share

All of these methods work even if your object is inside an array.

It works on the first level of the object, but no, it does NOT work inside arrays. Just tested it for 2018.3.
avatar image
1

Answer by Bubsavvy · Oct 02, 2019 at 02:52 AM

Just to improve upon @whydoidoit's answer. If you want to find derived fields and properties when fetching the value, you can do the following.

 public static object GetValue(object source, string name)
     {
         if (source == null)
             return null;
 
         var type = source.GetType();
 
         var f = FindFieldInTypeHierarchy(type, name);
 
         if (f == null)
         {
             var p = FindPropertyInTypeHierarchy(type, name);
             if (p == null)
                 return null;
             return p.GetValue(source, null);
         }
         return f.GetValue(source);
     }
 
     public static FieldInfo FindFieldInTypeHierarchy(Type providedType, string fieldName)
     {
         FieldInfo field = providedType.GetField(fieldName, (BindingFlags)(-1));
         
 
         while (field == null && providedType.BaseType != null)
         {
             providedType = providedType.BaseType;
             field = providedType.GetField(fieldName, (BindingFlags)(-1));
         }
 
         return field;
     }
 
     public static PropertyInfo FindPropertyInTypeHierarchy(Type providedType, string propertyName)
     {
         PropertyInfo property = providedType.GetProperty(propertyName, (BindingFlags)(-1));
 
 
         while (property == null && providedType.BaseType != null)
         {
             providedType = providedType.BaseType;
             property = providedType.GetProperty(propertyName, (BindingFlags)(-1));
         }
 
         return property;
     }
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

26 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

Related Questions

Statefull PropertyDrawer 0 Answers

Caching data for a PropertyDrawer 1 Answer

How to handle this case: elements inside an array has a CustomPropertyDrawer? 1 Answer

How to find the first child serializedProperty inside a PropertyDrawer 1 Answer

How To Draw a PropertyDrawer within a PropertyDrawer 0 Answers


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