Search Unity

Question Get all exposed variables in any script

Discussion in 'Editor & General Support' started by HairlessGorilla, Aug 6, 2022.

  1. HairlessGorilla

    HairlessGorilla

    Joined:
    Mar 4, 2021
    Posts:
    38
    Hi all!
    I'm working on my first asset which I'm planning on releasing, it will be a saving and loading plugin. But one of the features I'm trying to achieve is to select a gameobject and a component attatched to it and select a property to save. But I'm struggling to figure out how to get every property in a component, as I want it to include custom scripts and unity's built-in components.

    I tried mixing FieldInfo's and PropertyInfo's, but the PropertyInfo returns variables that are not exposed, which is not what I'm looking for. How would I get this to work with ONLY exposed properties?

    Thanks in advance!
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,735
    _geo__ likes this.
  3. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,338
    The default GetProperties does only return public properties. What BindingFlags did you use? I guess what you need depends on your definition of "exposed variables".
     
  4. HairlessGorilla

    HairlessGorilla

    Joined:
    Mar 4, 2021
    Posts:
    38
    I'm trying to get only the variables that you can see in the inspector. When I used GetProperties on a transform component, it returned a lot of random variables with names like LocalToWorldMatrix. I'm trying to get it to only return the things you see in the inspector, like position, rotation and scale.

    Is this possible?
    Thanks.
     
  5. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,338
    Okay, so you are after all the things that get serialized. Have you tried costructing a SerializedObject from the component? Once you have that you can get the iterator and go through all the serialized properties.

    In the thread that Kurt linked the objective was quite the opposite. The OP wanted a solution to inspect non-serialized properties, thus the use of reflections. In your case you might not need reflections.

    However, if the serialized object approach does not work for you you could do what I did in the other thread, which is filtering out all the unity internal properties by name. You could also try to check if the property is a primitive type or has the serializeable attribute to filter the list. It's a tedious task, but it might also work.
     
    Last edited: Aug 7, 2022
  6. HairlessGorilla

    HairlessGorilla

    Joined:
    Mar 4, 2021
    Posts:
    38
    Hey, how would I go about doing the serializeable thing? This stuff is completely new to me and I have no idea how i would go about doing this. Would you mind providing me an example of how I can use a serializedobject to get the serialized properties?
    Thanks.
     
  7. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,338
    The SerializedObject has a constructor which you can hand your object to. Then you can get an iterator in the form of a SerializedProperty from it. Use it to go through the list of serialized properties by calling one of the Next*() methods on it repeatedly until it returns false.

    Mind you: the GetEnumerator() is for traversing INTO the current property. That's not what you want (that might get you at your first try).
     
  8. HairlessGorilla

    HairlessGorilla

    Joined:
    Mar 4, 2021
    Posts:
    38
    Hey, I tried to do this but I had no success. I am really new to this sort of stuff and I know what I was doing was way off. But could you please read over my code and help me?
    Code (CSharp):
    1. foreach (Component c in components)
    2.         {
    3.             SerializedObject obj = new SerializedObject(c);
    4.             SerializedProperty it = obj.GetIterator();
    5.  
    6.             for (int i = 0; i < it.arraySize; i++)
    7.             {
    8.                 Debug.Log(it.NextVisible(true));
    9.             }
    10.         }
    Thanks for your help!
     
  9. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,338
    I admit the API takes a bit getting used to.

    What you are doing with "it.arraySize" is to get the array length of the VERY FIRST property IF it happens to be an array. So in all cases in which your very first property is not a array it will be 0. As it happens the very first property usually is a reference to the script, so chances are it's always 0 (not an array).

    One way to go through an iterator like this is to use a while loop:
    Code (CSharp):
    1. SerializedObject serializedObject = new SerializedObject(c);
    2. serializedObject.Update();
    3. // SerializedProperty now point to the first property
    4. SerializedProperty serializedProperty = serializedObject.GetIterator();
    5. // Enter and skip the script (the script usually is the very first property)
    6. serializedProperty.NextVisible(enterChildren: true);
    7. // iterate the property in a loop until the Next... returns false
    8. while (serializedProperty.NextVisible(enterChildren: false))
    9. {
    10.     // This is where we now we can access each property through serializedProperty
    11.     Debug.Log(serializedProperty.name);
    12. }
     
    Last edited: Aug 7, 2022
  10. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,338
    I have just updated the class I made for the thread that Kurt referenced to use serialized properties. Maybe you can extract what you need from there:

    PropertyInspector.gif

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. #if UNITY_EDITOR
    5. using System.Reflection;
    6. using UnityEditor;
    7. using UnityEngine.SceneManagement;
    8. using UnityEditor.Build.Reporting;
    9. using UnityEditor.Build;
    10. #endif
    11.  
    12. public class CustomSerializedPropertyInspector : MonoBehaviour
    13. {
    14.     public Component Target;
    15.     public string PropertyName;
    16. }
    17.  
    18. #if UNITY_EDITOR
    19. [CustomEditor(typeof(CustomSerializedPropertyInspector))]
    20. public class CustomSerializedPropertyInspectorEditor : Editor
    21. {
    22.     SerializedProperty targetProp;
    23.     SerializedProperty propertyNameProp;
    24.     SerializedObject _tmpSerializedObject; // Editor has a member called serializedProperty. Dont confuse this with it.
    25.     Component _lastComp;
    26.  
    27.     void OnEnable()
    28.     {
    29.         targetProp = serializedObject.FindProperty("Target");
    30.         propertyNameProp = serializedObject.FindProperty("PropertyName");
    31.     }
    32.  
    33.     public override void OnInspectorGUI()
    34.     {
    35.         EditorGUILayout.PropertyField(targetProp);
    36.  
    37.         Component targetComp = targetProp.objectReferenceValue as Component;
    38.         if (targetComp == null)
    39.         {
    40.             GUILayout.Label("No target selected.");
    41.             return;
    42.         }
    43.  
    44.         // Did the target change?
    45.         if(targetComp != _lastComp)
    46.         {
    47.             _lastComp = targetComp;
    48.             _tmpSerializedObject = new SerializedObject(targetComp);
    49.             propertyNameProp.stringValue = null;
    50.         }
    51.         if(_tmpSerializedObject != null)
    52.             _tmpSerializedObject.Update();
    53.         SerializedProperty serializedProperty;
    54.  
    55.         // fetch the value and draw it
    56.         if (!string.IsNullOrEmpty(propertyNameProp.stringValue))
    57.         {
    58.             GUILayout.BeginHorizontal();
    59.             GUILayout.Label(propertyNameProp.stringValue + ":");
    60.             GUILayout.FlexibleSpace();
    61.             string value = "null";
    62.  
    63.             // SerializedProperty now point to the first property
    64.             serializedProperty = _tmpSerializedObject.GetIterator();
    65.             // Enter and skip the script (the script usually is the very first property)
    66.             serializedProperty.NextVisible(enterChildren: true);
    67.             // iterate the property in a loop until the Next... returns false
    68.             while (serializedProperty.NextVisible(enterChildren: false))
    69.             {
    70.                 // This is where we now we can access each property through serializedProperty
    71.                 if (serializedProperty.name == propertyNameProp.stringValue)
    72.                     value = PropertyValueToString(serializedProperty);
    73.             }
    74.             GUILayout.Label(value);
    75.             GUILayout.EndHorizontal();
    76.         }
    77.         else
    78.         {
    79.             GUILayout.BeginHorizontal();
    80.             GUILayout.Label("Property:");
    81.             GUILayout.FlexibleSpace();
    82.             GUILayout.Label("No property selected.");
    83.             GUILayout.EndHorizontal();
    84.         }
    85.  
    86.         // search throuch the type and list the properties
    87.         // SerializedProperty now point to the first property
    88.         serializedProperty = _tmpSerializedObject.GetIterator();
    89.         // Enter and skip the script (the script usually is the very first property)
    90.         serializedProperty.NextVisible(enterChildren: true);
    91.         // iterate the properties of the script in a loop until NextVisible() returns false
    92.         while (serializedProperty.NextVisible(enterChildren: false))
    93.         {
    94.             GUI.enabled = serializedProperty.name != propertyNameProp.stringValue;
    95.             if (GUILayout.Button(serializedProperty.name))
    96.             {
    97.                 propertyNameProp.stringValue = serializedProperty.name;
    98.             }
    99.             GUI.enabled = true;
    100.         }
    101.  
    102.         serializedObject.ApplyModifiedProperties();
    103.     }
    104.  
    105.     public static string PropertyValueToString(SerializedProperty serializedProperty)
    106.     {
    107.         switch (serializedProperty.propertyType)
    108.         {
    109.             case SerializedPropertyType.Integer:
    110.                 return serializedProperty.intValue.ToString();
    111.             case SerializedPropertyType.Boolean:
    112.                 return serializedProperty.boolValue.ToString();
    113.             case SerializedPropertyType.Float:
    114.                 return serializedProperty.floatValue.ToString();
    115.             case SerializedPropertyType.String:
    116.                 return serializedProperty.stringValue;
    117.             case SerializedPropertyType.Vector2:
    118.                 return serializedProperty.vector2Value.ToString();
    119.             case SerializedPropertyType.Vector3:
    120.                 return serializedProperty.vector3Value.ToString();
    121.             // Lots of other types
    122.             default:
    123.                 return serializedProperty.ToString();
    124.         }
    125.     }
    126. }
    127.  
    128. public class CustomSerializedPropertyInspectorRemover : IProcessSceneWithReport
    129. {
    130.     public int callbackOrder => 0;
    131.  
    132.     public void OnProcessScene(Scene scene, BuildReport report)
    133.     {
    134.         if (EditorApplication.isPlayingOrWillChangePlaymode)
    135.             return;
    136.  
    137.         var roots = scene.GetRootGameObjects();
    138.         foreach (var root in roots)
    139.         {
    140.             var comps = root.GetComponentsInChildren<CustomSerializedPropertyInspector>(includeInactive: true);
    141.             foreach (var comp in comps)
    142.             {
    143.                 Object.DestroyImmediate(comp);
    144.             }
    145.         }
    146.     }
    147. }
    148. #endif
    149.  
     
    HairlessGorilla and adamgolden like this.
  11. HairlessGorilla

    HairlessGorilla

    Joined:
    Mar 4, 2021
    Posts:
    38
    Ah thanks! That works perfectly. I have one more question though, do you know why the serializedProperty is not getting some properties? Like it doesn't show rotation or world position?
    Thanks.
     
  12. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,338
    Maybe try Next() instead of NextVisible() or maybe some properties are derived from others (worldPosition for example is derived from localPosition and thus not serialized). You'll have to dig around to find out.
     
  13. HairlessGorilla

    HairlessGorilla

    Joined:
    Mar 4, 2021
    Posts:
    38
    Thanks. That works!
    I just need help with one more thing if you don't mind. How do I set the property's value using the serializedProperty instance of it?
    For example, I'm trying to load data from a file, and I have the serializedProperty version of the property and I can use serializedProperty.SetValue(); but that does not affect the actual components version of the property. How would I make it change the default property?

    Thanks.
     
  14. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,338
    Last edited: Aug 9, 2022
  15. HairlessGorilla

    HairlessGorilla

    Joined:
    Mar 4, 2021
    Posts:
    38
    Hey, so I'm having one final problem. When I try to build the project, I get errors in the console "the type or namespace "SerializedProperty" cannot be found".
    Would you know why I can't use SerializedProperties in my build?
    Thanks.
     
  16. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,338