
Content
- Part 0. The list of GUI elements used in the articles.
- Part 1. Attributes
- Part 2. Windows
- Part 3. Class editor, heir from MonoBehavior or ScriptableObject
- Part 4. Class editor, custom editor for a variable to be serialized.
Foreword
Hello, friends! I noticed that many programmers skip the rich possibilities for Unity to customize the editorial interface for one reason or another. In this series of articles, I will write out a few very simple examples that allow game designers and artists to make life easier, as well as a couple of more complicated examples, but also easily implemented.
The most part is taken from the experience of use, where it came from the native documentation of the engine. You can easily find the information you need by flipping the Unity 3D documentation. I simply say from my own experience that many programmers either have no time or no desire to delve into mantras. Therefore, I am spreading the briefest guide on the main editorial features that I used at work and in my projects.
Inline Attributes
I will not paint all the attributes, I will sign only briefly those that I had to use myself.
Attributes to methods
Menu itemUnity - Scripting API: MenuItem[MenuItem("Tools/Initialization Project")]
Allows you to create a menu to access the static method. The “/” indicates the hierarchy. You can place new buttons in the standard main menu of the engine, indicating the path, for example, “File / Create New Asset”.
')
It can contain three parameters.
string path
[MenuItem("Tools/Initialization Project", true)] public static bool ValidateInitialization() {
[MenuItem("Tools/Initialization Project")] public static void Initialization() {
Also, if you use the main menu items, then an additional button will appear not only there, but also in the context menu of the right mouse button. For example, in my project, I added copying the path to an asset.
In addition, you can assign hot keys to methods. For this, right on the way to the menu, you need to write the necessary combination. To do this, use one of the service characters + letter.
% - ctrl on Windows or cmd on OSX
# - shift
& - alt
In my project, with copying the path to an asset, it looks like this
[MenuItem("Assets/Copy Path %&c")] private static void CopyAssetPath() { }
Context menu itemUnity - Scripting API: ContextMenu public class UnitController : MonoBehavior { [SerializeField] private new Transform transform = null;
Attributes to variables
Sample Signature, Tips, and Clamper Limit the input valueUnity - Scripting API: RangeAttribute [Range(float min, float max)]
You can say, this is a custom editor for an attribute that allows you to set the boundaries of the specified value through the inspector. Not klampit in realtime - only in the inspector. Useful if you specify, for example, the probability of falling out of objects from 0 to 1 or from 0 to 100.
SignatureUnity - Scripting API: HeaderAttribute [Header(string title)]
Specifies the caption over the field to be serialized, which is displayed in the inspector.
IndentUnity - Scripting API: SpaceAttribute [Space]
Sets the indent in the inspector.
TooltipUnity - Scripting API: TooltipAttribute [Tooltip(string tip)]
Sets a hint to the inspector when hovering over a variable to be serialized.
Variable serializationUnity - Scripting API: SerializeField [SerializeField]
Allows serialization of variables regardless of their scope. A very useful attribute that allows you to make all class variables private, but customizable in the inspector.
Disable serializationUnity - Scripting API: NonSerializable [NonSerializable]
Allows you to remove the serialization of public variables. I highly recommend the data approach. It is better to define the property get; set; and get data on it. In addition, the property can be made virtual and reloaded, if necessary, in the classes of heirs. And the fact that it is public allows it to be used in interfaces.
Hiding a variable in the inspectorUnity - Scripting API: HideInInspector [HiddenInInspector]
Allows you to hide the serializable field in the inspector. It doesn't matter if it is public or private / protected with the SerializeField attribute.
Class Attributes
Creating a child instance from ScriptableObjectUnity - Scripting API: CreateAssetMenuAttribute [CreateAssetMenu(menuName = "Entity/Weapon")]
ScriptableObject is a very useful class that allows you to store a conditional database in a project without resorting to prefabs. The project creates objects with the type you created. You can also work as with prefabs, have their advantages and disadvantages. In general, this class is a topic for a separate article, small but informative.
The above attribute allows you to create an object with your type in the project, along the path where you opened the context menu.
Execution in the editorUnity - Scripting API: ExecuteInEditMode [ExecuteInEditMode]
Allows the script to work in the editor. It is mainly useful for post-effects, because it allows you to immediately evaluate the result in the camera without starting the project. But sometimes you can use for other purposes.
For example, as an initializer for serializable fields of built-in types, such as transform, renderer, rectTranform, etc. I would not recommend it everywhere, it is better to require manual initialization, or to write an editorial script, but sometimes it is convenient.
The need for another componentUnity - Scripting API: RequireComponent [RequireComponent(System.Type type)]
Causes the editor to require the presence of a specific component on the same object on which the script with this attribute hangs. When added, it immediately creates components of the specified type on the same object. Also prohibits deleting an already added component.
New item in the add component menuUnity - Scripting API: AddComponentMenu [AddComponentMenu(string path)]
Adds a submenu to the drop-down list in the Components → ... and AddComponent menu. Conveniently, if you have a large library of code and need to organize it in the editor.
At this, the simple part ends and add quite a bit of moderately complex.
Custom Attributes (CustomPropertyDrawer)
Unity - Scripting API: PropertyAttributeUnity - Scripting API: PropertyDrawerIf the attributes listed above are not enough for you, you can always use the API to write your own custom attributes. The implementation of this tool is also quite simple and consists of several steps. In this example, I will describe the creation
own attribute to a variable.
First, you need to define a descendant class from the standard PropertyAttribute class. I will immediately create it with a constructor, in which the input parameter will be the path to the list of what we need to use in the attribute.
public class IntAttribute : PropertyAttribute { private string path = “”; public IntAttribute(string path) { this.path = path; } }
Secondly, after that we create an editor script in which we will draw this newest class. You need to inherit it from PropertyDrawer, and also write the CustomPropertyDrawer attribute to it.
[CustomPropertyDrawer(typeof(IntAttribute ))] public class IntAttributeDrawer : PropertyDrawer { }
I call classes the most common names in order to simply show how to use customizable ones.
The base is ready, now we need to draw this attribute in the form in which we need it. Basically, I use attributes in cases where enumeration capabilities are not enough, but you need to draw a drop-down list with a choice.
For example, you have an effects database that has an id → effect match. You store this base somewhere, no matter in a ScriptableObject or on some kind of prefab. Here is the simplest implementation of the “repository”
Note - always create the first serializable field in classes in strings. Because of this, in the lists, elements will not be referred to as element 1, element 2 .., but in the manner in which you assign a variable in the inspector.Code
For classes that I interact with “from the outside,” I always write the interface. Everyone has his own approach to this moment, but this approach will easily allow, in which case, to replace the class only in one place with another, and the rest will work with the interface. Moreover, the unit supports working with interfaces in such methods as GetComponent (s) ..., GetComponent (s) InChildren, etc.
Interface and effect class public interface IResource { int ID { get; } string Name { get; } } [System.Serializable] public class Effect : IResource { [SerializeField] private string name = “”; [SerializeField] private int id = 0; public int ID { get { return id; } } public string Name { get { return name; } } }
Interface and container class public interface IContainer { IResource[] Resources { get; } } public abstract class ResourcesContainer : MonoBehaviour, IContainer { public virtual IResource[] Resources { get { return null; } } } public class EffectsContainer : ResourcesContainer { [SerializeField] private Effect[] effects = null; public override IResource[] Resources { get { return effects; } } }
Usually, I place objects with such data in resources, then take them from there. You can locate and just in the project and where you need to define links. But I am going along a simpler path that has already been tested on more than one platform.
EditorIt remains to add the editor:
[CustomPropertyDrawer(typeof(IntAttribute ))] public class IntAttributeDrawer : PropertyDrawer { protected string[] values = null; protected List<int> idents = null; protected virtual void Init(SerializedProperty property) { if (attribute != null) { IntAttribute intAttribute = (IntAttribute)attribute;
Screenshot with attribute Conclusion
All the above lifehacks can simplify not only your work (especially when the project is developed for several months or years), but also the work of beginners, as well as artists and game designers. Not every specialist will get into the code. Of course, good organization and discipline can help and document each component in this way, but this is not always the case, especially with independent developers.
PS: Later, I will write a couple more articles on other types of editor upgrades, including:
CustomEditor;
CustomPropertyDrawer;
EditorWindow;
Class debug and how to eat it;
Class Gizmos.
And also I will add examples with a window and the user editor. Write in the comments if you need such articles or you can get by with what is already on Habré.