Skip to content

Editor Scripts

An Editor Script is a C# script that runs inside the Unity Editor, these scripts can be done to basically extend the functionality of Unity's powerful editor. An Editor script can be useful to automate an annoying part of development or improve the user experience of complex Components and Scriptable Objects.

One such example of an Editor Script is the custom Inspector of EntityStateConfiguration that comes bundled with RoR2EditorKit. Thanks to this inspector, utilizing Entity State Configurations is easy and convenient, without having to manually add the fields to serialize or guess the proper string formatting for serialization.

(Left is the default look of the EntityStateConfiguration, right is how it looks with RoR2EditorKit's inspector)

image

Glossary

Term Definition
Editor Script A regular C# script that contains editor related code. These are usually bundled with a separate Editor only assembly.
SerializedObject a SerializedObject is a special type of class that represents a UnityEngine.Object in a serialized format. Editors and multiple editing related scripts utilizes them since it allows to represent virtually any UnityEngine.Object in a generic way.
SerializedProperty A SerializedProperty is a property within a SerializedObject that contains the actual data of an Object, for example, a class with a public string field named "myString" has a SerializedProperty of name "myString" that contains the actual stored value. Serialized properties can have nested properties, such as in the case of a custom Serializable data structure.
Editor An "Editor" is a custom class that can be inherited to create custom Inspector for objects. These objects are edited utilizing their SerializedObject representations.
PropertyDrawer A custom class that Draws a GUI for a type of SerializedProperty.
Editor Window An actual, dockable and movable window that can be used inside the editor, these windows can have custom scripts related to them.
UIElements UIElements are a custom, special class inside unity thats used to represent user interfaces, these elements in modern versions of unity are mostly used to replace the old UI systems of unity. Its built in a very similar structure to Web Development, where UIElements can be queried utilizing the UQuery system. (CSS, JS, HTML)
UXML An UXML file is a file that represents the hierarchy and layout of different UIElements. Its quite similar to HTML in web development.
USS A USS file is a file that represents the visual style of UIElements inside their hierarchy and layout. Its quite similar to CSS in web development.
IMGUI The traditional way of writing editor UI is via IMGUI, which means basically programming your inspectors and UI. Most editors and inspectors in unity still utilize IMGUI.

Creating an Editor Assembly

By default, any script that's within an "Editor" folder is considered an Editor Script, and will be added to a special assembly called "Assembly-CSharp-Editor". We recommend instead of utilizing the default editor assembly to create a custom Assembly Definition. More information about Assembly Definitions can be found here

To create a new Assembly Definition, right click in the project window and select Create/AssemblyDefinition

image

To properly configure your Assembly Definition to be used within the editor, we need to select ONLY the "Editor" platform under the AssemblyDefinition's Platforms section. Failing to do so can and will cause issues when building things such as Scripts and AssetBundles. An easy way to do this is by: 1. Untick Any Platform 2. Click Deselect All 3. Tick Editor

image

My first inspector

We will begin by creating a custom editor for a component. this inspector will have a button that does something when clicked. for simplicity sake we will utilize IMGUI to create our UI. We begin by creating a new script inside our editor assembly, instead of making it inherit from MonoBehaviour we will make it inherit from UnityEditor.Editor

image

The editor class has a main method we can override called OnInspectorGUI, the base method of this method draws the default inspector, so for our purposes we will keep the base call. To add our new button, we can utilize the class GUILayout's method Button. The method returns a bool that determines if the button was clicked or not, so we can just use an if statement to run code when the button is clicked.

The class EditorUtility contains a bunch of utility methods within the editor, we will use this to create a popup dialog that informs the user they clicked the button.

Finally, we need to tell unity what object this editor is for, we utilize the CustomEditorattribute for specifying what object this editor is for.

image

Now if you inspect the specified type, you can see that its inspector window has a new button, and clicking it will display our custom dialog.

image

(Note, you can draw the default inspector at any point in time by utilizing DrawDefaultInspector())

Useful Classes

Class Type Description
AssetDatabase The AssetDatabase represents a database of all the assets within the editor, this can be used to load specific assets, manage them, save in memory objects to assets, etc
Editor The base class for any and all inspector related classes, the Editor class can be used to write custom inspectors for objects.
EditorApplication A static class that represents the UnityEditor.exe instance currently open.
EditorGUI Class used for writing GUI code for the editor, it utilizes Serialzied Properties and Serialized Objects, the GUI written in here does not layout automatically, for automatic layout, use the class below
EditorGUILayout A class used for writing GUI code for the editor, which automatically layouts to simplify writing complex windows and inspectors.
EditorGUIUtility A general utility class that contains various utility methods for the unity editor's GUI system
EditorUtility A general utility class that contains various utility methods for general editor usage.
EditorWindow The base class for any and all editor windows.
FileUtil Class used to manipulate directories and files, which can be used to copy, move, delete assets and directories.
InitializeOnLoadMethodAttribute An Attribute that can be applied to static methods, methods with this attribute will be executed every time a domain reload occurs in the editor. Useful for setting up underlying systems
MonoScript Representation of a .cs file within the unity editor
ObjectNames Class used to create displayable names in the editor
PrefabUtility Class that contains utility methods for handling and managing prefab assets
PropertyDrawer A custom class that Draws a GUI for a type of SerializedProperty.
Selection A class that can be used to get the selected object or assets in the editor.
SerializedObject a SerializedObject is a special type of class that represents a UnityEngine.Object in a serialized format. Editors and multiple editing related scripts utilizes them since it allows to represent virtually any UnityEngine.Object in a generic way.
SerializedProperty A SerializedProperty is a property within a SerializedObject that contains the actual data of an Object, for example, a class with a public string field named "myString" has a SerializedProperty of name "myString" that contains the actual stored value. Serialized properties can have nested properties, such as in the case of a custom Serializable data structure.
Undo Access to the Undo/Redo system

Developing editor scripts with RoR2EditorKit

RoR2EditorKit is a powerful package maintained by the community that contains a robust API and utilities for helping the development of Editor related scripts. Thanks to its 5.0.0 update, RoR2EditorKit's core assembly which contains its API and main functionality does not require RoR2 to be within the editor nor ThunderKit to be installed, as such, it can be used in virtually any scenario to help your development of editor scripts. We will use this to re-create our previous inspector utilizing UIElements.

R2EKIcon

Installation

  1. Go to Window/Package Manager to open the Unity Package Manager

image

  1. Go to the RoR2EditorKit Repository. Taking a look at the website you can see a big green <> Code button. Clicking it and then clicking the copy url button will copy the link to the repository.

image

  1. On the Package Manager window, hit the + button, and click the Add package from Git URL... option

image

  1. Paste the git URL copied beforehand. As a side note, it is recommeneded to install only the latest tagged release from the repository. To install a specific release tag, append the Tag's name preceded by a # character.

For example, to download the version 5.4.0 of RoR2EditorKit to your project, you must use the git url https://github.com/risk-of-thunder/RoR2EditorKit.git#5.4.0

image

Creating an inspector using UIElements

To create an inspector using UIElements we recommend utilizing the editor tool UIBuilder. Access to the UIBuilder can be found by clicking "Window/UI Toolkit/UI Builder". The manual entry for UIBuilder can be found here

image

Note that by default, UIBuilder only exposes Runtime based controls, to get access to all controls for editor usage, click the main UXML file in the hierarchy and tick "Editor Extension Authoring". You can also enable it by default by going to the UIToolkit Project Settings

image image

We can create the UI of our inspector by simply utilizing the "PropertyField" element, this element will draw the correct control for a field with the given name.

image

Something worth mentioning is that a lot of UIElements have a property called "Binding Path", the Binding Path is used in the editor to bind the values and states of a Serialized Property to a specific UIElement, to bind an element to a property, set "BindingPath" to the field's name.

image

For the button we can simply drag and drop the Button element into our UXML file.

image

You may notice that all UIElements have a section called "Style", the "Style" of the UIElement is used to modify how an element looks and well, its style. These styles can be inlined, or written as actual style classes stored inside a USS file.

image

You can now save the UXML file, which will be how our inspector is laid out and how it looks. The name of the UXML file should match the name of the inspector class youre going to create.

image

Now, we can start giving our UI a proper behaviour and have it appear as the UI of our custom inspector, while you can make your inspector inherit from Editorand override CreateInspectorGUI, unity by default does not provide any tooling for obtaining an inspector's UXML file and instantiating it. For this case, we will levrage the custom inspector setup in RoR2EditorKit. to differentiate the UnityEditor.Editor class from the custom subclasses in RoR2EditorKit, RoR2EditorKit calls them Inspector. For our specific usage which is for creating an inspector for a ScriptableObject, we will inherit from VisualElementScriptableObjectInspector

image

Inside the class we can see we're required to implement the InitializeVisualElement method, RoR2EditorKit calls this method after creating an instance of your UXML file and assigning it to your inspected object, and as such, templateInstanceRoot represents the actual root of your UXML document. We can utilize the extension method .Q to quickly query and obtain a Visual Element that matches the given filters. In the following code, the Query statement tells unity to "Find a UIElement of type 'Button' that has the name 'MyButton' and return it".

The Button element has a clicked event that we can use to run code when the button is clicked.

Lastly, we need to implement an override to the ValidatePath method, since there can be situations where multiple UXML files have the same name, RoR2EditorKit uses this method to check if the path to an UXML file is the correct path to its inspector. The default behaviour is that it only checks if the path contains the package's name. For our use case, we can just do the same but replace the package name with a directory that's parent of our UXML file.

image

The resulting UI gives an inspector that separates the layout and look of the UI and it's functionality, And still works as the IMGUI inspector.

image

For an indepth explanation of the utilities provided by RoR2EditorKit, check this wiki page.

Utilizing editor code within Runtime code

You may stumble into the necesity of running Editor related code in a regular, runtime level MonoBehaviour or ScriptableObject. This scenario can be solved by utilizing conditional compilation using pre-processor directives.

Take for example, this OnValidate method that needs to save the asset after its been modified by some user.

private void OnValidate()
{
    if(someCondition)
    {
        _hidenInInspectorValue = 0.4f;
        //Find a way to save the above change somehow
    }
}

To ensure proper saving of our modified asset, we can utilize EditorUtility.SetDirty. To properly utilize this method without causing issues at build time we can encapsulate the statement inside an #if UNITY_EDITOR pre-processor, and fully qualify the type name EditorUtility

private void OnValidate()
{
    if(someCondition)
    {
        _hidenInInspectorValue = 0.4f;
#if UNITY_EDITOR
        UnityEditor.EditorUtility.SetDirty(this);
#endif
    }
}

Of course, it is worth mentioning that any code encapsulated by the #if UNITY_EDITOR statement will not be present in builds of your content.

IMGUI UI in Visual Elements

Sometimes you may encounter the need to utilize IMGUI inside an UI created with Visual Elements. This can be due to some limitation of UIElements that hasnt been developed yet, or for ease of utilization of certain features such as dropdown menus.

For situations like this, you can utilize the IMGUIContainer element, which is a special VisualElement that can be used to draw UI using IMGUI.

public void SetupElement(VisualElement rootElement)
{
    rootElement.Add(new IMGUIContainer(MyIMGUIMethod);
}

private void MyIMGUIMethod()
{
    EditorGUILayout.LabelField("This label was written using IMGUI!");
}

Due to limitations, you cannot use Visual Elements in an IMGUI UI

Back to top