You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 22:50:14