Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Resolved Trying to access properties inside a class with SerializedProperty not working

Discussion in 'Scripting' started by PaperMouseGames, Feb 16, 2022.

  1. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Hi there, I'm trying to access the following class:

    Code (CSharp):
    1. [Serializable]
    2.     public class BasicItemProperties
    3.     {
    4.         public string itemName;
    5.         public ItemType itemType;
    6.         public Sprite itemSprite;
    7.         public string itemDescription;
    8.     }
    With this custom editor code:

    Code (CSharp):
    1. BasicItemProperties temp = basicItemProperties.objectReferenceValue
    2.                     as object as BasicItemProperties;
    3.  
    4.                 ItemType itemTypeTest = temp.itemType;
    I was able to get this working when temp was a ScriptableObject (though the cast was slightly different), but now that temp is a class that isn't a Unity.object I'm getting errors.

    I'm sure there must be a way to access the properties of a class from a custom editor but I just can't seem to get it working.

    Any tips on this?
     
  2. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    Hi,

    I'm not sure if I understood you right, but here it goes.

    Say you have a MB class with a field of type Item, named item. You can get it like this in your custom editor:
    Code (CSharp):
    1. var item = serializedObject.FindProperty("item");
    Your Item might look something like this (doesn't make much sense, but I hope it doesn't matter):
    Code (CSharp):
    1. [System.Serializable]
    2. public class Item
    3. {
    4.    // some fields
    5.    public string itemName;
    6.    public FoodValues food;
    7. }
    To get a field in your Item from your custom editor, you can then do something like this:
    Code (CSharp):
    1. // Get food
    2. var food = item.FindPropertyRelative("food");
    And then you can draw it for example:
    Code (CSharp):
    1. EditorGUILayout.PropertyField(food, true);
     
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,560
    That's because SerializedProperty.objectReferenceValue returns a UnityEngine.Object.

    BasicItemProperties is not a UnityEngine.Object.

    That property doesn't return you class for the same reason 'floatValue' doesn't return you class. That property isn't for that.

    Unfortunately SerializedProperty doesn't have a property to return plain old objects. It's just a quark of how Unity designed their serialization engine.
     
  4. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Thanks for the response, I'll try this out. In this example, would
    var food
    be of type
    FoodValues
    ?

    I don't need to draw the properties, they are being drawn fine by the editor, but I'm trying to compare them and so I need to make sure I get the actual property type if that makes sense. So in your example I need food to be of type
    FoodValues
    .
     
  5. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Hey thanks for the info! So there's no straightforward way of getting a non-Unity object from a serialized field?

    I can't try the above method right now suggested by eses, but if that works that looks pretty good! The string references aren't ideal but I really only need to do it once or twice in this editor.
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,560
    Depdends on what you need to do.

    If you need to access serialized members of the subobject, eses suggestion will work.

    So say your script was:
    Code (csharp):
    1. public class MyScript : MonoBehaviour
    2. {
    3.     public BasicItemProperties Props;
    4. }
    Then your editor for MyScript to access Props would be:
    Code (csharp):
    1. var props = serializedObject.FindProperty("Props");
    2. var itemNameProp = props.FindPropertyRelative("itemName");
    3. string itemName = itemName.stringValue;
    If you're in a PropertyDrawer for BasicItemProperties, you won't have to find the "Props" property since the OnGUI method passes you that:
    https://docs.unity3d.com/ScriptReference/PropertyDrawer.html

    But if you needed to access non-serialized members (like methods/functions or fields/properties marked as nonserialized). Then you'd have to get a ref to your MyScript and just read from it directly:
    Code (csharp):
    1. var script = serializedObject.targetObject as MyScript;
    2. var val = script.Props.SomeUnserializedProperty;
    If you were in a PropertyDrawer again, and you didn't know that we were a member of MyScript. Well it gets complicated then since you'd have to reflect it. Here's an old thread talking about it:
    https://forum.unity.com/threads/get-a-general-object-value-from-serializedproperty.327098/
     
  7. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Maybe I'm doing this wrong but none of these options are working for me. I'll look more closely at the thread linked by @lordofduct though, maybe I can find a solution there.

    Just to reiterate I'm trying to get the
    ItemType
    inside of the
    BasicItemProperties
    from an editor script.

    Here is my
    ItemType
    class:

    Code (CSharp):
    1. [CreateAssetMenu(fileName = "New Item Type", menuName = "Inventory Assets/Item Type")]
    2.     public class ItemType : ScriptableObject
    3.     {
    4.         [SerializeField] ItemCategory itemCategory;
    5.  
    6.         public ItemCategory ItemCategory { get => itemCategory; }
    7.     }
    It's mostly just a simple SO. and it is held inside of the
    BasicItemProperties
    class:

    Code (CSharp):
    1. [Serializable]
    2.     public class BasicItemProperties
    3.     {
    4.         public string itemName;
    5.         public ItemType itemType;
    6.         public Sprite itemSprite;
    7.         public string itemDescription;
    8.     }
    I'm not trying to expose the
    ItemType
    in the inspector. That's already being done with the following code:

    Code (CSharp):
    1. EditorGUILayout.PropertyField(basicItemProperties, new GUIContent("Basic Item Properties"));
    What I'm trying to do is actually get the
    ItemType
    so I can use it. Exactly like I'm doing with this other class
    ItemTypeOrganizer
    in the following code:

    Code (CSharp):
    1. ItemTypeOrganizer organizer = (ItemTypeOrganizer)itemTypeOrganizer.objectReferenceValue;
    2.  
    3.                 organizer.SomeMethod();
    And here is the
    ItemTypeOrganized
    script I'm working on:

    Code (CSharp):
    1. [CreateAssetMenu(fileName = "New ItemType Organizer", menuName = "Inventory Assets/ItemType Organizer")]
    2.     public class ItemTypeOrganizer : ScriptableObject
    3.     {
    4.         public void SomeMethod()
    5.         {
    6.                // Do stuff
    7.         }
    8.     }
    I take it this works because
    ItemTypeOrganized
    inherits from SO and so it's a type of Unity object, where
    BasicItemProperties
    is not. Still, there's gotta be a way to get one the properties inside of the class right?
     
  8. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    Did you already figure this out? I'm not exactly sure what the problem is, but I think you can get access to your ItemType like this (from your custom editor), pretty much what lordofduct said already:
    Code (CSharp):
    1. // Your MB with basic item properties
    2. var script = serializedObject.targetObject as YourMBClassNameHere;
    3.  
    4. // Your basic item properties item type
    5. var itemType = script.basicItemProperties.itemType;
    6.  
    7. // get something from item type
    8. var value = itemType.someValue;
     
  9. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    You're exactly right, this works perfectly. I mist have misunderstood the earlier post and tried to access the properties from the wrong target object. Your comments on the code were very helpful thank you!

    I did also find that this works:

    Code (CSharp):
    1. ItemType itemType = (ItemType)basicItemProperties.FindPropertyRelative("itemType").objectReferenceValue;
    So both ways seem to return the right value for this property.

    I also read through some of this linked thread:

    https://forum.unity.com/threads/get-a-general-object-value-from-serializedproperty.327098/

    At the bottom there's a suggestion by Dr-Nick that seems interesting.

    It's using a new feature called boxedValue: https://docs.unity.cn/2022.1/Documentation/ScriptReference/SerializedProperty-boxedValue.html

    I wonder if that would work. I'm hesitant to upgrade my game to a beta version of Unity, not sure just how stable they are, but maybe once it becomes a full release I'll try that out.

    It seems like it could work, through I'm not 100% sure. Not super familiar with handling data in these Editor scripts, I haven't done that much honestly.

    For now, the solutions suggested here work great though! Thanks everyone!
     
    Bunny83 and eses like this.
  10. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,103
    Right. I haven't used the new boxedValue feature myself since I didn't use Unity for quite some time now ^^. Though I've seen people recognise it and from what I've heard it seems to fill exactly that gap we had for years. So it makes the old cumbersome reflection madness kinda obsolete. Though it seems it still has some limitations. We have to do some more testing I guess.