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

C# Unity存储方法引用时丢失类实例状态的问题

问题描述

我已经实现了RPC方法的存储功能,但调用这些方法时,代码是在类的新实例上执行的,而非原对象本身。比如不管调用多少次SumanNumero方法,调用RPC_MetodoEjemplo始终输出0(变量x初始值是5)。

使用场景代码
public class Something: NetworkBehaviour
{
    int x = 5;
    // 无参数RPC示例
    [ClientRPC]
    public void RPC_MetodoEjemplo()
    {
        Debug.Log(x);
    }
    
    public void SumanNumero()
    {
        x += 10;
    }
}
我的实现方案
private List<RPCMethodInfo> clientRPCMethods = new List<RPCMethodInfo>();

public RPCManager()
{
    // 获取所有继承自NetworkBehaviour的类
    Type[] networkBehaviours = AppDomain.CurrentDomain.GetAssemblies()
        .SelectMany(x => x.GetTypes())
        .Where(x => typeof(NetworkBehaviour).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
        .ToArray();
    
    // 获取所有带有ClientRPC特性的方法
    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));
        }
    }
}

// 从客户端调用此方法执行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;
    }
}
问题根源与修正方案

问题核心

你的代码里犯了一个关键错误:在RPCManager构造函数中,你把Type类型对象直接传给了RPCMethodInfo的Target,而非NetworkBehaviour的实际运行时实例。调用Method.Invoke(method.Target, ...)时,.NET会自动为该Type创建新实例,这个新实例的字段x会被初始化为默认值0(而非你设定的5),所以每次调用RPC都会输出0。

修正步骤

1. 调整RPCMethodInfo结构

让它存储实际的组件实例,而非Type:

public class RPCMethodInfo
{
    public MethodInfo Method { get; }
    public NetworkBehaviour TargetInstance { get; }

    public RPCMethodInfo(MethodInfo method, NetworkBehaviour instance)
    {
        Method = method;
        TargetInstance = instance;
    }
}

2. 修改RPCManager逻辑

去掉构造函数中的Type扫描,改为让每个组件实例主动注册自己的RPC方法:

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

// 供NetworkBehaviour实例调用,注册自身的RPC方法
public void RegisterRPCInstance(NetworkBehaviour instance)
{
    MethodInfo[] methods = instance.GetType().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, instance));
    }
}

// 调用指定实例的RPC方法
public void CallRPC(string methodName, NetworkBehaviour targetInstance, object[] parameters)
{
    var methodInfo = clientRPCMethods.FirstOrDefault(m => 
        m.Method.Name == methodName && m.TargetInstance == targetInstance);
    
    if (methodInfo != null)
    {
        methodInfo.Method.Invoke(methodInfo.TargetInstance, parameters);
    }
}

3. 在组件中完成注册

在Something类的生命周期方法中注册自身:

public class Something: NetworkBehaviour
{
    int x = 5;
    [ClientRPC]
    public void RPC_MetodoEjemplo()
    {
        Debug.Log(x);
    }
    
    public void SumanNumero()
    {
        x += 10;
    }

    void Awake()
    {
        // 假设RPCManager是单例模式
        RPCManager.Instance.RegisterRPCInstance(this);
    }
}

额外说明

  • 如果需要跨实例通过方法名调用,建议添加实例唯一标识(比如对象ID)来区分不同的NetworkBehaviour实例,避免同名方法冲突。
  • 不要手动创建NetworkBehaviour实例,这类Unity组件必须由Unity初始化,手动实例化会导致字段初始化异常(比如x不会被设为5)。

内容的提问来源于stack exchange,提问作者7FULL

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:44:49