- Home /
how do I generate a drop-down list of functions on scripts attached to a game object?
Hi
I'm writing a property drawer to customise the unity editor GUI for a class I've written.
To make it work the way I would like to I need to be able to generate a drop list of all functions belonging to all instances of monobehaviour derived types that are attached to the same game object.
Clearly making a drop list in a property drawer is pretty trivial.
However, to populate the drop list correctly I need to iterate all components attached to the same gameObject as the component that has the property drawer and generate a list of all (public?) functions exposed by those components.
I also need to be able to use the user's selection in the drop-list to create a delegate to the appropriate function of the appropriate component.
I know this is possible, because the GUI for creating and editing animation events does something like this to allow the user to choose which function to call when the event is triggered, but how this might be accomplished is far from obvious based on the documentation and existing tutorials I've found.
I'm sure I'll work it out myself eventually and if I do I'll post an answer, but I'd appreciate your help if you know how to do this or can point me at sample code...
Alex
Answer by vexe · Mar 18, 2014 at 11:55 AM
The solution is simple, if you know where to look - it's called Reflection :)
First, you should specify the access level of the methods you're interested in (public, private, etc)
I will give you an answer on how to get the methods, but I will leave the way you store them up to you (you could put them all in one list, you could create a GameObjectEntry, that has a List of MonoBehaviourEntry, which has a List of the methods themselves, etc)
I will just use a list.
The general idea:
1- Get all the MonoBehaviours on your desired gameObject. 2- from each MB, fetch the methods you're interested in.
var methods = new List<MethodInfo>();
var mbs = target.GetComponents<MonoBehaviour>();
var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Default; // BindingFlags is located in System.Reflection - modify these to your liking to get the methods you're interested in
foreach (var mb in mbs)
{
methods.AddRange(mb.GetMethods(flags));
}
foreach(var m in methods)
Debug.Log(m.Name);
That's pretty much it really :D
Here's some helper methods you might be interested in (they're extension methods, so put them in a static class):
public static class Extensions
{
public static List<MethodInfo> GetMethods(this MonoBehaviour mb, Type returnType, Type[] paramTypes, BindingFlags flags)
{
return mb.GetType().GetMethods(flags)
.Where(m => m.ReturnType == returnType)
.Select(m => new { m, Params = m.GetParameters() })
.Where(x =>
{
return paramTypes == null ? // in case we want no params
x.Params.Length == 0 :
x.Params.Length == paramTypes.Length &&
x.Params.Select(p => p.ParameterType).ToArray().IsEqualTo(paramTypes);
})
.Select(x => x.m)
.ToList();
}
public static List<MethodInfo> GetMethods(this GameObject go, Type returnType, Type[] paramTypes, BindingFlags flags)
{
var mbs = go.GetComponents<MonoBehaviour>();
List<MethodInfo> list = new List<MethodInfo>();
foreach (var mb in mbs) {
list.AddRange(mb.GetMethods(returnType, paramTypes, flags));
}
return list;
}
}
An example of the first one:
using bf = System.Refleciton.BindingFlags; // put this at the top to reduce some typing
var publicFlags = bf.Instance | bf.Public | bf.Default; // Default could suffice...
var privateFlags = bf.Instance | bf.NonPublic; // NonPublic alone doesn't suffice when you need to get private methods...
var publicVoidMethods = mb.GetMethods(typeof(void), null, publicFlags); // returns all public methods of the signature void methodName();
var privateSpecialMethods = mb.GetMethods(typeof(Vector3), new[] { typeof(int), typeof(GameObject) }, privateFlags);
and so on...
The second one works very similar to the foreach loop ^ - given a gameObject, it would return the specified methods.
var methods = go.GetMethods(... similar input to mb.GetMethods ...);
Hope that was helpful.
If you want to learn about reflection, there's no one better than Jamie King on youtube :)
EDIT:
Was missing this:
public static bool IsEqualTo<T>(this IList<T> list, IList<T> other)
{
if (list.Count != other.Count) return false;
for (int i = 0, count = list.Count; i < count; i++) {
if (!list[i].Equals(other[i])) {
return false;
}
}
return true;
}
Thanks dude.
I knew the reflection stuff was there and was aware that I'd have to use it to get at the methods - I was really just hoping that there might be existing sample code out there doing more or less what I was trying to to save me muddling through it...
I guess I have most of it now from your answer ;)
I'll totally check out that youtube video too.
If you're interested, once this is done I'll push it into my friend's github repo for sample property drawers: https://github.com/tenpn/ChestOfPropertyDrawers
$$anonymous$$
Cool great I'll check it out. Glad I helped. If you need anything more let me know :)
Oh crap there's a missing extension method - IsEqualTo - gonna udpate.
another github repo of property drawers - n.b. comments are not in English https://github.com/anchan828/property-drawer-collection
Is this technique possible using UnityScript/Javascript?
Your answer
Follow this Question
Related Questions
in-editor cloning of game objects 1 Answer
What is an instance of a GameObject? 1 Answer
Any way to lock an object from changes in the editor? 4 Answers
Automatically update prefab instance 1 Answer