Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

SerialiedProperty check if it has a PropertyAttribute

Discussion in 'Scripting' started by emrys90, Oct 13, 2016.

  1. emrys90

    emrys90

    Joined:
    Oct 14, 2013
    Posts:
    752
    I'm making a custom editor, and there's a case in it where I want to check if a SerializedProperty has a custom PropertyAttribute on it, and if so to get a value from that attribute. How can I do this?

    To be more specific as well, I need it to work without knowing which fields I need to check. This is a base level editor that replaces the drawing for Unity somewhat, it loops through all serialized properties and conditionally displays them.
     
  2. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    816
  3. emrys90

    emrys90

    Joined:
    Oct 14, 2013
    Posts:
    752
    That requires you to get the type of the field. I haven't seen any info in SerializedProperty about getting the C# type of the field. I see getting the type name, but then you wouldn't have the field info to get the attribute from.
     
  4. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    When you say custom Property attribute do you mean things like:
    [Range(0f,1f)]
     
  5. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I haven't played with editor scripting in a while. But I'm thinking you can do something with SerialisedProperty.serialisedObject, SerialisedObject.targetObject and SerialisedProperty.name.

    It would require some reflection. I'm thinking a process like this
    • Get the SerialisedObject from the SerialisedProperty
    • Get the Object from SerialisedObject
    • Check the type of the Object
    • Iterate through the fields on the Object and find one that matches SerialisedProperty.name
    • Get the FieldInfo of the target field
    • Check the FieldInfo for attributes
    There is probably an easier way, however I can't find it at the moment. This way should work, even if it is a little painful.

    Edit:

    Or you could ignore all of that and simply grab the attributes directly from PropertyDrawer.
     
    brownboot67 likes this.
  6. emrys90

    emrys90

    Joined:
    Oct 14, 2013
    Posts:
    752
    @BoredMormon That's what I wound up doing. Not too elegant, and won't work for nested types inside of this class though. I was hoping Unity would provide access to it in some way, they must already be tracking this info for each serialized property otherwise how could things like property drawers work.

    Using a PropertyDrawer on these fields won't work for my case due to how I am displaying the field in this custom editor.
     
  7. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    816
    What about SerializedProperty.Type?

    https://docs.unity3d.com/ScriptReference/SerializedProperty-type.html
     
  8. emrys90

    emrys90

    Joined:
    Oct 14, 2013
    Posts:
    752
    That returns just the name of the type, a string value. That isn't enough info to be able to get the attributes on the field, is it?
     
  9. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    No. As I understand it you need either the fieldInfo or the underlying object type. So one level up from the property.
     
  10. Reahreic

    Reahreic

    Joined:
    Mar 23, 2011
    Posts:
    253
    I know it's a long dead resurrection, but yes.
     
  11. Castle24

    Castle24

    Joined:
    Feb 26, 2018
    Posts:
    3
    The greeting from 2020. Unity still not provide the method to handle the attributes directly. But it finally provides its source code. The below code is how it gets the attributes of PropertyField(), which is ScriptAttributeUtility.GetHandler.
    Code (CSharp):
    1.  
    2.             // Determine if SerializedObject target is a script or a builtin type
    3.             UnityEngine.Object target = property.serializedObject.targetObject;
    4.             if (NativeClassExtensionUtilities.ExtendsANativeType(target))
    5.             {
    6.                 // For scripts, use reflection to get FieldInfo for the member the property represents
    7.                 field = GetFieldInfoFromProperty(property, out propertyType);
    8.  
    9.                 // Use reflection to see if this member has an attribute
    10.                 attributes = GetFieldAttributes(field);
    11.             }
    12.             else
    13.             {
    14.                 // For builtin types, look if we hardcoded an attribute for this property
    15.                 // First initialize the hardcoded properties if not already done
    16.                 if (s_BuiltinAttributes == null)
    17.                     PopulateBuiltinAttributes();
    18.  
    19.                 if (attributes == null)
    20.                     attributes = GetBuiltinAttributes(property);
    21.             }
    Of course, these two nice methods are private. But the logic behind them is the same as @Kiwasi said except unity also caches them.
    I also test the nested class with the same idea and it works. Maybe you can share your code. I know it's hard to ask you to find these ancient codes.;)
     
    Last edited: Feb 10, 2020
  12. Reahreic

    Reahreic

    Joined:
    Mar 23, 2011
    Posts:
    253
    Dear past self who commented in this thread.
    Why didn't you add the solution you uncovered to your request?

    Sincerely
    present self
    --

    As I ended up here again (after 2 years) I decided to include what I used to satisfy these needs. (After digging it out of the box of misc. scripts) I don't take credit for inventing this as it doesn't match my personal variable naming style, indicating I encountered it in the wild somewhere.

    The below is readily modified to work with the various unity attributes (that I've implemented at least) as an extension method to SerializedProperty.

    Code (CSharp):
    1.  
    2. public static RangeAttribute GetRangeAttribute(this SerializedProperty prop, bool inherit) {
    3.     if (prop == null) { return null; }
    4.  
    5.     Type t = prop.serializedObject.targetObject.GetType();
    6.  
    7.     FieldInfo f = null;
    8.     PropertyInfo p = null;
    9.     foreach (var name in prop.propertyPath.Split('.')) {
    10.         f = t.GetField(name, (BindingFlags)(-1));
    11.  
    12.         if (f == null) {
    13.             p = t.GetProperty(name, (BindingFlags)(-1));
    14.             if (p == null) {
    15.                 return null;
    16.             }
    17.             t = p.PropertyType;
    18.         } else {
    19.             t = f.FieldType;
    20.         }
    21.     }
    22.  
    23.     RangeAttribute[] attributes;
    24.  
    25.     if (f != null) {
    26.         attributes = f.GetCustomAttributes(typeof(RangeAttribute), inherit) as RangeAttribute[];
    27.     } else if (p != null) {
    28.         attributes = p.GetCustomAttributes(typeof(RangeAttribute), inherit) as RangeAttribute[];
    29.     } else {
    30.         return null;
    31.     }
    32.         return attributes.Length > 0 ? attributes[0] : null;
    33. }
    34.  
     
    florianBrn, Grhyll, _geo__ and 4 others like this.
  13. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    335
  14. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    335
    By the way, if you need the get the attributes of the field PropertyDrawer itself represents, you can just use PropertyDrawer.fieldInfo:
    Code (CSharp):
    1. // Inside a ProprtyDrawer
    2. var myCustomAttribute = fieldInfo.GetCustomAttribute<MyCustomAttribute>(false);
     
    hamza_unity995 likes this.
  15. EJSainz

    EJSainz

    Joined:
    Mar 24, 2015
    Posts:
    29
    Thanks everyone, I've been preparing an editor-driven FSM heavily based on PropertyDrawers, and found the necessity of having a CustomPropertyDrawer with a different treatment based on the presence of an attribute.

    Unity community keeps being great.