- Home /
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 Send$$anonymous$$essage()? 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.Send$$anonymous$$essage.html
Answer by whydoidoit · Apr 18, 2013 at 04:00 PM
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
}
Let's presume that you have the list of strings and you want to have two functions for accessing the skill - perform it(with a target) and check if you can perform it.
using System.Linq;
...
public class SkillFunctions
{
public string name;
public Action<GameObject> use;
public Func<GameObject, bool> canUse;
}
public $$anonymous$$onoBehaviour targetBehaviourForFunctions;
public Dictionary<string, SkillFunctions> skillLookup;
public List<SkillFunctions> skills;
public List<string> yourListOfNames;
void Awake()
{
if(!targetBehaviourForFunctions) targetBehaviourForFunctions = this;
skillLookup = yourListOfNames.Select(n=>{
var miUse = targetBehaviourForFunctions.GetType().Get$$anonymous$$ethod(n, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
var miCanUse = targetBehaviourForFunctions.GetType().Get$$anonymous$$ethod("IsAbleTo" + n, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
return new SkillFunctions { name = n, use = mi.CreateDelegate(typeof(Actions<GameObject>), targetBehaviourForFunctions) as Action<GameObject>,
canUse = miCanUse != null ? miCanUse.CreateDelegate(typeof(Func<GameObject, bool>), targetBehaviourForFunctions) as Func<GameObject, bool> : (o)=>true
};
}).ToDictionary(c=>c.name);
skills = skillLookup.Values.ToList();
}
Now you can call that liek this:
someObject.GetComponent<WhateverThisScriptIsCalled>().skillLookup[someSkill].use();
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.
It would just allow you to specify the $$anonymous$$onoBehaviour (any script you wrote) as the thing which has the functions on it.
If you are missing $$anonymous$$ethodInfo (using $$anonymous$$onoDevelop right click, and use the resolve menu) add a
using System.Runtime.Reflection;
Oh I'm sorry - just working on another .NET project and that's a 4.5 call :S
You care after
Delegate.CreateDelegate(typeof(Action<GameObject>), skillsObject, methodInfo);
So being able to create a delegate directly from a $$anonymous$$ethodInfo is a 4.5 function. In Unity's .NET you have to use Delegate.CreateDelegate(typeof(Action), objectInstance, methodInfoYouFound) as Action;
Answer by flaviusxvii · Apr 18, 2013 at 03:14 PM
You can use your strings to "look up" functions in your Skills class. Something like Skills.GetMethod("someFunctionName").
http://stackoverflow.com/questions/540066/calling-a-function-from-a-string-in-c-sharp
Thanks, I didn't know about this method, but now I do
hmm, I get the error " Type Skills' does not contain a definition for
Get$$anonymous$$ethod' and no extension method Get$$anonymous$$ethod' of type
Skills' could be found"
Answer by Dracorat · Apr 18, 2013 at 09:02 PM
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.)