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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Question Can you make expression bodied properties visible in the inspector?

Discussion in 'Scripting' started by petey, Aug 5, 2022.

  1. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,771
    Hi there,
    Just wondering if there's a simple way to make expression bodied properties visible in the inspector?

    Code (CSharp):
    1.     public float speed;
    2. public float turboSpeed => speed * 2;
    Something like this might just be handy to see in the inspector.

    I'm guessing I have to write a full editor script but kinda hoping there's an easy way?
    Thanks,
    Pete
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,945
    Whatever was contained in the expression would need to be a reversible function.

    In your case it is, but often it is not, so I'm guessing "no."

    Like what if turboSpeed was set to return speed * speed ? There are two possible real number solutions to speed that would satisfy any nonzero value you put into turboSpeed.

    Maybe what you want in the above would two properties, speed and turboSpeed, or speed and turboSpeedMultiplier ?
     
    petey likes this.
  3. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    6,003
    No simple way without custom editor work, honestly. Addons like Odin Inspector do make this trivial, for a price of course.
     
    petey likes this.
  4. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,771
    Okay that makes sense. I'm just getting my head around using them.
    Yeah I don't really want to go down the Odin path, it seems handy though.

    Thanks!
     
  5. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,127
    An intriguing question.
    If you don't mind adding a temporary Debug Component to your object then I have something for you.

    PropertyInspector.gif

    Here is the code (very ugly, as I've just written it as a proof of concept):

    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 CustomPropertyInspector : MonoBehaviour
    13. {
    14.     public Component Target;
    15.     public string PropertyName;
    16. }
    17.  
    18. #if UNITY_EDITOR
    19. [CustomEditor(typeof(CustomPropertyInspector))]
    20. public class CustomPropertyInspectorEditor : Editor
    21. {
    22.     SerializedProperty targetProp;
    23.     SerializedProperty propertyNameProp;
    24.  
    25.     void OnEnable()
    26.     {
    27.         targetProp = serializedObject.FindProperty("Target");
    28.         propertyNameProp = serializedObject.FindProperty("PropertyName");
    29.     }
    30.  
    31.     public override void OnInspectorGUI()
    32.     {
    33.         EditorGUILayout.PropertyField(targetProp);
    34.  
    35.         Component targetComp = targetProp.objectReferenceValue as Component;
    36.         if (targetComp == null)
    37.         {
    38.             GUILayout.Label("No target selected.");
    39.             return;
    40.         }
    41.  
    42.         // get properties from the comp
    43.         PropertyInfo[] typePropertyInfo;
    44.         typePropertyInfo = targetComp.GetType().GetProperties();
    45.  
    46.         PropertyInfo[] componentPropertyInfo;
    47.         componentPropertyInfo = typeof(Component).GetProperties();
    48.  
    49.         // fetch the value and draw it
    50.         if (!string.IsNullOrEmpty(propertyNameProp.stringValue))
    51.         {
    52.             GUILayout.BeginHorizontal();
    53.             GUILayout.Label(propertyNameProp.stringValue + ":");
    54.             GUILayout.FlexibleSpace();
    55.             string value = "null";
    56.             for (int i = 0; i < typePropertyInfo.Length; i++)
    57.             {
    58.                 if (typePropertyInfo[i].Name == propertyNameProp.stringValue)
    59.                     value = typePropertyInfo[i].GetValue(targetComp).ToString();
    60.             }
    61.             GUILayout.Label(value);
    62.             GUILayout.EndHorizontal();
    63.         }
    64.         else
    65.         {
    66.             GUILayout.BeginHorizontal();
    67.             GUILayout.Label("Property:");
    68.             GUILayout.FlexibleSpace();
    69.             GUILayout.Label("No property selected.");
    70.             GUILayout.EndHorizontal();
    71.         }
    72.  
    73.         // search throuch the type and list the properties
    74.         for (int i = 0; i < typePropertyInfo.Length; i++)
    75.         {
    76.             // Skip Component internal properties (we are not interested in them)
    77.             bool skip = false;
    78.             for (int c = 0; c < componentPropertyInfo.Length; c++)
    79.             {
    80.                 if (typePropertyInfo[i].Name == componentPropertyInfo[c].Name)
    81.                 {
    82.                    // Hey M$, how about "continue 2" support in c# ^^
    83.                     skip = true;
    84.                     break;
    85.                 }
    86.             }
    87.             if (skip)
    88.                 continue;
    89.  
    90.             // Skip Unity basic properties (we are not interested in them)
    91.             if (typePropertyInfo[i].Name == "useGUILayout"
    92.                 || typePropertyInfo[i].Name == "runInEditMode"
    93.                 || typePropertyInfo[i].Name == "enabled"
    94.                 || typePropertyInfo[i].Name == "isActiveAndEnabled"
    95.                 )
    96.                 continue;
    97.  
    98.             GUI.enabled = typePropertyInfo[i].Name != propertyNameProp.stringValue;
    99.             if (GUILayout.Button(typePropertyInfo[i].Name))
    100.             {
    101.                 propertyNameProp.stringValue = typePropertyInfo[i].Name;
    102.             }
    103.         }
    104.         GUI.enabled = true;
    105.  
    106.         serializedObject.ApplyModifiedProperties();
    107.     }
    108. }
    109.  
    110. public class CustomPropertyInspectorRemover : IProcessSceneWithReport
    111. {
    112.     public int callbackOrder => 0;
    113.  
    114.     public void OnProcessScene(Scene scene, BuildReport report)
    115.     {
    116.         if (EditorApplication.isPlayingOrWillChangePlaymode)
    117.             return;
    118.  
    119.         var roots = scene.GetRootGameObjects();
    120.         foreach (var root in roots)
    121.         {
    122.             var comps = root.GetComponentsInChildren<CustomPropertyInspector>(includeInactive: true);
    123.             foreach (var comp in comps)
    124.             {
    125.                 Object.DestroyImmediate(comp);
    126.             }
    127.         }
    128.     }
    129. }
    130. #endif
    It's meant only for debugging as it is inefficient and uses reflections. It does not depend on which type you inspect as long as it's derived from Component. It uses the ToString() to display the value. So that might not always give you what you need.
    And as Kurt said, it only reads the values.

    I've added a hook into IProcessSceneWithReport. So in case you forget to remove it, it will destroy itself at BUILD TIME.

    CAVEAT: I have not yet used this in production. It litterally just came out of my keyboard.
     
    Last edited: Aug 6, 2022
  6. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,771
    Oh wow, that's pretty interesting :D Thanks for taking a look at that @_geo__
     
  7. MarkHelsinki

    MarkHelsinki

    Joined:
    Jul 14, 2021
    Posts:
    23
    Add it as a Favourite in the Asset store and wait for it to come on sale, which it usually does once or twice a year. You won't regret it. The list of benefits is extensive. It's one of those things that you wonder why or how you ever worked without it.
     
  8. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,771
    Yeah, I'll grab it. Looks like it'll save heaps of time. Just waiting for a good break in my work to add it. :)