Reflectionを使って文字列から関数を実行する
まずは具体例から
Playerクラス
class Player
{
public void Attack(string name, int atk)
{
Debug.Log($"{name} は {atk} のダメージを与えた!");
}
}
このAttack関数を実行したいとき、普段ならこのように書きます
普段の書き方
Player player = new Player();
player.Attack("田中", 30);
今回は
・関数を実行するインスタンス
・実行する関数名
・引数のstring配列
の3つを使って実行してみます
Attackを実行
// リフレクションを使って関数を呼び出す
public void CallFunction()
{
Player player = new Player(); // 関数を実行したいインスタンス
string[] parameters = new string[] { "田中", "30" }; // 引数の文字列
string methodName = "Attack"; //実行したい関数名
// playerの型情報typeを取得
Type type = player.GetType();
// PlayerクラスのAttack関数の情報を取得
MethodInfo method = type.GetMethod(methodName);
// Attack関数の引数情報を取得
ParameterInfo[] paramInfos = method.GetParameters();
// parametersの型をstringから引数の型に直したモノを入れる配列
object[] typedParameters = new object[parameters.Length];
// 引数情報paranInfosで取得した型にparametersを変換
for (int i = 0; i < parameters.Length; i++)
{
typedParameters[i] = Convert.ChangeType(parameters[i], paramInfos[i].ParameterType);
}
// 関数を実行
method.Invoke(player, typedParameters);
}
上の具体例のインスタンス, methodName, parametersを引数に変えたものがこちら
引数にチェンジ
// リフレクションを使って関数を呼び出す
void CallFunctionByMethodName(object obj, string methodName, string[] parameters)
{
// objの型情報typeを取得
Type type = obj.GetType();
// objのクラスの名前が methodName の関数の情報を取得する
MethodInfo method = type.GetMethod(methodName);
if (method != null)
{
ParameterInfo[] paramInfos = method.GetParameters();
if (parameters != null && parameters.Length >= 1)
{
object[] typedParameters = new object[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
typedParameters[i] =
Convert.ChangeType(parameters[i], paramInfos[i].ParameterType);
}
method.Invoke(obj, typedParameters);
}
else
{
method.Invoke(obj, null);
}
}
}
GameObjectの名前とコンポーネントも文字列で指定する
さらにGameObjectの名前とコンポーネントも文字列で指定して関数を呼び出すように変更します。
文字列からGameObjectを取得するにはGameObject.Find(string name)
を利用します。
GameObject gameObject = GameObject.Find(gameObjectName);
文字列からコンポーネントを取得するにはComponent GetComponent(string type)
を利用します。
gameObject.GetComponent(componentName);
呼び出したいときに渡す情報をクラスにまとめておきます。
class FunctionByString
{
public string GameObjectName { get; set; }
public string ComponentName { get; set; }
public string FunctionName { get; set; }
public string[] Parameters { get; set; }
}
上のFunctionByStringを渡して実行するプログラムがこちら
void CallFunctionBy(FunctionByString functionByString)
{
// 文字列からGameObjectを取得
GameObject targetObject = GameObject.Find(functionByString.GameObjectName);
if (targetObject != null)
{
// 文字列からコンポーネントを取得
Component targetComponent =
targetObject.GetComponent(functionByString.ComponentName);
if (targetComponent != null)
{
// さっき作った関数に渡す
CallFunctionByMethodName(
targetComponent,
functionByString.FunctionName,
functionByString.Parameters);
}
}
}
何が嬉しいか
CSVなどで関数の実行を制御できる
イベントシーンの動きをCSVなどで制御できます
問題点
GameObject.FindやGetComponentを毎回行っている
重めの処理なのでよく使うコンポーネントはDictionaryとかにまとめておいた方がよいかも
参考文献