PEGI (Player & Editor GUI) | Unity | Custom Inspectors

 

Unity's inspectors are pretty good at showing you the state of your MonoBehaviours and ScriptableObjects. But to allow your scripts to be used by others it is often better to provide a user-friendly interface for your fellow co-workers... sure we can also write a documentation for our classes and keep it updated as things get changed, but it's objectively more preferable to instead blow one's own brain out. Unity lets you make your own interfaces. And as I was making them I would often create a wrapper functions to make code shorter. Time went by and I kept adding/removing and changing those wrappers. Hundreds of custom inspectors later the PEGI class is now a big helper.


 

To demonstrate I'll compare the code I'd need to write before with the one I write now to achieve the same result (A slider that calls a recalculate when changed, and a button with icon right next to it that sets a default value).

 

 

 

Without PEGI wrapper (Standard Unity GUILayout):

 

var controller = (MyMaterialController )target;

 

EditorGUILayout.BeginHorizontal();

EditorGUI.BeginChangeCheck();

 

controller .transparency = EditorGUILayout.IntField("Object's Transparency:", controller .transparency);

 

if (EditorGUI.EndChangeCheck())

            controller .RecalculateShaderParameters(); 

 

  GUIContent cont = new GUIContent
            {
                tooltip = "Will load default value",
                image = refreshIcon
            };
if (controller .transparency != 0.5 && GUILayout.Button(cont,  GUILayout.MaxWidth(25), GUILayout.MaxHeight(25)))

             controller .transparency = 0.5;

 

EditorGUILayout.EndHorizontal();

 

 

 

With PEGI:

 

if ("Object's  Transparency:".edit(ref transparency))

    RecalculateShaderParameters();

if (transparency != 0.5f && icon.Refresh.Click("Will load default value",25).nl())

transparency = 0.5f;

 

 

 

* The PEGI function is usually inside of your class, that is why target. part is not needed. They can access private & static members, and game can build with them.

 

* nl(); or pegi.nl(); means New Line. Before PEGI class, I would often want to hide a parts of inspector when they are not needed, and as a result have a BeginHorizontal without a matching "End", which would throw tons of warnings. Now everything is in one line by default, and only when I call nl() the EndHorizontal is called.   

To replace Unity Inspectors with Custom inspectors create a folder named Editor anywhere in the Assets, make a new script there, and just put the code snippets similar to the ones below, one for each class that you want to replace:

 

        using PlayerAndEditorGUI;
        using UnityEditor;

 

 


        [CustomEditor(typeof(YourCoolClass))]
        public class YourCoolClassEditor : Editor {
            public override void OnInspectorGUI() =>
                ((YourCoolClass)target).Inspect(serializedObject);

        }

 

 

 

        [CustomEditor(typeof(YourCoolClass2BlaBlaBla))]
        public class YourCoolClass2ThisNameCanBeWhateverEditor : Editor {
            public override void OnInspectorGUI() =>
                ((YourCoolClass2BlaBlaBla)target).Inspect(serializedObject);

        }

Usually I just do it for a couple of manager classes because you can inspect any class from inspector of any other class. For example, pegi's edit_List will let you inspect any of it's elements if that element has a PEGI function (implements an IPEGI interface).

 

Custom List Inspector

 

"Image Datas:".edit_List(imageDatas);

 

* It lets you reorder elements (right image);

*Inspected element's class can have PEGI_inList function which will tell how it looks in list inspector (first image), For example: a tick-box to enable/disable it. 

* And in this way I can inspect non-serialized classes which are not Unity Objects.

 

 Another thing that have proven extremely useful is the iNeedAttention interface. Which when implemented will cause a list to show a warning sign when NeedAttention() returns a not empty string and the string is the message that will show when hovering over the warning icon. 

 

I notify myself when reference is not set, list not filled up, basically any mistake that can be detected automatically is highlighted in this way so I don't have to do a same mistake twice.   

 

Example:

 

Scripts come with more then enough examples (looots of examples actually, there are tons of other classes in my Tools GitHub repository that use PEGI), but here is an another small example:

 

class YourCoolClass : PEGI {

 

private int textureSize;

Texture2D texture;

 

public bool PEGI (){

bool changes = false;

changes |= "Resolution:".edit(ref textureSize).nl();

if (textureSize <= 8)

"Texture is too small".writeHint();

else if (textureSize > 4096)

"Texture is too big".writeHint();

else if ("Create new texture".Click()){

texture = new Texture2D(textureSize,textureSize);

"Texture Created".showNotification();

}

}

}

 

The benefits:

 

* It can be seamlessly added into existing project (Something I actually did as part of my work).

* The main things which got my co-workers like my inspectors is DropDown lists and nested inspection. If you ever felt like you are doing to many drag and drop operation to set relations and assign Unity Objects to each other, imagine as if every time you'd be presented with a dropdown that contains the option you set up somewhere else. And with just "Face Texture".select(ref faceTexture, listOfAllFaceTextures).nl(); your life became much easier. 

* With Nested Inspection you will not need to click on a reference to highlight an object, then click on the object to see it's inspector and then look for the object you were inspecting before. In one object you can unfold and inspect another object. Keep in mind that most of this mentions are not part of the PEGI system, it's something you can add or don't add, also hide or show depending.  

* This is so much better then writing an instruction, and plus you are letting users control the private and static fields via inspector that directly relates to it. I even used it to control Unity's render settings such as fog strength and sky colors, also Main Camera's background color, all from a single inspector.   

* No need to create any additional files every time you need an inspector. Just a function inside a class and iPEGI interface next to class name. 

* I don't need to worry about mismatching BeginHorizontal() / EndHorizontal() functions, which would often happen as I want to hide and show some buttons under different conditions. For example, I can hide the "Create new" button if name for a new object is too short or is already included in the list. 

* Every function returns true if value was modified. Unity provides "BeginChangeCheck/EndChangeCheck" to see if values within the block were changed, but I found myself writing less code by having a function return true if value was changed. 

* Inspecting Non-Serialized classes, private, static stuff.

* And finally, I wanted to use the same code to draw class in inspector, and in build (screenshot of what I'm talking about is somewhere on this page). You can see that painter tool uses the same function to draw itself in-game and in inspector. So basically as a bonus you get an inspector in your build - it allowed me to debug my projects very easily. 

* In addition to scripts there are also a folder with icons, which I found myself reusing very often. To add your icons, drop them in the folder and add it's name to icon enum.

 

The PEGI scripts are at my public GitHub

 

Maybe it's because I was using them so much that they seem easy to me, but using this class is a difference between doing a tedious job I never want to do again, and something so easy that it feels like a win every time I make an inspector and other people's life easier with it. And it's always a straight-forward process, something I don't need to think too much about. Join the Discord of you want to try and use it. Seeing other people using it would help me improve it's usability and intuitiveness.  

 

 

Please reload

Geeky:
GitHub icon
unity-Connet-Logo.png
Discord icon
Social:
Facebook icon
unnamed.png
Instagram icon