Skip to content

Instantly share code, notes, and snippets.

@figgleforth
Created November 18, 2018 13:59
Show Gist options
  • Save figgleforth/e460ce70b9373924b840eb27c78dc9cc to your computer and use it in GitHub Desktop.
Save figgleforth/e460ce70b9373924b840eb27c78dc9cc to your computer and use it in GitHub Desktop.
Helpful script for displaying uints in the Unity inspector
/*
Written by: Lucas Antunes (aka ItsaMeTuni), lucasba8@gmail.com
In: 2/15/2018
The only thing that you cannot do with this script is sell it by itself without substantially modifying it.
*/
using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
[CustomPropertyDrawer(typeof(EnumMaskAttribute))]
public class EnumMaskPropertyDrawer : PropertyDrawer {
bool foldoutOpen = true;
object theEnum;
Array enumValues;
Type enumUnderlyingType;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
if (foldoutOpen)
return EditorGUIUtility.singleLineHeight * (Enum.GetValues(fieldInfo.FieldType).Length + 2);
else
return EditorGUIUtility.singleLineHeight;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
theEnum = fieldInfo.GetValue(property.serializedObject.targetObject);
enumValues = Enum.GetValues(theEnum.GetType());
enumUnderlyingType = Enum.GetUnderlyingType(theEnum.GetType());
//We need to convert the enum to its underlying type, if we don't it will be boxed
//into an object later and then we would need to unbox it like (UnderlyingType)(EnumType)theEnum.
//If we do this here we can just do (UnderlyingType)theEnum later (plus we can visualize the value of theEnum in VS when debugging)
theEnum = Convert.ChangeType(theEnum, enumUnderlyingType);
EditorGUI.BeginProperty(position, label, property);
foldoutOpen = EditorGUI.Foldout(new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), foldoutOpen, label);
if (foldoutOpen) {
//Draw the All button
if (GUI.Button(new Rect(position.x, position.y + EditorGUIUtility.singleLineHeight * 1, 30, 15), "All")) {
for (int i = 0; i < Enum.GetNames(fieldInfo.FieldType).Length; i++) {
ToggleIndex(i, true);
}
}
//Draw the None button
if (GUI.Button(new Rect(position.x + 32, position.y + EditorGUIUtility.singleLineHeight * 1, 40, 15), "None")) {
for (int i = 0; i < Enum.GetNames(fieldInfo.FieldType).Length; i++) {
ToggleIndex(i, false);
}
}
//Draw the list
for (int i = 0; i < Enum.GetNames(fieldInfo.FieldType).Length; i++) {
if (EditorGUI.Toggle(new Rect(position.x, position.y + EditorGUIUtility.singleLineHeight * (2 + i), position.width, EditorGUIUtility.singleLineHeight), Enum.GetNames(fieldInfo.FieldType)[i], IsSet(i))) {
ToggleIndex(i, true);
} else {
ToggleIndex(i, false);
}
}
}
fieldInfo.SetValue(property.serializedObject.targetObject, theEnum);
property.serializedObject.ApplyModifiedProperties();
}
/// <summary>
/// Get the value of an enum element at the specified index (i.e. at the index of the name of the element in the names array)
/// </summary>
object GetEnumValue(int _index) {
return Convert.ChangeType(enumValues.GetValue(_index), enumUnderlyingType);
}
/// <summary>
/// Sets or unsets a bit in theEnum based on the index of the enum element (i.e. the index of the element in the names array)
/// </summary>
/// <param name="_set">If true the flag will be set, if false the flag will be unset.</param>
void ToggleIndex(int _index, bool _set) {
if (_set) {
//enum = enum | val
theEnum = DoOrOperator(theEnum, GetEnumValue(_index), enumUnderlyingType);
} else {
object val = GetEnumValue(_index);
object notVal = DoNotOperator(val, enumUnderlyingType);
//enum = enum & ~val
theEnum = DoAndOperator(theEnum, notVal, enumUnderlyingType);
}
}
/// <summary>
/// Checks if a bit flag is set at the provided index of the enum element (i.e. the index of the element in the names array)
/// </summary>
bool IsSet(int _index) {
object val = DoAndOperator(theEnum, GetEnumValue(_index), enumUnderlyingType);
if (enumUnderlyingType == typeof(int)) {
return (int)val != 0;
} else if (enumUnderlyingType == typeof(uint)) {
return (uint)val != 0;
} else if (enumUnderlyingType == typeof(short)) {
return (short)val != 0;
} else if (enumUnderlyingType == typeof(ushort)) {
return (ushort)val != 0;
} else if (enumUnderlyingType == typeof(long)) {
return (long)val != 0;
} else if (enumUnderlyingType == typeof(ulong)) {
return (ulong)val != 0;
} else if (enumUnderlyingType == typeof(byte)) {
return (byte)val != 0;
} else if (enumUnderlyingType == typeof(sbyte)) {
return (sbyte)val != 0;
} else {
throw new System.ArgumentException("Type " + enumUnderlyingType.GetType().FullName + " not supported.");
}
}
/// <summary>
/// Call the bitwise OR operator (|) on _lhs and _rhs given their types.
/// Will basically return _lhs | _rhs
/// </summary>
/// <param name="_lhs">Left-hand side of the operation.</param>
/// <param name="_rhs">Right-hand side of the operation.</param>
/// <param name="_type">Type of the objects.</param>
/// <returns>Result of the operation</returns>
static object DoOrOperator(object _lhs, object _rhs, Type _type) {
if (_type == typeof(int)) {
return ((int)_lhs) | ((int)_rhs);
} else if (_type == typeof(uint)) {
return ((uint)_lhs) | ((uint)_rhs);
} else if (_type == typeof(short)) {
//ushort and short don't have bitwise operators, it is automatically converted to an int, so we convert it back
return unchecked((short)((short)_lhs | (short)_rhs));
} else if (_type == typeof(ushort)) {
//ushort and short don't have bitwise operators, it is automatically converted to an int, so we convert it back
return unchecked((ushort)((ushort)_lhs | (ushort)_rhs));
} else if (_type == typeof(long)) {
return ((long)_lhs) | ((long)_rhs);
} else if (_type == typeof(ulong)) {
return ((ulong)_lhs) | ((ulong)_rhs);
} else if (_type == typeof(byte)) {
//byte and sbyte don't have bitwise operators, it is automatically converted to an int, so we convert it back
return unchecked((byte)((byte)_lhs | (byte)_rhs));
} else if (_type == typeof(sbyte)) {
//byte and sbyte don't have bitwise operators, it is automatically converted to an int, so we convert it back
return unchecked((sbyte)((sbyte)_lhs | (sbyte)_rhs));
} else {
throw new System.ArgumentException("Type " + _type.FullName + " not supported.");
}
}
/// <summary>
/// Call the bitwise AND operator (&) on _lhs and _rhs given their types.
/// Will basically return _lhs & _rhs
/// </summary>
/// <param name="_lhs">Left-hand side of the operation.</param>
/// <param name="_rhs">Right-hand side of the operation.</param>
/// <param name="_type">Type of the objects.</param>
/// <returns>Result of the operation</returns>
static object DoAndOperator(object _lhs, object _rhs, Type _type) {
if (_type == typeof(int)) {
return ((int)_lhs) & ((int)_rhs);
} else if (_type == typeof(uint)) {
return ((uint)_lhs) & ((uint)_rhs);
} else if (_type == typeof(short)) {
//ushort and short don't have bitwise operators, it is automatically converted to an int, so we convert it back
return unchecked((short)((short)_lhs & (short)_rhs));
} else if (_type == typeof(ushort)) {
//ushort and short don't have bitwise operators, it is automatically converted to an int, so we convert it back
return unchecked((ushort)((ushort)_lhs & (ushort)_rhs));
} else if (_type == typeof(long)) {
return ((long)_lhs) & ((long)_rhs);
} else if (_type == typeof(ulong)) {
return ((ulong)_lhs) & ((ulong)_rhs);
} else if (_type == typeof(byte)) {
return unchecked((byte)((short)_lhs & (short)_rhs));
} else if (_type == typeof(sbyte)) {
//byte and sbyte don't have bitwise operators, it is automatically converted to an int, so we convert it back
return unchecked((sbyte)((sbyte)_lhs & (sbyte)_rhs));
} else {
throw new System.ArgumentException("Type " + _type.FullName + " not supported.");
}
}
/// <summary>
/// Call the bitwise NOT operator (~) on _lhs given its type.
/// Will basically return ~_lhs
/// </summary>
/// <param name="_lhs">Left-hand side of the operation.</param>
/// <param name="_type">Type of the object.</param>
/// <returns>Result of the operation</returns>
static object DoNotOperator(object _lhs, Type _type) {
if (_type == typeof(int)) {
return ~(int)_lhs;
} else if (_type == typeof(uint)) {
return ~(uint)_lhs;
} else if (_type == typeof(short)) {
//ushort and short don't have bitwise operators, it is automatically converted to an int, so we convert it back
return unchecked((short)~(short)_lhs);
} else if (_type == typeof(ushort)) {
//ushort and short don't have bitwise operators, it is automatically converted to an int, so we convert it back
return unchecked((ushort)~(ushort)_lhs);
} else if (_type == typeof(long)) {
return ~(long)_lhs;
} else if (_type == typeof(ulong)) {
return ~(ulong)_lhs;
} else if (_type == typeof(byte)) {
//byte and sbyte don't have bitwise operators, it is automatically converted to an int, so we convert it back
return (byte)~(byte)_lhs;
} else if (_type == typeof(sbyte)) {
//byte and sbyte don't have bitwise operators, it is automatically converted to an int, so we convert it back
return unchecked((sbyte)~(sbyte)_lhs);
} else {
throw new System.ArgumentException("Type " + _type.FullName + " not supported.");
}
}
}
#endif
[System.AttributeUsage(System.AttributeTargets.Field, AllowMultiple = false)]
public class EnumMaskAttribute : PropertyAttribute {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment