This repository is no longer maintained. I am working on a much more robust Unity reflection framework which will entirely replace it.
A set of Unity classes and their inspector drawers that provide easy reflection and decoupling.
This editor extension provides 2 new classes, UnityMember
and AnimatorParameter
, along with their custom drawers, that behave like Unity's built-in UnityEvent
for fields, properties, methods and animator parameters reflection.
With them, you can easily refer to members of Unity.Object
classes directly in the inspector and use them in scripting later. This allows for quick prototyping and decoupling for complex games and applications (albeit at a small performance cost).
It's easier to explain it with pictures:
Inspector view:
(Note: These screenshots are outdated; UnityMethod
and UnityVariable
are now unified in UnityMember
)
- Inspect fields, properties and methods
- Inspect animator parameters
- Serialized for persistency across reloads
- Works on GameObjects and ScriptableObjects
- Multi-object editing
- Undo / Redo support
- Looks and feels like a built-in Unity component
- Doesn't support open-constructed generic methods (yet)
- Import Ludiq.Controls in your project
- Import the
Reflection
folder in your project
You're good to go!
- Create a behaviour script
- Add
using Ludiq.Reflection;
to your namespaces - Add a
UnityMember
as a public member - Add a
[Filter]
attribute to choose which members are shown - Set your bindings in the inspector
- Access the variables and methods directly from your script!
Here's a simple example that will display the value of any method and any variable on start:
using UnityEngine;
using Ludiq.Reflection;
public class BasicExample : MonoBehaviour
{
[Filter(Methods = true)]
public UnityMember method;
[Filter(Fields = true, Properties = true)]
public UnityMember variable;
void Start()
{
Debug.LogFormat("Method return: {0}", method.Invoke());
Debug.LogFormat("Variable value: {0}", variable.Get());
}
}
object UnityMember.Get()
T UnityMember.Get<T>()
Retrieves the value of the variable.
void UnityMember.Set(object value)
Assigns a new value to the variable.
object UnityMember.Invoke(params object[] arguments)
T UnityMember.Invoke<T>(params object[] arguments)
Invokes the method with any number of arguments of any type and returns its return value, or null if there isn't any (void).
object UnityMember.GetOrInvoke(params object[] arguments)
T UnityMember.GetOrInvoke<T>(params object[] arguments)
If the member is a field or property, retrieves its value. If the member is a method, invokes it with any number of arguments of any type and returns its return value, or null if there isn't any (void).
This method is usually combined with [Filter(Gettable = true)]
.
object UnityMember.InvokeOrSet(params object[] argumentsOrValue)
T UnityMember.InvokeOrSet<T>(params object[] argumentsOrValue)
If the member is a method, invokes it with any number of arguments of any type and returns its return value, or null if there isn't any (void). If the member is a field or property, sets its value to the first argument and returns null.
This method is usually combined with [Filter(Methods = true, Settable = true)]
.
Type UnityMember.type { get; }
The type of the reflected field or property or return type of the reflected method.
Unfortunately, the Unity inspector doesn't allow for null
values to be assigned to properties. If you want to know if a UnityMember
has been properly assigned (e.g. not "Nothing" in the inspector), use the isAssigned
indicator:
using UnityEngine;
using Ludiq.Reflection;
public class AssignmentExample : MonoBehaviour
{
[Filter(Fields = true, Properties = true)]
public UnityMember variable;
void Start()
{
// Bad:
if (variable != null) // Will always return true
{
Debug.Log(variable.Get()); // Might throw an exception
}
// Good:
if (variable.isAssigned)
{
Debug.Log(variable.Get());
}
}
}
You can tell the inspector to look on the current object instead of manually specifying one by adding the [SelfTargeted]
attribute. For example:
using UnityEngine;
using Ludiq.Reflection;
public class AdvancedExample : MonoBehaviour
{
[SelfTargeted, Filter(Methods = true)]
public UnityMember selfMethod;
}
You can specify which members will appear in the inspector using the Filter
attribute. You can combine a number of options to display only the members you want. For example:
using UnityEngine;
using Ludiq.Reflection;
public class AdvancedExample : MonoBehaviour
{
// Only show fields of type Transform
[Filter(typeof(Transform), Fields = true)]
public UnityMember transformField;
// Only show methods that return an integer or a float
[Filter(typeof(int), typeof(float), Methods = true)]
public UnityMember numericMethod;
// Only show methods that return primitives or enums
[Filter(Methods = true, TypeFamily = TypeFamily.Primitive | TypeFamily.Enum)]
public UnityMember primitiveOrEnumMethod;
// Only show static methods
[Filter(Methods = true, Static = true, Instance = false)]
public UnityMember staticMethod;
// Include non-public properties
[Filter(Properties = true, NonPublic = true)]
public UnityMember hiddenProperty;
// Exclude readonly properties
[Filter(Properties = true, ReadOnly = false)]
public UnityMember writableProperty;
// Include methods that are defined in the object's hierarchy
[Filter(Methods = true, Inherited = true)]
public UnityMember definedMethod;
// Combine any of the above options
[Filter(typeof(Collider), Fields = true, Static = true, ReadOnly = false)]
public UnityMember colliderVariable;
}
You should always enable at least one of the following options:
Option | Description |
---|---|
Fields | Display fields |
Properties | Display properties |
Methods | Display methods |
Gettable | Display fields, properties with a getter and methods with a return type |
Settable | Display fields and properties with a setter |
The additional optional options are:
Option | Description | Default |
---|---|---|
Inherited | Display members defined in the types's ancestors | false |
Instance | Display instance members | true |
Static | Display static members | false |
Public | Display public members | true |
NonPublic | Display private and protected members | false |
ReadOnly | Display read-only properties and fields | true |
WriteOnly | Display write-only properties and fields | true |
Extension | Display extension methods | true |
Parameters | Display methods with parameters | true |
TypeFamily | Determines which member type families are displayed | TypeFamily.All |
Types | Determines which member types are displayed | (Any) |
The TypeFamily
enumeration is a bitwise flag set with the following options:
Flag | Description |
---|---|
None | No type allowed |
All | Any type allowed |
Value | Value types (excl. void) |
Reference | Reference types |
Primitive | Primitive types |
Array | Arrays |
Enum | Enumerations |
Class | Classes |
Interface | Interfaces |
Void | Void |
You can combine them with the bitwise or operator:
TypeFamily enumsOrInterfaces = TypeFamily.Enum | TypeFamily.Interface;
You can change the filter defaults by editing the the FilterAttribute
constructor in Reflection/FilterAttribute.cs
.
You can tell the inspector to label the member options as {Name} : {Type}
instead of {Type} {Name}
by adding the [LabelTypeAfter]
attribute. For example:
using UnityEngine;
using Ludiq.Reflection;
public class AdvancedExample : MonoBehaviour
{
[Filter(Methods = true), LabelTypeAfter]
public UnityMember method;
}
You can create UnityMember
s directly from script, if you need to:
using UnityEngine;
using Ludiq.Reflection;
public class ScriptExample : MonoBehaviour
{
[Filter(Fields = true, Properties = true)]
public UnityMember inspectorVariable;
void Start()
{
// Print the transform's position
var variable = new UnityMember("Transform", "position", gameObject);
Debug.Log(variable.Get());
// Call SetActive directly on the GameObject
var method = new UnityMember("SetActive", gameObject);
method.Invoke(false);
// Modify a variable assigned from the inspector
inspectorVariable = new UnityMember("Transform", "Rotation", inspectorVariable.target);
Debug.Log(inspectorVariable.Get());
}
}
If you want to directly access the System.Reflection
objects, you can do so using the following properties. Note that you must previously have reflected the member, either manually via UnityMember.Reflect()
, or automatically by accessing / invoking it.
FieldInfo UnityMember.fieldInfo { get; }
The underlying reflected field, or null if the variable is a property or a method.
PropertyInfo UnityMember.propertyInfo { get; }
The underlying reflected property, or null if the getter is a field or a method.
MethodInfo UnityMember.methodInfo { get; }
The underlying reflected method, or null if the getter is a field or a property.
You can "reflect" animator parameters with the AnimatorParameter
class. It supports the SelfTargeted
attribute, but not the Filter
attribute. Example:
using UnityEngine;
using Ludiq.Reflection;
public class AnimatorExample : MonoBehaviour
{
public AnimatorParameter speedParameter;
void Start()
{
speedParameter.Set(5);
}
}
The following methods and properties are available:
object AnimatorParameter.Get()
Retrieves the value of the parameter.
void AnimatorParameter.Set(object value)
Assigns a new value to the parameter.
void AnimatorParameter.SetTrigger()
Triggers the parameter.
void AnimatorParameter.ResetTrigger()
Resets the trigger on the parameter.
Type AnimatorParameter.type { get; }
The type of the parameter, or null if it is a trigger.
AnimatorControllerParameter AnimatorParameter.parameterInfo { get; }
The underlying animator controller parameter.
I'll happily accept pull requests if you have improvements or fixes to suggest.
- Open-constructed generic method support (requires generic type serialization)
The whole source is under MIT License, which basically means you can freely use and redistribute it in your commercial and non-commercial projects. See the license file for the boring details. You must keep the license file and copyright notice in copies of the library.
If you use it in a plugin that you redistribute, please change the namespace to avoid version conflicts with your users. For example, change Ludiq.Reflection
to MyPlugin.Reflection
.