Need help with UnityRuntimeInspector?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.
yasirkula

Description

Runtime Inspector and Hierarchy solution for Unity for debugging and runtime editing purposes

560 Stars 55 Forks MIT License 54 Commits 0 Opened issues

Services available

Need anything else?

Runtime Inspector & Hierarchy for Unity 3D

screenshot

Available on Asset Store: https://assetstore.unity.com/packages/tools/gui/runtime-inspector-hierarchy-111349

Forum Thread: https://forum.unity.com/threads/runtime-inspector-and-hierarchy-open-source.501220/

Support the Developer ☕

A. ABOUT

This is a simple yet powerful runtime Inspector and Hierarchy solution for Unity 3D that should work on pretty much any platform that Unity supports, including mobile platforms.

B. LICENSE

Runtime Inspector & Hierarchy is licensed under the MIT License (Asset Store version is governed by the Asset Store EULA). Please note that this asset uses an external asset which is licensed under the BSD 3-Clause License.

C. INSTALLATION

There are 5 ways to install this plugin:

  • import RuntimeInspector.unitypackage via Assets-Import Package
  • clone/download this repository and move the Plugins folder to your Unity project's Assets folder
  • import it from Asset Store
  • (via Package Manager) add the following line to Packages/manifest.json:
    • "com.yasirkula.runtimeinspector": "https://github.com/yasirkula/UnityRuntimeInspector.git",
  • (via OpenUPM) after installing openupm-cli, run the following command:
    • openupm add com.yasirkula.runtimeinspector

D. HOW TO

  • To use the hierarchy in your scene, drag&drop the RuntimeHierarchy prefab to your canvas
  • To use the inspector in your scene, drag&drop the RuntimeInspector prefab to your canvas

You can connect the inspector to the hierarchy so that whenever the selection in the hierarchy changes, inspector inspects the newly selected object. To do this, assign the inspector to the Connected Inspector property of the hierarchy.

You can also connect the hierarchy to the inspector so that whenever an object reference in the inspector is highlighted, the selection in hierarchy is updated. To do this, assign the hierarchy to the Connected Hierarchy property of the inspector.

Note that these connections are one-directional, meaning that assigning the inspector to the hierarchy will not automatically assign the hierarchy to the inspector or vice versa. Also note that the inspector and the hierarchy are not singletons and therefore, you can have several instances of them in your scene at a time with different configurations.

E. FEATURES

  • The hierarchy costs 1 SetPass call and ~5 batches (assuming that Sprite Packing is enabled in Editor Settings)
  • The inspector costs 1 SetPass call and ~10 batches (assuming that Sprite Packing is enabled in Editor Settings)
  • Both panels are heavily optimized in terms of GC in order not to generate any unnecessary garbage. By default, both the inspector and the hierarchy are refreshed 4 times a second to reflect any changes to their user interface almost immediately. Each refresh of the inspector generates some garbage for GC since most of the time, the inspected object has variables of value types. These variables are boxed when accessed via reflection and this boxing creates some unavoidable garbage. However, this process can be greatly optimized by increasing the Refresh Interval of the inspector and/or the hierarchy
  • Includes a built-in color picker and a reference picker:

screenshot

  • Visual appearance of the inspector and the hierarchy can be tweaked by changing their Skin. There are two premade skins included in the Skins directory: LightSkin and DarkSkin (pro-only.. meh, just kiddin'). You can create your own skins using the Create-RuntimeInspector-UI Skin context menu

screenshot

Be aware that the changes made to a skin via the Unity Inspector might not be reflected to the UI immediately. To reflect these changes to the UI, you may have to click the cog icon while the skin is selected and select Refresh UI.

E.1. INSPECTOR

screenshot

RuntimeInspector works similar to the editor Inspector. It can expose commonly used Unity types out-of-the-box, as well as custom classes and structs that are marked with System.Serializable attribute. 1-dimensional arrays and generic Lists are also supported.

  • Refresh Interval: as the name suggests, this is the refresh interval of the inspector. At each refresh, values of all the exposed fields and properties are refreshed. This generates some garbage for boxed value types (unavoidable) and thus, increasing this value even slightly should help with GC a lot
  • Expose Fields: determines which fields of the inspected object should be exposed: None, Serializable Only or All
  • Expose Properties: determines which properties of the inspected object should be exposed
  • Array Indices Start At One: when enabled, exposed arrays and lists start their indices at 1 instead of 0 (just a visual change)
  • Use Title Case Naming: when enabled, variable names are displayed in title case format (e.g. m_myVariable becomes My Variable)
  • Show Tooltips: when enabled, hovering over a variable's name for a while will show a tooltip displaying the variable's name. Can be useful for variables whose names are partially obscured
  • Tooltip Delay: determines how long the cursor should remain static over a variable's name before the tooltip appears. Has no effect if Show Tooltips is disabled
  • Nest Limit: imagine exposing a linked list. This variable defines how many nodes you can expose in the inspector starting from the initial node until the inspector stops exposing any further nodes
  • Inspected Object Header Visibility: if the inspected object has a collapsible header, determines that header's visibility
  • Pool Capacity: the UI elements are pooled to avoid unnecessary Instantiate and Destroy calls. This value defines the pool capacity for each of the UI elements individually. On standalone platforms, you can increase this value for better performance
  • Settings: an array of settings for the inspector. A new settings asset can be created using the Create-RuntimeInspector-Settings context menu. A setting asset stores 4 different things:
    • Standard Drawers and Reference Drawers: a drawer is a prefab used to expose a single variable in the inspector. For variables that extend UnityEngine.Object, a reference drawer is created and for other variables, a standard drawer is created
    • While searching for a suitable drawer for a variable, the corresponding drawers list is traversed from bottom to top until a drawer that supports that variable type is found. If such a drawer is not found, that variable is not exposed
    • Hidden Variables: allows you to hide some variables from the inspector for a given type and all the types that extend/implement it. You can enter asterisk character (*) to hide all the variables for that type
    • Exposed Variables: allows you to expose (counter) some hidden variables. A variable goes through a number of filters before it is exposed:
    • Its Type must be serializable
    • It must not have a System.Obsolete, System.NonSerialized or HideInInspector attribute
    • If it is in Exposed Variables, it is exposed
    • It must not be in Hidden Variables
    • it must pass the Expose Fields and Expose Properties filters
    • So, to expose only a specific set of variables for a given type, you can hide all of its variables by entering an asterisk to its Hidden Variables and then entering the set of exposed variables to its Exposed Variables

While changing the inspector's settings, you are advised not to touch InternalSettings; instead create a separate Settings asset and add it to the Settings array of the inspector. Otherwise, when InternalSettings is changed in an update, your settings might get overridden.

E.2. HIERARCHY

screenshot

RuntimeHierarchy simply exposes the objects in your scenes to the user interface. In addition to exposing the currently active Unity scenes in the hierarchy, you can also expose a specific set of objects under what is called a pseudo-scene in the hierarchy. Pseudo-scenes can help you categorize the objects in your scene. Adding/removing objects to/from pseudo-scenes is only possible via the scripting API and helper components.

  • Refresh Interval: the refresh interval of the hierarchy. At each refresh, the destroyed objects are removed from the hierarchy while newly created objects are added to the hierarchy. Sibling indices of the objects are also synced with the Unity Hierarchy at each refresh
  • Object Names Refresh Interval: accessing GameObject.name property generates garbage. Therefore, names of objects in the hierarchy are not synced at each Refresh Interval but rather at each Object Names Refresh Interval to help avoid excessive garbage
  • Search Refresh Interval: the refresh interval for the search results. At each refresh, each GameObject's name is checked to see if it matches the searched term, so this process will generate some garbage
  • Expose Unity Scenes: when disabled, Unity scenes are not exposed in the hierarchy. This is useful when you want to use the hierarchy solely for pseudo-scenes
  • Expose Dont Destroy On Load Scene: when enabled, DontDesroyOnLoad objects will be exposed in the hierarchy
  • Pseudo Scenes Order: the order of the pseudo-scenes from top to bottom in the hierarchy. Note that entering a pseudo-scene here does not automatically create it when the application starts. Pseudo-scenes can be created via the scripting API only
  • Create Dragged Reference On Hold: when enabled, if you click and hold on an object in the hierarchy for a while, a dragged reference item will be created that can be dropped on a reference drawer in the inspector to assign that object to that variable (similar to Unity's drag&drop reference assignment)
  • Dragged Reference Hold Time: this value defines the hold time in seconds before a dragged reference item is created
  • Can Reorganize Items: when enabled, dropping a dragged reference item that holds a Transform onto an object in the hierarchy will change the dragged Transform's parent (similar to parenting in Unity's Hierarchy)
  • Double Click Threshold: when an object in the hierarchy is double clicked, OnItemDoubleClicked event is raised (see SCRIPTING API). This value determines the maximum allowed delay between two clicks to register a double click
  • Show Horizontal Scrollbar: when enabled, a horizontal scrollbar will be displayed if the names displayed in the hierarchy don't fit the available space. Note that only the visible items' width values are used to determine the size of the scrollable area
  • Sync Selection With Editor Hierarchy: simply synchronizes the selected object between the Unity Hierarchy and this RuntimeHierarchy

Additional settings for Can Reorganize Items can be found at the RuntimeHierarchy/ScrollView/Viewport object:

screenshot

  • Sibling Index Modification Area: when a dragged reference item is dropped near the top or bottom edges of a Transform in hierarchy, it will be inserted above or belove the target Transform. This value determines the size of the area near the top and bottom edges
  • Scrollable Area: while hovering the cursor near the top or bottom edges of the scroll view with a dragged reference item, scroll view will automatically be scrolled to show contents in that direction. This value determines the size of the area near the top and bottom edges of the scroll view
  • Scroll Speed: determines how fast the scroll view will be scrolled while hovering the cursor over Scrollable Area
  • Can Drop Parent On Child: when enabled, a dragged reference item can be dropped onto one of its child objects. In this case, the child object will be unparented and then the dragged reference item will become a child of it
  • Can Add Objects To Pseudo Scenes: when enabled, dropping a dragged reference item onto a pseudo-scene or above/below a root object in the pseudo-scene will automatically add it to that pseudo-scene

F. SCRIPTING API

Values of the variables that are mentioned in E.1 and E.2 sections can be tweaked at runtime via their corresponding properties. Any changes to these properties will be reflected to UI immediately. Here, you will find some interesting things that you can do with the inspector and the hierarchy via scripting:

  • You can change the inspected object in the inspector using the following functions:
public void Inspect( object obj );
public void StopInspect();
  • You can access the currently inspected object via the
    InspectedObject
    property of the inspector
  • You can change the selected object in the hierarchy using the following functions:
public bool Select( Transform selection ); // returns true when the selection is changed successfully
public void Deselect();
  • You can access the currently selected object in the hierarchy via the
    CurrentSelection
    property
  • You can call the
    Refresh()
    function on the inspector and/or the hierarchy to refresh them manually
  • You can register to the
    OnSelectionChanged
    event of the hierarchy to get notified when the selection has changed
  • You can register to the
    OnInspectedObjectChanging
    delegate of the inspector to get notified when the inspected object is about to change and, if you prefer, change the inspected object altogether. For example, if you want to inspect only objects that have a Renderer component attached, you can use the following function:
private object OnlyInspectObjectsWithRenderer( object previousInspectedObject, object newInspectedObject )
{
    GameObject go = newInspectedObject as GameObject;
    if( go != null && go.GetComponent() != null )
        return newInspectedObject;

// Don't inspect objects without a Renderer component
return null;

}

  • You can register to the
    ComponentFilter
    delegate of the inspector to filter the list of visible components of a GameObject in the inspector (e.g. hide some components)
  • You can register to the
    GameObjectFilter
    delegate of the hierarchy to hide some objects from the hierarchy (or, you can add those objects to
    RuntimeInspectorUtils.IgnoredTransformsInHierarchy
    and they will be hidden from all hierarchies; just make sure to remove them from this HashSet before they are destroyed)
  • You can register to the
    OnItemDoubleClicked
    event of the hierarchy to get notified when an object in the hierarchy is double clicked
  • You can add RuntimeInspectorButton attribute to your functions to expose them as buttons in the inspector. These buttons appear when an object of that type is inspected. This attribute takes 3 parameters:

    • string label: the text that will appear on the button
    • bool isInitializer: if set to true, and if the function returns an object that is assignable to the type that the function was defined in, the resulting value of the function will be assigned back to the inspected object. In other words, this function can be used to initialize null objects or change the variables of alive objects
    • ButtonVisibility visibility: determines when the button can be visible. Buttons with
      ButtonVisibility.InitializedObjects
      can appear only when the inspected object is not null whereas buttons with
      ButtonVisibility.UninitializedObjects
      can appear only when the inspected object is null. You can use
      ButtonVisibility.InitializedObjects | ButtonVisibility.UninitializedObjects
      to always show the button in the inspector
  • Although you can't add RuntimeInspectorButton attribute to Unity's built-in functions, you can show buttons under built-in Unity types via extension methods. You must write all such extension methods in a single static class, mark the methods with RuntimeInspectorButton attribute and then introduce these functions to the RuntimeInspector as following:

    RuntimeInspectorUtils.ExposedExtensionMethodsHolder = typeof( TheScriptThatContainsTheExtensionsMethods );

F.1. PSEUDO-SCENES

You can use the following functions to add object(s) to pseudo-scenes in the hierarchy:

public void AddToPseudoScene( string scene, Transform transform );
public void AddToPseudoScene( string scene, IEnumerable transforms );

These functions will create the relevant pseudo-scenes automatically if they do not exist.

You can use the following functions to remove object(s) from pseudo-scenes in the hierarchy:

public void RemoveFromPseudoScene( string scene, Transform transform, bool deleteSceneIfEmpty );
public void RemoveFromPseudoScene( string scene, IEnumerable transforms, bool deleteSceneIfEmpty );

You can use the following functions to create or delete a pseudo-scene manually:

public void CreatePseudoScene( string scene );
public void DeletePseudoScene( string scene );
public void DeleteAllPseudoScenes();

F.1.1. PseudoSceneSourceTransform

This helper component allows you to add an object's children to a pseudo-scene in the hierarchy. When a child is added to or removed from the object, this component refreshes the pseudo-scene automatically. If HideOnDisable is enabled, the object's children are removed from the pseudo-scene when the object is disabled.

F.2. DRAGGED REFERENCE ITEMS

In section E.2, it is mentioned that you can drag&drop objects from the hierarchy to the variables in the inspector to assign these objects to those variables. However, you are not limited with just hierarchy. There are two helper components that you can use to create dragged reference items for other objects:

  • DraggedReferenceSourceCamera: when attached to a camera, casts a ray to your scene at each mouse click and creates a dragged reference item if you hold on an object for a while. You can register to the ProcessRaycastHit delegate of this component to filter the objects than can create a dragged reference item. For example, if you want only objects with tag NPC to be able to create a dragged reference item, you can use the following function:
private Object CreateDraggedReferenceItemForNPCsOnly( RaycastHit hit )
{
    if( hit.collider.gameObject.CompareTag( "NPC" ) )
        return hit.collider.gameObject;

// Non-NPC objects can't create dragged reference items
return null;

}

  • DraggedReferenceSourceUI: when assigned to a UI element, that element can create a dragged reference item for its Reference object after it is clicked and held for a while

You can also use your own scripts to create dragged reference items by calling the following function in the RuntimeInspectorUtils class:

public static DraggedReferenceItem CreateDraggedReferenceItem( Object reference, PointerEventData draggingPointer, UISkin skin = null );

F.3. CUSTOM PROPERTY DRAWERS

NOTE: this section talks about the architecture of the Inspector drawers. However, the amount of information presented here might be overwhelming or boring; thus, you are also recommended to examine some of the built-in drawers. For starters, you can examine BoolField and TransformField. Then, you can progress to BoundsField, GameObjectField and ArrayField.

You can introduce your own property drawers to the inspector to extend its functionality using a Settings asset mentioned in section E.1. Each property drawer extends from InspectorField base class. There is also an ExpandableInspectorField abstract class that allows you to create an expandable/collapsable property drawer like arrays. Lastly, extending ObjectReferenceField class allows you to create drawers that can be assigned values via the reference picker or via drag&drop.

F.3.1. InspectorField

To have a standardized visual appearance across all the property drawers, there are some common variables for each drawer: - Layout Element: is used to set the height of the property drawer. A standard height is set by the currently active Inspector skin's Line Height property. This value is multiplied by the virtual HeightMultiplier property of the drawer. For ExpandableInspectorField's of unknown height, this variable should be left unassigned - Variable Name Text: the Text object that displays the name of the exposed variable - Variable Name Mask: to understand this one, you may have to examine a simple property drawer like BoolField. An Image is drawn on top of the Variable Name Text in order to mask its visible area in an efficient way. And this mask is assigned to this variable

Each property drawer has access to the following properties: - object Value: the most recent value of the variable that this drawer is bound to. It is refreshed at each refresh interval of the inspector. Changing this property will also change the bound object - RuntimeInspector Inspector: the RuntimeInspector that currently uses this drawer - UISkin Skin: the skin that is assigned to this drawer - Type BoundVariableType: the type of the bound object - int Depth: the depth that this drawer is drawn at. As Depth increases, a padding should be applied to the contents of this drawer from left - string Name: the name of the bound variable. When set, the variable name is converted to title case format, if Use Title Case Naming is enabled in the inspector - string NameRaw: When set, the variable name is used as is without being converted to title case format - float HeightMultiplier: affects the height of the drawer

There are some special functions on drawers that are invoked on certain circumstances: - void Initialize(): should be used instead of Awake/Start to initialize the drawer - bool SupportsType( Type type ): returns whether or not this property drawer can expose (supports) a certain type in the inspector - bool CanBindTo( Type type, MemberInfo variable ): returns whether or not this drawer can expose the provided variable. This function is called only if SupportsType returns true. This function is useful for drawers that can expose only variables with specific attribute(s) (e.g. NumberRangeField queries RangeAttribute). Please note that the variable parameter can be null. By default, this function returns true - void OnBound( MemberInfo variable ): called when the drawer is bound to a variable via reflection. Please note that the variable parameter can be null - void OnUnbound(): called when the drawer is unbound from the variable that it was bound to - void OnInspectorChanged(): called when the Inspector property of the drawer is changed - void OnSkinChanged(): called when the Skin property of the drawer is changed. Your custom drawers must adjust their UI elements' visual appearance here to comply with the assigned skin's standards - void OnDepthChanged(): called when the Depth property of the drawer is changed. Here, your custom drawers must add a padding to their content from left to comply with the nesting standard. This function is also called when the Skin changes - void Refresh(): called when the value of the bound object is refreshed. Drawers must refresh the values of their UI elements here

F.3.2. ExpandableInspectorField

Custom drawers that extend ExpandableInspectorField have access to the following properties: - bool IsExpanded: returns whether the drawer is expanded or collapsed. When set to true, the drawer is expanded and its content is drawn under it - int Length: the number of elements that this drawer aims to draw. If its value does not match the number of child drawers that this drawer has, the contents of the drawer are regenerated

ExpandableInspectorField has the following special functions: - void GenerateElements(): the sub-drawers of this drawer must be generated here - void ClearElements(): the sub-drawers of this drawer must be cleared here

Sub-drawers of an ExpandableInspectorField should be stored in the

protected List elements
variable as ExpandableInspectorField uses this list to compare the number of sub-drawers with the Length property. Also, when Refresh() is called, sub-drawers in this list are also refreshed automatically and when ClearElements() is called, sub-drawers in this list are cleared automatically.

You can create sub-drawers using the

RuntimeInspector.CreateDrawerForType( Type type, Transform drawerParent, int depth, bool drawObjectsAsFields = true )
function. If no property drawer is found that can expose this type, the function returns null. Here, for ExpandableInspectorField's, the drawerParent parameter should be set as the drawArea variable of the ExpandableInspectorField. If the drawObjectsAsFields parameter is set to true and if the type extends UnityEngine.Object, Reference Drawers are searched for a drawer that supports this type. Otherwise Standard Drawers are searched.

After creating sub-drawers, ExpandableInspectorField's must bind their sub-drawers to their corresponding variables manually. This is done via the following BindTo functions of the InspectorField class: -

BindTo( InspectorField parent, MemberInfo variable, string variableName = null )
: binds the object to a MemberInfo (it can be received via reflection). Here, parent parameter should be set to this ExpandableInspectorField. If variableName is set to null, its value is fetched directly from the MemberInfo parameter -
BindTo( Type variableType, string variableName, Getter getter, Setter setter, MemberInfo variable = null )
: this one allows you to define your own getter and setter functions for this sub-drawer. For example, ArrayField uses this function because there is no direct MemberInfo to access an element of an array. With this method, you can use custom functions instead of MemberInfo's to get/set the values of the bound objects (ArrayField uses Array.GetValue for its elements' getter function and Array.SetValue for its elements' setter function)

There are actually some helper functions in ExpandableInspectorField to easily create sub-drawers without having to call CreateDrawerForType or BindTo manually: -

InspectorField CreateDrawerForComponent( Component component, string variableName = null )
: creates a Standard Drawer for a component -
InspectorField CreateDrawerForVariable( MemberInfo variable, string variableName = null )
: creates a drawer for the variable that the MemberInfo stores -
InspectorField CreateDrawer( Type variableType, string variableName, Getter getter, Setter setter, bool drawObjectsAsFields = true )
: similar to the BindTo function with the Getter and Setter parameters, allows you to use custom functions to get and set the value of the object that the sub-drawer is bound to

If you don't want the name of the variable to be title case formatted, you can enter an empty string as the variableName parameter and then set the NameRaw property of the returned InspectorField object.

F.3.3. ObjectReferenceField

Property drawers that extend ObjectReferenceField class have access to the

void OnReferenceChanged( Object reference )
function that is called when the reference assigned to that drawer is changed.

F.3.4. Helper Classes

PointerEventListener: this is a simple helper component that invokes PointerDown event pressed, PointerUp event when released and PointerClick event when clicked

BoundInputField: most of the built-in drawers use this component for their input fields. This helper component allows you to validate the input of the input field when the input has changed and get notified when the input is submitted. It has the following properties and functions: - string DefaultEmptyValue: the default value that the input field will have when its input is empty. For example, NumberField sets this value to "0" - string Text: a property to refresh the current value of the input field. If the input field is currently focused and being edited, then this property will not change its text immediately but store the value in a variable so that it can be used when the input field is no longer focused. Also, setting this property will not invoke the OnValueChanged event - UISkin Skin: the skin that this input field uses. When set, input field will adjust its UI accordingly - OnValueChangedDelegate OnValueChanged: called while the value of input field is being edited (called at each change to the input). The OnValueChangedDelegate has the following signature:

bool OnValueChangedDelegate( BoundInputField source, string input )
. A function that is registered to this event should parse the input and return true if the input is valid, false otherwise - OnValueChangedDelegate OnValueSubmitted: called when user finishes editing the value of input field. Similar to OnValueChanged, a function that is registered to this event should parse the input and return true only if the input is valid - bool CacheTextOnValueChange: determines what will happen when user stops editing the input field while its contents are invalid (i.e. its background has turned red). If this variable is set to true, input field's text will revert to the latest value that returned true for OnValueChanged. Otherwise, the text will revert to the value input field had when it was focused

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.