存储对方法的引用而不丢失类更改 C# Unity

Store the reference to a method without losing classes changes C# Unity

提问人:7FULL 提问时间:9/25/2023 最后编辑:journpy7FULL 更新时间:9/26/2023 访问量:34

问:

基本上,我已经实现了存储方法,但是当我调用它们时,它们就像在调用对象的新实例而不是对象本身一样。

当我调用该方法时,它只打印 0

这就是我使用它的地方。我执行 SumanNumero 多少次并不重要。当我调用RPC_MetodoEjemplo时,我只得到 0(变量初始化为 5)

public class Something: NetworkBehaviour
    int x = 5;
    // Esto es un ejemplo de un rpc sin parámetros
    [ClientRPC]
    public void RPC_MetodoEjemplo()
    {
        Debug.Log(x);
    }
    
    public void SumanNumero()
    {
        x += 10;
    }
}

这是我的方法:

private List<RPCMethodInfo> clientRPCMethods = new List<RPCMethodInfo>();

public RPCManager()
{
    // We obtain all the classes that inherits from NetworkBehaviour
    Type[] networkBehaviours = AppDomain.CurrentDomain.GetAssemblies()
        .SelectMany(x => x.GetTypes())
        .Where(x => typeof(NetworkBehaviour).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
        .ToArray();
    
    // We obtain all the methods that have the ClientRPC attribute
    foreach (Type networkBehaviour in networkBehaviours)
    {
        MethodInfo[] methods = networkBehaviour.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
            .Where(x => x.GetCustomAttributes(typeof(ClientRPCAttribute), false).Length > 0)
            .ToArray();

        foreach (MethodInfo method in methods)
        {
            clientRPCMethods.Add(new RPCMethodInfo(method, networkBehaviour));
        }
    }
}

// Llama a este método desde el cliente para ejecutar un RPC
public void CallRPC(string methodName, object[] parameters)
{
    foreach (var method in clientRPCMethods)
    {
        if (method.Method.Name == methodName)
        {
            method.Method.Invoke(method.Target, parameters);
            return;
        }
    }
}


public class RPCMethodInfo
{
    public MethodInfo Method { get; }
    public object Target { get; }

    public RPCMethodInfo(MethodInfo method, object target)
    {
        Method = method;
        Target = target;
    }
}
C# Unity-Game-Engine 方法 存储数据

评论

0赞 MakePeaceGreatAgain 9/25/2023
您需要该类型的实例才能调用其任何实例成员(例如 -function)。但你没有创造任何东西。SumanNumero
0赞 7FULL 9/25/2023
问题是,实现该方法的类可能有很多不同的类。我只想将 [ClientRPC] 放在任何函数中并从我的 RPCManager 调用它
0赞 derHugo 9/25/2023
您正在将 TYPE 传入为......它应该是该类型的实际目标实例......网络不是那么简单;)您需要处理网络标识,并在每个客户端上唯一标识 RPC 调用的确切实例Target
0赞 7FULL 9/25/2023
是的,但它可以是我项目中的任何类,我只是在任何 class(){ } 中使用 ' [ClientRPC] 我想要的任何函数
0赞 derHugo 9/25/2023
是的。。。你可以这样做,但这根本不是它的工作方式^^ 在(已弃用)UNet、镜像、光子、网络代码中。你的名字..除了存储对这些方法的引用之外,还有更多的事情发生......相信我,我已经用UDP / TCP套接字编写了自己的本地网络...它并不像你现在想象的那么微不足道

答:

1赞 7FULL 9/25/2023 #1

多亏了@derHugo我做了一些事情。

它不是那么有效,但它在游戏开始时只被调用了 1 次,所以还不错。(可能会更好,可能当我完成项目时我会重构它)

就是这样:

using UnityEngine;
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;

public class RPCManager
{
    private Dictionary<GameObject, List<RPCInfo>> methods = new Dictionary<GameObject, List<RPCInfo>>();
    public RPCManager()
    {
        GameObject[] objetosEnEscena = GameObject.FindObjectsOfType<GameObject>();
        
        foreach (GameObject objeto in objetosEnEscena)
        {
            NetworkBehaviour[] scripts = objeto.GetComponents<NetworkBehaviour>();
            
            foreach (NetworkBehaviour script in scripts)
            {
                MethodInfo[] metodos = script.GetType().GetMethods();
                
                List<RPCInfo> metodosConRPC = new List<RPCInfo>();
                
                foreach (MethodInfo metodo in metodos)
                {
                    if (metodo.GetCustomAttributes(typeof(ClientRPCAttribute), true).Length > 0)
                    {
                        metodosConRPC.Add(new RPCInfo(metodo, script));
                    }
                }

                methods.Add(objeto, metodosConRPC.ToList());
            }
        }
    }
    
    // This is to add a new RPC method to the list
    public void AddRPC(GameObject gameObject, MethodInfo methodInfo)
    {
        if (methods.ContainsKey(gameObject))
        {
            methods[gameObject].Add(new RPCInfo(methodInfo, gameObject.GetComponent<NetworkBehaviour>()));
        }
        else
        {
            methods.Add(gameObject, new List<RPCInfo> {new RPCInfo(methodInfo, gameObject.GetComponent<NetworkBehaviour>())});
        }
    }
    
    // Call RPC
    public void CallRPC(string methodName, object[] parameters)
    {
        foreach (KeyValuePair<GameObject, List<RPCInfo>> pair in methods)
        {
            foreach (RPCInfo rpcInfo in pair.Value)
            {
                //Debug.Log(rpcInfo.toTExt());
                if (rpcInfo.methodInfo.Name == methodName)
                {
                    rpcInfo.methodInfo.Invoke(rpcInfo.target, parameters);
                }
            }
        }
    }
    
    public List<Type> FindNetworkBehaviourTypes()
    {
        Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

        List<Type> networkBehaviourTypes = new List<Type>();

        foreach (Assembly assembly in assemblies)
        {
            Type[] types = assembly.GetTypes();
            networkBehaviourTypes.AddRange(types.Where(t => t.IsSubclassOf(typeof(NetworkBehaviour))));
        }

        return networkBehaviourTypes;
    }
    
    public List<GameObject> FindGameObjectsWithNetworkBehaviour()
    {
        List<Type> networkBehaviourTypes = FindNetworkBehaviourTypes();
        List<GameObject> gameObjectsWithNetworkBehaviour = new List<GameObject>();

        foreach (GameObject go in GameObject.FindObjectsOfType<GameObject>())
        {
            NetworkBehaviour[] networkBehaviours = go.GetComponents<NetworkBehaviour>();

            foreach (NetworkBehaviour nb in networkBehaviours)
            {
                if (networkBehaviourTypes.Contains(nb.GetType()))
                {
                    gameObjectsWithNetworkBehaviour.Add(go);
                    break; // No need to check this GameObject further
                }
            }
        }

        return gameObjectsWithNetworkBehaviour;
    }

    public class RPCInfo
    {
        public MethodInfo methodInfo;
        public object target;
        
        public RPCInfo(MethodInfo methodInfo, object target)
        {
            this.methodInfo = methodInfo;
            this.target = target;
        }
        
        // We use this method instead of ToString() because ToString() can only be called from the main thread
        // and this is executing in a background thread
        public string toTExt()
        {
            return "RPCInfo: " + methodInfo.Name + " " + target;
        }
    }
}
`