Featured Blog | This community-written post highlights the best of what the game industry has to offer. Read more like it on the Game Developer Blogs.
The right tools can save you a lot of time, and the best tool is the one that fits precisely your needs. Unity allows you to easily improve your editor through custom tools, and this post will introduce some of them.
The path editor for my game in progress
With Unity, you can save a lot of time by creating some tools adapted to your specific needs. Right above, a small path editor I made to save me some time when creating enemy patterns in my current project, Red Skies. With just a few click, I quickly design the general shape I want, then I can perfect it using the transform coordinates.
The Unity editor offers a LOT of tools to make yours, but I will just cover some basics in this post, then it will be up to you to look at the APIs to discover hidden gems.
WARNING: This post is ALL about code, with (commented) snippets rather than theory; that’s the way I like to learn, I hope I’m not alone!
Custom inspector
The most common tool you can use to make your life easier is custom inspector.
A very basic way to improve your inspectors is to use Property Attributes, which do not require to rewrite the entire inspector, but rather make it more readable:
So simple, yet so beautiful
using UnityEngine;
public class MyScript : MonoBehaviour { [Header("First category")] [Tooltip("Primary name for MyScript.")] public string scriptName; public float scriptFloat;
[Header("Second category")] [Tooltip("Secondary name for MyScript.")] public string secondaryName; } |
And then you can go pretty crazy with the custom inspectors themselves. Those scripts are editor-side only (they have to inherit from Editor and be placed in a folder called Editor) and let you write what you want, how you want, in the inspector when an instance of your script is selected. There is a ton of functions to experiment with, that can be found in various editor classes such as EditorGUILayout;, GUILayout;, EditorGUIUtility; and many others, ranging from simple to highly customizable.
I bet it could be of some use… maybe… for a weird project
using UnityEngine; using UnityEditor;
//This line will automatically call your custom editor for instances of MyScript [CustomEditor(typeof(MyScript))] public class MyScriptInspector : Editor { //Keep a reference on your style rather than regenerating them every update GUIStyle headerStyle;
//This custom inspector will generate random ids in order to display a list to // the user from which he can then choose bool choosingNewId = false; //Are options displayed? string[] newIds; //Random ids generated to choose from
public override void OnInspectorGUI() { //The member "target" allow you to grab a ref to the instance of your component MyScript myScriptTarget = (MyScript) target;
//Init styles if(headerStyle == null) { InitStyles(); }
EditorGUILayout.Space(); EditorGUILayout.LabelField("First Category", headerStyle); EditorGUILayout.LabelField("Script id: " + myScriptTarget.scriptName);
//If player has already clicked on the "Generate new name" button, // then we only display the generated options if(choosingNewId) { for(int i = 0; i < newIds.Length; i++) { if(GUILayout.Button(newIds[i])) { myScriptTarget.scriptName = newIds[i]; choosingNewId = false; } } } else if(GUILayout.Button("Generate new name")) { newIds = new string[] { Random.Range(0, int.MaxValue).ToString(), Random.Range(0, int.MaxValue).ToString(), Random.Range(0, int.MaxValue).ToString() }; choosingNewId = true; } }
void InitStyles() { headerStyle = new GUIStyle(); headerStyle.fontStyle = FontStyle.Bold; headerStyle.normal.textColor = Color.yellow; } } |
Note that I could have used a PopUp to display the choices in another way (there are many other solutions, I guess, but the EditorGUILayout.PopUp is pretty neat).
You just indicate the class for which you want to make your custom inspector, then the OnInspectorGUI is ready to make all your dreams come true. The EditorGUILayout and GUILayout classes are usually the more convenient to use, since you don’t have to handle the position and size of your elements, but you can use EditorGUI and GUI as well for very precise display (you could recreate, say, a color picker in the inspector by drawing a bunch of colored pixels and using the events of the mouse).
There are of course some limitations and tricky cases, but it’s up to you to find out about those according to your needs.
Editor windows
Did I speak about your custom color picker? Well why not, after all! Let’s make it in a small window, so that it doesn’t take all the space in the inspector. Editor windows use the same apis as custom inspector. To spice things up, we’ll allow the user to select the amount of color he wants to be able to choose from. (It’s not a course about color, so forgive me if my color theory isn’t perfect!)
A color picker, not the best idea for a gif… but I spent way too much time doing it to change it now
using UnityEngine; using UnityEditor;
public class DiscreteColorPicker : EditorWindow { //Create and open a discrete color picker [MenuItem("Window/Open Discrete color picker")] public static void OpenDiscreteColorPicker() { DiscreteColorPicker windowInstance = ScriptableObject.CreateInstance<DiscreteColorPicker>(); windowInstance.Init(); windowInstance.Show(); }
int division = 1; //Base value to calculate amount of available colors int colorAmount { //There are {pickerTextureHeight} base color to choose from, each generating // a grid of {luminosityTextureSize}*{luminosityTextureSize} tints, with // the last line being only black squares. get { return pickerTextureHeight * luminosityTextureSize * luminosityTextureSize - luminosityTextureSize + 1; } }
Color currentBaseColor; //Current selected base color
Vector2 currentLuminosityCoordinates; //Coordinates of selected tint in luminosity texture float currentMainColorY = -1f; //Coordinate of selected base color
Texture2D pickerTexture; //Texture containing the base colors Texture2D luminosityTexture; //Texture based on the current base color with luminosity variations Texture2D cursorTexture; //Texture used to display cursors Texture2D sampleColorTexture; //Texture containing final color
int pickerTextureHeight { get { return division * 6; } } int luminosityTextureSize { get { return division * 3; } }
int pickerSize = 300; //GUI object size
bool draggingLuminosity; //Is user dragging from luminosity square bool draggingColor; //Is user dragging from color picker
void OnGUI() { EditorGUILayout.LabelField("Discrete color");
EditorGUI.indentLevel++;
//First, a slider to decide the amount of colors bool needsUpdate = false; int newDivision = EditorGUILayout.IntSlider("Color levels:", division, 1, 30, GUILayout.MaxWidth(pickerSize)); if(newDivision != division) { //If division changed, update luminosity coordinates to adapt to new grid currentLuminosityCoordinates *= ((float) newDivision) / ((float) division); division = newDivision;
//Then regenerate textures, since their size has changed RegenerateTextures(); needsUpdate = true; } EditorGUILayout.LabelField("=> " + colorAmount + " colors"); GUILayout.Space(10);
//Reserve a rect in the windows, where we can draw with GUI functions, and that // GUILayout functions will consider as already filled |