Unity 如何通过字符串作为函数名调用其他脚本的公开函数
Unity 无分支字符串调用其他脚本方法的实现方案
以下方案均无需编写大量if判断语句,可根据项目场景选择,所有代码均经过验证可直接运行:
方案1:委托字典映射(生产环境最推荐,性能无损耗)
核心逻辑是在脚本启动时一次性建立「字符串方法名 -> 可执行委托」的映射表,调用时直接查表执行,性能和直接调用原生方法几乎一致,无GC、无反射开销,完全替代原有的if链逻辑。
实现步骤
- 首先编写被调用的目标脚本,所有待调用方法均为
public void类型:
using UnityEngine; public class TargetScript : MonoBehaviour { // 所有需要被字符串调用的public void方法 public void DoAttack() { Debug.Log("攻击触发"); } public void MoveLeft() { Debug.Log("左移触发"); } public void Jump() { Debug.Log("跳跃触发"); } // 新增其他方法直接在下面写即可 }
- 编写调用方脚本,通过字典做映射,完全不需要写if分支:
using System; using System.Collections.Generic; using UnityEngine; public class MethodCaller : MonoBehaviour { // 存储「方法名字符串 -> 可执行委托」的映射表 private Dictionary<string, Action> _methodMap = new Dictionary<string, Action>(); // 在Inspector面板拖入要调用的目标脚本实例 [SerializeField] private TargetScript _targetScript; private void Start() { // 启动时一次性绑定所有需要被调用的方法,后续直接查表即可 _methodMap["Attack"] = _targetScript.DoAttack; _methodMap["Move"] = _targetScript.MoveLeft; _methodMap["Jump"] = _targetScript.Jump; // 新增方法直接加一行映射即可,不用修改调用逻辑 } // 对外提供统一调用入口,传入方法名字符串即可执行对应方法 public void CallMethod(string methodName) { if (_methodMap.TryGetValue(methodName, out var targetMethod)) { targetMethod.Invoke(); } else { Debug.LogWarning($"未找到名为{methodName}的可调用方法映射"); } } }
扩展说明
如果需要调用带参数的方法,仅需扩展字典的委托类型即可,比如带int类型伤害参数的TakeDamage(int damage)方法,可将字典改为Dictionary<string, Action<int>>,绑定和调用逻辑和上述完全一致,无额外复杂度。
方案2:反射自动绑定(适合无需手动维护映射的场景)
如果不想手动逐个声明方法映射,可通过反射批量扫描目标脚本中符合要求的方法,自动生成映射表,可配合自定义特性实现零手动维护,适合方法数量极多、不想手动写绑定代码的场景。
实现步骤
- 先定义一个自定义特性,用来标记哪些方法允许被字符串调用:
using System; // 标记在方法上,代表该方法允许被字符串调用 [AttributeUsage(AttributeTargets.Method)] public class StringCallableAttribute : Attribute { }
- 给目标脚本中需要被调用的方法加上这个特性:
using UnityEngine; public class TargetScript : MonoBehaviour { [StringCallable] public void DoAttack() { } [StringCallable] public void MoveLeft() { } [StringCallable] public void Jump() { } }
- 调用方脚本启动时自动扫描所有带特性的方法,自动填充映射表,完全不需要手动写绑定逻辑:
using System; using System.Collections.Generic; using System.Reflection; using UnityEngine; public class MethodCaller : MonoBehaviour { private Dictionary<string, Action> _methodMap = new Dictionary<string, Action>(); [SerializeField] private TargetScript _targetScript; private void Start() { // 显式指定BindingFlags查找公共实例方法,避免网上常见的查找失败问题 var allMethods = _targetScript.GetType().GetMethods( BindingFlags.Public | BindingFlags.Instance); foreach (var method in allMethods) { // 只筛选带StringCallable特性、返回值为void、无参数的方法 if (method.GetCustomAttribute<StringCallableAttribute>() != null && method.ReturnType == typeof(void) && method.GetParameters().Length == 0) { // 直接将方法转为委托存入字典,后续调用无反射开销 _methodMap[method.Name] = (Action)Delegate.CreateDelegate( typeof(Action), _targetScript, method); } } } public void CallMethod(string methodName) { if (_methodMap.TryGetValue(methodName, out var targetMethod)) { targetMethod.Invoke(); } else { Debug.LogWarning($"未找到名为{methodName}的可调用方法"); } } }
注意:网上很多反射示例无法运行的核心原因是漏写
BindingFlags参数,必须显式指定Public | Instance才能在Unity环境下稳定找到对应方法,不要直接调用无参的GetMethod()重载。
方案3:Unity原生SendMessage(仅适合快速原型,不推荐正式项目)
Unity自带的消息机制可直接通过字符串调用方法,但性能差、坑多,仅适合快速搭建原型时临时使用:
// 获取目标脚本所在的GameObject,直接发送方法名 _targetGameObject.SendMessage( methodName, SendMessageOptions.DontRequireReceiver);
常见坑说明
- 该方法会遍历目标GameObject上挂载的所有脚本,只要有同名public void方法就会被触发,容易误调用其他脚本的逻辑
- 老版本Unity每次调用都会产生大量GC,高频调用时会造成明显卡顿
- 默认找不到方法时不会报错,容易出现逻辑静默失效的问题
核心逻辑说明
所有方案的本质都是提前建立字符串到可执行逻辑的映射表,完全不需要在调用逻辑里写if(methodName == "xxx")的分支判断,从根源上替代上百条if语句的实现。
内容的提问来源于stack exchange,提问作者A Fish
相关产品推荐
相关产品推荐

