Types in the Unity Editor via reflection
How to select from a list of C# classes in a Unity Editor dropdown.
I've written a couple of Unity Editor tools for Hexahedra recently, a level editor and a tutorial editor. The tutorial editor lets me trigger popups on, among other things, the player failing a level in a particular way, so that I can provide feedback to the player for each one.
HexEvents
The underlying puzzle framework passes HexEvents
to Unity for display, and each way of failing has its own subtype. HexahedronInInactiveWorkstation
, PayloadDamaged
, etc. To set up the triggers, I needed a dropdown that contained all the different failure events to choose from.
So, how can we build that list and wire it into the Unity Editor? Happily, with a bit of C# reflection, it's straightforward.
Building the list
This is the reflection part.
private void FindFailureEvents() {
Type workstationEventType = typeof(WorkstationEvent);
Assembly hexSim = Assembly.GetAssembly(workstationEventType);
Type[] types = hexSim.GetTypes();
// Find all types that are:
// - a subtype of WorkstationEvent
// - have their own implementation of PuzzleFailed (which only the ones returning true have)
failureEvents = types.Where(
t => t.IsSubclassOf(workstationEventType) &&
t.GetMethod("PuzzleFailed").DeclaringType == t).ToList();
}
Building a Type[]
array of failure events.
The failure events are all subtypes of WorkstationEvent
(which is a HexEvent
that knows which workstation it happened in). All the subtypes we need are in the same assembly, so we start by getting all the types in the assembly, and we then whittle them down with a LINQ statement.
types.Where(t => t.IsSubclassOf(workstationEventType))
gets us every kind of WorkstationEvent
, even ones that aren't failures. Getting just the failures required a bit of trickery. All HexEvents
have a PuzzleFailed
method. The one on HexEvent
itself returns false
:
public abstract class HexEvent {
public virtual bool PuzzleFailed() {
return false;
}
}
The only functionality all HexEvents
must have is an indication of whether the level fails when they occur.
The only subtypes that bother to override the method are the ones that are failures and need to return true
, so adding the second part of the LINQ statement, t.GetMethod("PuzzleFailed").DeclaringType == t
, which finds all the subtypes that override PuzzleFailed
, happens to get all the events we need.
Getting it into the Editor
Armed with the list, we can easily extract the type names as strings with a LINQ Select
. The only other thing we need for a dropdown is which Type the TutorialTriggerPuzzleFailed
object we're configuring is currently looking for, so that we can indicate which popup entry is selected:
case TutorialTriggerType.PUZZLE_FAILURE: {
if (failureEvents == null) {
FindFailureEvents();
}
TutorialTriggerPuzzleFailed puzzleFailed = (TutorialTriggerPuzzleFailed) triggers[pointer];
String[] eventNames = failureEvents.Select(fe => fe.Name).ToArray();
int currentIndex = failureEvents.IndexOf(puzzleFailed.failureType);
puzzleFailed.failureType = failureEvents[EditorGUILayout.Popup("Failure Type", currentIndex, eventNames)];
break;
}
Unity Editor code to populate the popup and take action when the select option changes.
So there you are, Types in the Unity Editor. Passing Types around lets you do some powerful stuff with your game's behaviour; if you found this post useful I'd love to see tweets of what you've come up with — hit me up @SidequestNinja.
Hexahedra has a Steam demo — head to the store page to try it out.