Skip to content

Instantly share code, notes, and snippets.

@a-gruzdev
Last active December 13, 2024 16:05
Show Gist options
  • Save a-gruzdev/3edded527e4572318bcba7cb46d08528 to your computer and use it in GitHub Desktop.
Save a-gruzdev/3edded527e4572318bcba7cb46d08528 to your computer and use it in GitHub Desktop.
Fixed length array associated with any enum and with handy PropertyDrawer
using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
[Serializable]
public class EnumArray<E, T> : ISerializationCallbackReceiver where E : Enum
{
public static readonly int s_Length;
[SerializeField] private T[] _values;
public int Length => s_Length;
static EnumArray()
{
var names = Enum.GetNames(typeof(E));
s_Length = names.Length;
#if UNITY_EDITOR
EnumArrayDrawer.Names[typeof(E)] = names;
if (Enum.GetUnderlyingType(typeof(E)) != typeof(int))
Debug.LogWarning($"EnumArray should not be used with Enum type {typeof(E)} as it doesn't derive from int");
var values = Enum.GetValues(typeof(E)) as int[];
Array.Sort(values, (i0, i1) => i0 - i1);
if (values[0] != 0) Debug.LogWarning($"EnumArray should not be used with Enum type {typeof(E)} as it doesn't start from 0");
for (int i = 1; i < values.Length; i++)
{
if (values[i] != values[i - 1] + 1)
{
Debug.LogWarning($"EnumArray should not be used with Enum type {typeof(E)} as it contains a gap in enums");
break;
}
}
#endif
}
public EnumArray()
{
_values = new T[s_Length];
}
public T this[E e]
{
get => _values[e.GetHashCode()];
set => _values[e.GetHashCode()] = value;
}
public T this[int i]
{
get => _values[i];
set => _values[i] = value;
}
public void OnAfterDeserialize()
{
if (_values.Length != s_Length)
Array.Resize(ref _values, s_Length);
}
public void OnBeforeSerialize() { }
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(EnumArray<,>))]
public class EnumArrayDrawer : PropertyDrawer
{
private const float Padding = 4;
private const float Spacing = 2;
public static readonly System.Collections.Generic.Dictionary<Type, string[]> Names = new();
private Type _type;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
if (_type == null)
_type = fieldInfo.FieldType.GenericTypeArguments[0];
property.Next(true);
return (EditorGUIUtility.singleLineHeight + Spacing) * (property.arraySize + 1) + Padding * 2;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (Event.current.type == EventType.Repaint)
EditorStyles.helpBox.Draw(position, false, false, false, false);
position.height = EditorGUIUtility.singleLineHeight;
position.y += Padding;
position.x += Padding;
position.width -= Padding * 2;
GUI.Label(position, label, EditorStyles.boldLabel);
property.Next(true);
var step = EditorGUIUtility.singleLineHeight + Spacing;
var labels = Names[_type];
for (int i = 0; i < property.arraySize; i++)
{
position.y += step;
label.text = labels[i];
EditorGUI.PropertyField(position, property.GetArrayElementAtIndex(i), label);
}
}
}
#endif
@mattdevv
Copy link

Thanks for sharing, I forked this to add validation for the Enum type used and changed the inspector drawer to a foldout since it can take up a lot of space.

@a-gruzdev
Copy link
Author

Thank you! I took the validation part

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment