Gamasutra: The Art & Business of Making Gamesspacer
arrowPress Releases
July 29, 2014
PR Newswire
View All





If you enjoy reading this site, you might also want to check out these UBM Tech sites:


Opinion: Extending The Unity3D Editor
Opinion: Extending The Unity3D Editor
August 26, 2011 | By Richard Fine

August 26, 2011 | By Richard Fine
Comments
    4 comments
More:



[In this reprinted #altdevblogaday-opinion piece, Binary Refinery director Richard Fine takes you through a tour of some useful info for customizing Unity3D's editor suite to meet your game's unique requirements.]

One of the handy things about Unity3D is that it's very easy to extend the editor suite. Every game has unique requirements for tooling, and being able to build those up quickly and in a fully integrated manner can make a world of difference to your development speed.

A number of pretty sophisticated packages exist that offer complex tools on top of the base Unity featureset, from visual script editors to in-editor navigation mesh generation. The documentation for how you might build such a thing yourself, however, is a bit thin. Here's a whirlwind tour of some of the most useful bits of info about editor customization that I've found in the course of my work.

How Editor Scripts Are Built

Because you don't want all your editor customization to be included in the game that you ship, and because you don't want your shipping game to have any dependencies on things in the Unity editor, Unity keeps runtime and editor code in separate assemblies.

The compilation order is such that runtime code is compiled before editor code, so that your editor classes can safely refer to runtime components (otherwise it'd be difficult to edit them) – but it does mean that your runtime components can't reference any of your editor code.

You have to maintain a strict layering. Unity's gotten a bit more explicit about this in 3.4 – now the project files it generates (for VS/MonoDevelop) clearly correspond to the four compilation stages it provides, so there's no confusion about which files will get build at which times.

The documentation is a little unclear on one particular point. When I first started, I thought that I had to have a single 'Editor' folder at the top of my project, and that all editor classes had to go inside it. The system's actually a bit more flexible than that; you can have as many 'Editor' folders as you want in your project, buried wherever you like inside your Assets folder, and all of them can contain editor code.

So now it's very common that I'll have a folder for one particular feature (e.g. 'AI') that contains all the components for that feature, with an Editor folder alongside (e.g. 'AI/Editor') that contains the editor extensions for working with those components.

As far as Unity's built-in types go, runtime types all live in the UnityEngine namespace (in the UnityEngine assembly), while editor types all live in the UnityEditor namespace (in the UnityEditor assembly).

The different projects generated by Unity 3.4. The assets folder in each project is the same physical folder on disk; each project includes different files within it. Note that this only shows the C# projects - if you're using UnityScript, you get another four.

The UnityEditor.Editor Class

By far the most common kind of customization I set up is a custom inspector. Unity's Inspector panel provides your window into a component's state, but in its base form it only understands a limited set of types, and will only expose public fields (no properties).

Custom inspectors give you the opportunity to completely control how users view and edit your components; for example, they let you display read-only properties, enforce value constraints, or just change the way an option is presented – for example, replacing a [0..1] float field with a percentage slider.

Inspectors in Unity are all subclasses of the Editor class, so that's where you start. One thing I don't like about the editor class, though, is the way it handles types: it has a 'target' member that refers to the object the inspector is editing, but it's of the base 'Object' type, so you keep on having to cast it to a more useful type. To get around this I use a very simple generic class:

public class InspectorBase : Editor where T : UnityEngine.Object
{
protected T Target { get { return (T) target; } }
}


Now, if I want to create an inspector for MyCustomComponent, I can derive the inspector from InspectorBase, and use the 'Target' member instead of the untyped 'target' member, and I don't have to keep casting everywhere.

Note that you also need to attach the [CustomEditor] attribute to your inspector classes for Unity to actually pick them up and use them.

Editor GUI

Once you've created your custom inspector, the method you usually want to implement is OnInspectorGUI(). OnInspectorGUI() is responsible for specifying everything shown in the inspector, using Unity's immediate-mode GUI system.

Because this is editor code, we can use the types in the UnityEditor namespace, which includes EditorGUILayout. EditorGUILayout provides a bunch of really simple controls for use in the editor, over and above what Unity's regular runtime GUI system offers. For example, say I want to show the user a field for entering a 3D position. I could use EditorGUILayout.Vector3Field():

Target.somePosition = EditorGUILayout.Vector3Field("Some position", Target.somePosition);

This results in a line in your inspector that looks like this:


The immediate-mode GUI system works such that if I change the values in the UI, Vector3Field will return the new values, and Target.somePosition gets updated. You're free to manipulate the value – for example, clamping it to a range – before assigning it to the target; and you're free to ignore the return value completely (which would effectively make the field read-only).

The values don't have to be coming to/from fields, either – you can expose properties in the inspector by adding lines for them in this way, or call a function to get the current value and another function to save it, whatever you like.

Of course, Unity does this stuff by default for any public members. If you just want to build on top of what Unity's already showing, you don't have to reimplement all those fields – Editor has a DrawDefaultInspector() method that tells Unity to draw all the controls it would usually draw, but when it's finished, you've still got the opportunity to add a few extra fields and buttons yourself.

Speaking of buttons… EditorGUILayout is pretty comprehensive, but you might notice that there are some things missing – what if I want to put a "recalculate" button on my navigation mesh component, for example? The trick is that EditorGUILayout is still built on top of the regular runtime GUILayout, so everything in GUILayout is available to you as well.

As you make changes to the fields in the inspector, and assign new values to the fields of your target object, Unity detects that you're changing the object and flags it as 'dirty' so that it will be written out to disk the next time you save the scene or project. This detection is limited: it only picks up direct assignment to public properties. If you're changing the target object through properties or through calling methods on it, you may need to call EditorUtility.SetDirty yourself.

Extending The Component Context Menu

It's often useful to be able to manually trigger certain behavior when testing things out. You could do this by putting a button on the custom inspector that triggers the behavior:

if(GUILayout.Button("Explode now!")) Target.ExplodeNow();

but there's an even easier way – one that doesn't require a custom inspector at all. What you can do instead is use the UnityEngine.ContextMenu attribute:

/* In the target class... */
[ContextMenu("Explode now!")]
public void ExplodeNow() { ... }


Right-clicking in the component's inspector – whether you've customized it or not – will then show you a context menu, with the extra item in there. Very handy for quickly rigging things up for testing.

Extending the main menus

Everything I've talked about so far has been about customization centered around a particular component. What about other kinds of extension, like general utilities?

The animation system in my game stores its assets in a folder structure, such that each folder corresponds to one entry in an enum. When I change the enum, it's useful to be able to synchronize that folder structure, adding any missing folders and deleting any obsolete ones. So I've got this class, with a simple method:

public class AnimationSystem{
public static void SyncFolderStructure() { ... }
}

But how and when do I call it? What I've done is to wire it up to a menu item in the Assets menu, using the MenuItem attribute:

[MenuItem("Assets/Sync folder structure")]
public static void SyncFolderStructure() { ... }


Clicking the menu item calls the function. Note that the function needs to be static – but the class it's in can be anything, and can derive from anything (including nothing).

Wizards

Editor GUI elements don't only have to live inside the Inspector. It's also possible to create arbitrary editor windows, that can be moved and docked like any of Unity's built-in windows, and that are populated using GUI commands just like in an inspector. One of the simplest ways of doing this is to use a ScriptableWizard, which is like a dialogue box – you display it, set some values in it, then hit a button to make it work its magic.


Unity's built-in Create Ragdoll wizard (GameObject->Create Other->Ragdoll). You drag and drop the relevant bones and bits into the slots in the window, and then hit the 'create' button to have it rig your ragdoll with rigidbodies and joints.

ScriptableWizard, by default, works almost like an inspector: any public fields in your derived class will be automatically shown in the wizard window. Your wizard might be as simple as a bunch of public fields, plus an OnWizardCreate() method, which Unity will call when the user hits the 'Create' button. Worth noting that you can change the text on that button, too, if 'Apply' or 'OK' or similar would be more intuitive.

The only other aspect of the wizard is deciding how the user will launch it; the usual approach is to use a menu item bound to a static function, as shown above:

[MenuItem("GameObject/Create Other/Explosion")]
public static void CreateExplosion()
{
ScriptableWizard.DisplayWizard("Create explosion");
}


Conclusion

There's loads more – I've not even touched on custom asset types yet, or asset import postprocessors, or scene view customization. They'll wait for another day…

[This piece was reprinted from #AltDevBlogADay, a shared blog initiative started by @mike_acton devoted to giving game developers of all disciplines a place to motivate each other to write regularly about their personal game development passions.]


Related Jobs

FitGoFun
FitGoFun — Mountain View, California, United States
[07.29.14]

Unity 3D Programmer
Treyarch / Activision
Treyarch / Activision — Santa Monica, California, United States
[07.29.14]

Cinematic Animator (temporary) - Treyarch
Treyarch / Activision
Treyarch / Activision — Santa Monica, California, United States
[07.29.14]

Associate Cinematic Animator (temporary) - Treyarch
Activision
Activision — Seattle, Washington, United States
[07.29.14]

Software Engineer - Activision Seattle










Comments


Joel Nystrom
profile image
I'm gonna force our programmers to read this first thing Monday morning =)

Luis Guimaraes
profile image
The best thing to do is improve automation in every handwork your team has to do. We use SpriteManager2 for our game in Unity, and needed tools to quickly select and place tiles, and to do some mass adjustments in our more than 150 levels.



Here a custom window (ScriptableWizard) I made to speed up that pipeline: http://pastebin.com/EGr6gAhN

And here the window print: http://twitpic.com/6c0glo



As any rushed work it's not the best code, but were of great help.

Martain Chandler
profile image
Shopping for game engines not fun. But thank the deity or deities of you choice that we have the option now.



One new question I've been asking myself is, "How is this going to hold up over 10 years?"



Need crystal ball.

Dev Jana
profile image
This may be the most valuable blog post I've ever read. Thank you! Thank you! Thank you!


none
 
Comment: