Unity通用函数实现诉求:无需引用即可调用自定义函数
实现类似Unity内置生命周期函数的自动调用机制
方案1:自定义特性 + 运行时扫描(优化版)
这个方案比全脚本遍历更高效,只扫描带标记的方法,无需用户手动订阅。
步骤1:定义自定义特性
创建特性类标记需自动调用的方法:
using System; [AttributeUsage(AttributeTargets.Method)] public class NetworkCallbackAttribute : Attribute { }
步骤2:创建管理类扫描并缓存方法
写一个单例管理类,启动时扫描所有挂载的MonoBehaviour,缓存带特性的方法:
using System; using System.Collections.Generic; using System.Reflection; using UnityEngine; public class NetworkCallbackManager : MonoBehaviour { private static NetworkCallbackManager _instance; private Dictionary<string, List<Action>> _callbackMap = new Dictionary<string, List<Action>>(); private void Awake() { if (_instance != null && _instance != this) { Destroy(gameObject); return; } _instance = this; DontDestroyOnLoad(gameObject); ScanAllCallbacks(); } private void ScanAllCallbacks() { MonoBehaviour[] allScripts = FindObjectsOfType<MonoBehaviour>(); foreach (var script in allScripts) { Type scriptType = script.GetType(); MethodInfo[] methods = scriptType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (var method in methods) { if (method.GetCustomAttribute<NetworkCallbackAttribute>() != null) { if (method.GetParameters().Length == 0 && method.ReturnType == typeof(void)) { string methodName = method.Name; if (!_callbackMap.ContainsKey(methodName)) { _callbackMap[methodName] = new List<Action>(); } _callbackMap[methodName].Add(() => method.Invoke(script, null)); } else { Debug.LogWarning($"方法 {method.Name} 标记了NetworkCallback,但签名不符合(必须是无参void方法)"); } } } } } public static void TriggerCallback(string methodName) { if (_instance?._callbackMap.TryGetValue(methodName, out var actions)) { foreach (var action in actions) { try { action.Invoke(); } catch (Exception e) { Debug.LogError($"调用回调 {methodName} 时出错: {e.Message}"); } } } } public static void RegisterScript(MonoBehaviour script) { if (_instance == null) return; Type scriptType = script.GetType(); MethodInfo[] methods = scriptType.GetMethods(BindingFlags.Instance | BindingFlags.Publicublic | BindingFlags.NonPublic); foreach (var method in methods) { if (method.GetCustomAttribute<NetworkCallbackAttribute>() != null) { if (method.GetParameters().Length == 0 && method.ReturnType == typeof(void)) { string methodName = method.Name; if (!_instance._callbackMap.ContainsKey(methodName)) { _instance._callbackMap[methodName] = new List<Action>(); } _instance._callbackMap[methodName].Add(() => method.Invoke(script, null)); } } } } }
步骤3:用户侧使用方法
用户只需给目标方法加特性,无需额外操作:
using UnityEngine; public class PlayerUI : MonoBehaviour { [NetworkCallback] private void ClientDisconnected() { Debug.Log("客户端已断开,更新UI"); } }
步骤4:触发回调
需要调用所有ClientDisconnected方法时,执行:
NetworkCallbackManager.TriggerCallback("ClientDisconnected");
方案2:利用Unity内置SendMessage(快速但效率较低)
追求极致简单的话,可用Unity原生SendMessage机制,无需额外框架代码:
用户侧代码
直接定义方法(支持private):
public class PlayerManager : MonoBehaviour { private void ClientDisconnected() { Debug.Log("玩家管理器处理断开逻辑"); } }
触发方式
在网络系统事件发生时,给所有GameObject发消息:
GameObject[] allObjects = FindObjectsOfType<GameObject>(); foreach (var obj in allObjects) { obj.SendMessage("ClientDisconnected", SendMessageOptions.DontRequireReceiver); }
注:
SendMessageOptions.DontRequireReceiver会忽略无对应方法的对象,避免报错。
方案对比
- 方案1:效率更高,支持缓存和运行时动态注册,方法可设为private,还能做参数校验,适合中大型项目。
- 方案2:零框架代码,用户只需写方法名匹配的函数,适合小型快速开发场景,但遍历所有GameObject效率略低。
内容的提问来源于stack exchange,提问作者The Epic Bacon
相关产品推荐
相关产品推荐

