Is it possible to create a List filled with methods?

Hi everyone, I have created a mob named Stan. Stan derives from the class DefaultCharacter, which looks a little like the example below.

public Class DefaultCharacter:MonoBehaviour{
    public List<string> Abilities = new List<string>();
}

In the inspector, you can set his abilities by filling them with the names of abilities I’ve kept in another class named Skills. The abilities I’ve kept in skills are all functions, that you can call in game (think final fantasy’s abilites). I set them as functions rather than creating a class each skill can derive from as the skills will be so different from one another that it would be pointless to have them all come from one place.

This has let to certain complications, however, as I would like to be able to set Stan’s four abilities in the inspector, and then have my AI be able to run them according to their index, for example, if it was the enemies’ turn, I would (for example) have it choose a number from one to four, (one for each ability) and then use the skill at that index in the list.

The problem is I can’t convert the string’s name to a function call. Is there a way to create a list of functions? I’ve used List but obviously that doesn’t work.

Why not use SendMessage()? Since you won't be calling it every single frame, there shouldn't be any concern about efficiency. http://docs.unity3d.com/Documentation/ScriptReference/GameObject.SendMessage.html

3 Answers

3

You can use your strings to “look up” functions in your Skills class. Something like Skills.GetMethod(“someFunctionName”).

Thanks, I didn't know about this method, but now I do

will this work for ienumerators?

hmm, I get the error " Type Skills' does not contain a definition for GetMethod' and no extension method GetMethod' of type Skills' could be found"

Your reflection approach would be:

  var mi = skillsObject.GetType().GetMethod(functionName) as MethodInfo;

Now you need to create a delegate for that which uses some standard signature. If it’s a void UseAbility() type method then you would do:

   Action useSkill = (Action)mi.CreateDelegate(typeof(Action), skillsObject);

And call it using:

  useSkill();

You’d want to cache that or it will be just as slow as SendMessage

If you want it to return a value and take an object your would define your delegate signature like this:

    Func<GameObject, bool> canIUseSkillOnThisObject = mi.CreateDelegate(typeof(Func<GameObject, bool>), skillsObject) as Func<GameObject, bool>;

And use it like this:

   if(canIUseSkillOnThisObject(someObject))
   {
          //Do something
   }

You can also invoke the delegate directly (SLOW - not recommended) if((bool)mi.Invoke(skillsObject, parameter1, parameter2, parameter3)) //Any number of parameters { } CreateDelegate Invoke

You would be forced to define a function with the name of the skill and optionally have an function called IsAbleToXXXX (where XXXX is the skill). If you didn't define the latter, it would presume it was always able to work.

There are lots of cool ways to go further using Attributes so you could just write things like: [Skill] void Cast(GameObject target) { ... } [Skill] void Smash(GameObject target) { } And have the system automatically find all of these to avoid you needing to add them to the list of strings - just attach behaviours to a game object and it would get additional skills automatically.

If you are interested in such a system I'd be happy to write up some notes

Thanks for everything, I'm going to give this a try when I'm next able to edit my project. for var mi = skillsObject.GetType().GetMethod(functionName) as MethodInfo;, can I use var in C#? or must I declare it as a MethodInfo at the beginning? also, does using System.Linq enable the use of Action() and Func()? Thank you for offering to provide notes, though I admit my code is quite simplistic and I have to learn a bit about the methods you've provided me with first before venturing any deeper. Thanks for your help again!

Why not just edit the Skills class to have something like this:

public void PerformSkill(string skillName){
    switch(skillName.ToLower()) {
        case "super punch":
            SuperPunch();
            break;
        case "upper cut":
            UpperCut();
            break;
        default:
            Debug.Log(string.Format("Urp. {0} doesn't exist.", skillName));
    }
}

There are more admittedly “advanced” ways to do this, but this is easy, maps well and executes fast. Also doesn’t require reflection or keeping delegate dictionaries (which isn’t bad for the program but wrapping your head around them can be something like slamming a brick through the nearest available hole and hoping all goes well.)