Home

Awesome

PiRhoUtilities

A suite of UIElements based attributes, drawers, controls, and other helpers to expedite the creation of inspectors without the need of additional custom editors.

Installation

Updating

Known Issues

Usage

Property Drawer Attributes

These attributes are added to serializable fields in editable classes (MonoBehaviours or ScriptableObjects) to enable custom drawing for the field. Most attributes can have their properties retrieved from another field, property, or method through reflection by specifying the associated string name. All attributes are in the PiRhoSoft.Utilities namespace.

List

Add this to a SerializedList or SerializedArray derived type to show a much more user friendly list editor than Unity's default with custom callbacks and constraints for adding, removing, and reordering. Any additional PropertyTraitAttributes will be applied to each item in the list individually.

[Serializable] public class TestList : SerializedList<int> { }
[Serializable] public class TestArray : SerializedArray<float> { public TestArray(int count) : base(count) { } }

[List(AllowAdd = nameof(ListCanAdd))]
public TestList List;

[List(AllowAdd = ListAttribute.Never, AllowRemove = ListAttribute.Never, AllowReorder = false)]
public TestArray Array = new TestArray(4);

private bool ListCanAdd()
{
	return List.Count < 5;
}
PropertyDescriptionDefault
EmptyLabelWhen the list is empty display this text - use an empty string to hide the label - null will show the default text, "The list is empty"null
AllowAddThe string name of a bool returning method (parameterless), field, or property, that enables/disables adding items to the list. If adding is not conditional, use ListAttribute.Always or ListAttribute.NeverAlways
AllowRemoveThe string name of a bool returning method (parameterless) or (int index), field, or property, that enables/disables removing items from the list. If removing is not conditional, use ListAttribute.Always or ListAttribute.NeverAlways
AllowReorderEnables/disables reording items in the list.true
IsCollapsableWhether or not the List is collapsible to save spacetrue
AddCallbackThe string name of a method (parameterless) to call when an item has been added to the listnull
RemoveCallbackThe string name of a method (parameterless) or (int index) to call when an item is about to be removed from the listnull
ReorderCallbackThe string name of a method (parameterless) or (int to) or (int from, int to) to call when an item has been reordered in the listnull
ChangeCallbackThe string name of a method (parameterless) to call when the list changesnull

List

Dictionary

Add this to a SerializedDictionary derived type to show a dictionary editor (by default dictionary editing is unsupported by Unity). This has similar features/functionality as ListAttribute.

[Serializable] public class TestDictionary : SerializedDictionary<string, string> { }

[Dictionary(AddCallback = nameof(DictionaryItemAdded), RemoveCallback = nameof(DictionaryItemRemoved))]
public TestDictionary Dictionary;

private void DictionaryItemAdded(string key)
{
	Debug.Log($"'{key}' added", this);
}

private void DictionaryItemRemoved(string key)
{
	Debug.Log($"'{key}' removed", this);
}
PropertyDescriptionDefault
EmptyLabelWhen the dictionary is empty display this text - use an empty string to hide the label - null will show the default text, "The dictionary is empty"null
AddPlaceholderThe string to display in the add areanull
AllowAddThe string name of a bool returning method (parameterless) or (string key), field, or property, that enables/disables adding items to the dictionary. If adding is not conditional, use DictionaryAttribute.Always or DictionaryAttribute.NeverAlways
AllowRemoveThe string name of a bool returning method (parameterless) or (string key), field, or property, that enables/disables removing items from the dictionary. If removing is not conditional, use DictionaryAttribute.Always or DictionaryAttribute.NeverAlways
AllowReorderEnables/disables reording items in the list.true
IsCollapsableWhether or not the List is collapsible to save spacetrue
AddCallbackThe string name of a method (parameterless) or (string key) to call when an item has been added to the dictionarynull
RemoveCallbackThe string name of a method (parameterless) or (string key) to call when an item is about to be removed from the dictionarynull
ReorderCallbackThe string name of a method (parameterless) or (string key) to call when an item has been reordered in the dictionarynull

NOTE: Normally dictionaries don't have defined ordering, however, serializing dictionaries requires the key/value pairs to be stored in lists and are thus ordered.

Dictionary

ComboBox

Add this to a string field to display a ComboBox control in which you can select from dropdown list of values or enter your own custom value.

[ComboBox(new string[] { "One Fish", "Two Fish", "Red Fish", "Blue Fish" })]
public string ComboBox;
PropertyDescriptionDefault
OptionsA predefined list of options to show in the dropdownnull
OptionsSourceThe string name of a List<string> returning method (parameterless), field, or property, that defines the list of optionsnull
AutoUpdateWhether the options should automatically update if the values change (may affect performance)true

Combo Box

EnumButtons

Apply to an Enum field to show selectable buttons instead of a dropdown list. An optional bool can be specified to override the default behavior of the enum being selectable as flags or not (the default is based on whether the [Flags] attribute is set on the declared enum type).

public enum TestEnum
{
	Zero = 0x0,
	One = 0x1,
	Two = 0x2,
	Four = 0x4,
	Eight = 0x8
}

[EnumButtons]
public TestEnum Buttons;

[EnumButtons(true)]
public TestEnum Flags;
ParameterDescriptionDefault
FlagsWhether multiple options can be selected as a flags enumtrue if enum class has the Flags attribute, false otherwise

Enum Buttons

Euler

A simple attribute to apply to a Quaternion field to display and assign the Quaternion in Euler angles (like the default Transform inspector does).

[Euler]
public Quaternion Euler;

Frame

Add this to a class or struct type with a Serializable attribute to show the fields of the class in a collapsible frame style which looks much better than Unity's default.

[Serializable]
public class Subclass
{
	public bool Bool;
	public int Int;
	public float Float;
	public string String;
}

[Frame]
public Subclass Frame;
ParameterDescriptionDefault
IsCollapsableWhether the frame can be collapsed or nottrue

Frame

Group

Add this attribute to any number of fields to display them together as a group with the header label specified in the constructor despite being defined out of order in the class.

[Group("Group One")] public int Int1;
[Group("Group One")] public float Float1;
[Group("Group Two")] [Maximum(100)] public float Float2;
[Group("Group One")] public bool Bool1;
[Group("Group Two")] public bool Bool2;
[Group("Group Two")] public int Int2;

Group

Inline

Add this to a class or struct type with a Serializable attribute to show the fields of the class or struct inline rather than in the default foldout. Member labels can be optionally shown which is useful if the class has one value field.

[Serializable]
public class WrapperClass
{
	public int Value;
}

[Inline]
public WrapperClass Wrapper;
ParameterDescriptionDefault
ShowMemberLabelsWhether the label of the owning field should be used rather than the label for each of the fields in the class or struct (useful for wrapper classes with a single field)true

ObjectPicker

Add this to a UnityEngine.Object derived field to show a searchable popup listing (like the AddComponent window) of the available objects of the correct type instead of the default object picker. This will list assets based on their structure in the project folder, and Components/GameObjects by their hierarchy in the scene.

[ObjectPicker]
public ScriptableObject Asset;

Object Picker

TypePicker

Add this to a string field to show a searchable popup listing (like the 'AddComponent window') of types derived from the selected type. Since the Type class itself is not serializable this will store the selected type's AssemmblyQualifiedName so that the desired type can be looked up with Type.GetType(assemblyQualifiedName) method.

[TypePicker(typeof(MonoBehaviour), false)]
public string Type;
ParameterDescriptionDefault
BaseTypeThe type to use as the base type to select fromrequired
ShowAbstractWhether to display abstract types in the pickerfalse

Type Picker

Popup

Add this to an int, float, or string field to show a popup instead of the default text box. The constructor takes two parameters: an array of floats/ints/strings that are the values to select and assign, and an optional array of strings that are shown as the labels for each value. These can also be retreived from a method, property, or field returning a List<T> or a PopupValues<T> if string option names are desired.

[Popup(new int[] { 0, 1, 2, 3, 4 }, new string[] { "Zero", "One", "Two", "Three", "Four" })]
public int IntPopup;

[Popup(new float[] { 0.0f, 0.1f, 0.2f, 0.4f, 0.8f, 1.6f })]
public float FloatPopup;

[Popup(new string[] { "", "Hello", "Hola", "Bonjour" })]
public string StringPopup;

Popup

Reference

Add this attribte to a field in addition to Unity's [SerializeReference] attribute to enable creating and editing of any valid subtype for the field.

public interface IReferenceType
{
}

[Serializable]
public class ReferenceTypeOne : IReferenceType
{
	public bool Value;
}

[Serializable]
public class ReferenceTypeTwo : IReferenceType
{
	public int Value;
}

[SerializeReference] [Reference]
public IReferenceType Reference;

Reference

Tabs

Add this attribute to any number of fields to display them together in a tab like view. The first parameter specifies the group of fields to display together and the second is the title of the tab to display the field on.

[Tabs("Tabs", "One")] public int Int1;
[Tabs("Tabs", "One")] public float Float1;
[Tabs("Tabs", "Two")] [Maximum(100)] public float Float2;
[Tabs("Tabs", "One")] public bool Bool1;
[Tabs("Tabs", "Two")] public bool Bool2;
[Tabs("Tabs", "Two")] public int Int2;

Tabs

Slider

Apply to an int, float, or Vector2 field to display the value as a slider with a minimum and maximum. Applying this to a Vector2 displays a MinMaxSlider corresponding to the x and y values repectively. The minimum and maximum values can also be retreived from a method, property, or field of the corresponding type.

[Slider(0, 10)]
public int Slider;

[Slider(0, 10)]
public float SliderFloat

[Slider(0, 10)]
public Vector2 MinMaxSlider;

Slider Min Max Slider

Property Trait Attributes

These attributes are added to serializable fields in editable classes (MonoBehaviours or ScriptableObjects) to enable custom validation display and execution for the field. Unlike normal property attributes in Unity, multiple of these can be applied to a single field. Most attributes can have their properties retrieved from another field, property, or method through reflection by specifying the associated string name. All attributes are in the PiRhoSoft.Utilities namespace.

Button

Apply this to a field to display a button next to this field's control. Clicking the button will call the method defined by the passed in string name. Either a label or icon can be specified to show.

[Button(nameof(Clicked), "Click")]
public bool Toggle;

[Button(nameof(Clicked), ButtonIcon.Inspect, Location = TraitLocation.Right)]
public GameObject Inspect;

private void Clicked() => Debug.Log("Clicked");
ParameterDescriptionDefault
LabelThe label of the buttonnull
IconThe icon of the buttonAdd
TooltipThe tooltip of the buttonnull
LocationThe location of the button: Above, Below, Left, or RightAbove

ChangeTrigger

Apply this to a field and pass in the string name of a method to call when the value changes. The method may be optionally be parameterless, take the new value, or take both the old and the new value.

[ChangeTrigger(nameof(Changed))]
public bool Toggle;

private void Changed(bool oldValue, bool newValue) => Debug.Log($"Changed from {oldValue} to {newValue}");

Conditional

Add this to a field to only display the field based on the specfied rules. The constructor takes the string name of a sibling method, field, or property that returns the dependent value and how to test agaist that value. The dependent value can be a string, enum, int, float, bool, or UnityObject.

public bool ShowConditional = true;

[Conditional(nameof(ShowConditional), BoolTest.ShowIfTrue)]
public string ConditionalString;

CustomLabel

Add this to any field to change the display of the label. This is useful to give more info in the inspector without changing the name of the field to something over verbose or invalid in code. The label can also be retreived from a string method, field, or property.

[CustomLabel("Speed (m/s)")]
public float Speed;

Delay

[Delay]
public string DelayValidation;

Apply this to a any field that uses text input to edit its value to delay the value being applied until enter is pressed.

InspectTrigger

Apply this to a field and pass in the string name of a method to call when the object is initially selected in the inspector. This is useful for updating and validating values prior to inspecting.

[InspectTrigger(nameof(Inspect))]
public bool Toggle;

private void Inspect() => Debug.Log("Object selected");

Maximum

Add this to an int or float field to disallow selecting of a value higher than a specified value. The maximum can also be retreived from a method, field, or property of the corresponding type.

[Maximum(100.0f)]
public float MaximumFloat;

Minimum

Add this to an int or float field to disallow selecting of a value lower than a specified value. The minimum can also be retreived from a method, field, or property of the corresponding type.

[Minimum(0.0f)]
public float MinimumFloat;

MaximumLength

Apply this to a string field to constrain it to a maximum length of characters. The maximum can also be retreived from a method, field, or property.

[MaximumLength(5)]
public string MaximumString;

Multiline

Apply this to a string field to display it as a multiline text box.

[Multiline]
public string MultilineText;

MessageBox

Apply this to any field to display a MessageBox that can provide useful warnings or info.

[MessageBox("ALERT: ALL OUR BASE ARE BELONG TO US", MesasgeBoxType.Info)]
public string Message = "A message is above me";

NoLabel

Add this to any field to make it display in the inspector without a label.

[NoLabel]
public string NoLabel = "I don't have a label";

Placeholder

Apply this to a string, field to display a placeholder label in the text field when the the string is empty. The placeholder can also be retreived from a string method, field, or property.

[Placeholder("placeholder")]
public string Placeholder;

ReadOnly

Add this to any field to disable editing of the field (while still showing it) in the inspector.

[ReadOnly]
public float Disabled;

Required

Apply this to a string, UnityEngine.Object derived type, or [SerializeReference] field to display a message box warning if they are empty or null.

[Required("A string must be entered")]
public string RequiredString;

[Required("An object must be selected")]
public GameObject RequiredObject;

Snap

Add this to any number field (int, float, Vector2, etc) to round the selected value to be a multiple of a specified value. The snapped value can also be retreived from a method, field, or property that returns the corresponding type.

[Snap(0.5f)]
public float SnapFloat;

Stretch

Apply this to any field to display its control below the label instead of next to it. This is useful for controls that can take advantage of the extra space normally taken up by the label.

[Stretch]
[Multiline]
public string MultilineStretch;

Validate

Apply this to a field to call a bool returning method determining whether or not the specified value is valid. If not a MessageBox will be displayed.

[Validate(nameof(IsOdd), "An odd number must be entered")]
public int OddInt;
private bool IsOdd() => OddInt % 2 != 0;

Wrapper Classes

SerializedList<T>

This can be used just like the built in List class but because it isn't the built in List class can be targeted by PropertyDrawers. Because Unity doesn't serialize generic classes, though, it is necessary to subclass this for each type and give the subclass the [Serializable] attribute.

[Serializable] public class IntList : SerializedList<int> {}

public class ExampleBehaviour : MonoBehaviour
{
	[List] public IntList List = new IntList();
}

SerializedArray<T>

This is exactly like SerializedList except for Arrays. The exception is SerializedArray must be constructed with a length.

[Serializable] public class IntArray : SerializedArray<int> { public IntArray(int count) : base(count) {} }

public class ExampleBehaviour : MonoBehaviour
{
	[List] public IntArray Array = new IntArray(5);
}

SerializedDictionary<KeyType, ValueType>

And again for Dictionary. KeyType must be string in order to be targeted by the DictionaryAttribute

[Serializable] public class IntDictionary : SerializedDictionary<string, int> {}

public class ExampleBehaviour : MonoBehaviour
{
	[Dictionary] public IntDictionary Dictionary = new IntDictionary();
}