使用 C# Attributes,我将如何触发要在更改其分配字段时执行的函数

Using C# Attributes, how would I trigger functions to be executed on change of their assigned field

我目前正在为 Unity 开发一个网络库,因为集成的不是我们的游戏所需要的。 太多的抽象和太多的限制迫使我们想出了自己的解决方案。

整个通信发生在标准 .Net 套接字之上。 我们开发的系统主要是使用内部 classes 和结构构建的,实际上不需要进一步解释。

重要的是我们的ShatNetTransportLayerclass。 它是一个静态 class,内部使用两个缓冲区,一个用于接收数据,一个用于传输。 另一个更高级别 class 的工作方式与 "network gc" 非常相似。 一旦所有消息都已发送,消息就会被推送到发送列表中,该列表会定期检查和释放。这当然是简化的,实际上有针对不同频道等的确认检查

设置到此为止。 我需要知道的重要事情如下。

游戏中要联网的每个对象都将持有一个 ShatNetIdentity。 这是在服务器上注册网络对象并使它们在每个客户端保持同步的核心class。 虽然这还没有完全实现,但我们还有另一个问题需要克服。 计划是每个 ShatNetIdentity 都将成为联网游戏对象的核心组件,这样任何 ShatNetBehaviour 都可以通过游戏对象访问它。

ShatNetBehaviour 是一个 class 派生自 monobehaviour,它是 Unity 默认的 class 来处理场景元素。 它唯一添加的是获得与脚本附加到的对象相关联的 ShatNetIdentity 的可能性。

现在是关键部分:

由于我们团队中的开发人员从未接触过网络系统,因此我们需要 抽象 变量的同步,可能甚至远程方法调用。

在内部所有将被发送的是最大大小为 128 字节的消息。 其中一部分是 MessageType,一个 1 字节长的无符号整数,表示消息的类型,例如变量同步、消息调用、断开连接的玩家等等。 其次是由 2Byte 表示的发送者 ID。 休息就是信息本身。 涉及的内容更多,但这是核心。 这仍有待完全确定,但你明白了。 这些消息被写入缓冲区并在计划发送时发送或在接收事件发生时接收。

当我们使用 Unity 自己的 UNet 时,您可以将一个名为 SyncVar 的属性添加到 class 的任何字段,该字段是附加了 ShatNetIdentity 的对象的一部分. 如果值发生变化,还有一种方法可以定义回调函数。

参考:https://docs.unity3d.com/ScriptReference/Networking.SyncVarAttribute.html

示例:

//When this is not our client, this value will be updated automatically across the net
[SyncVar(hook="OnHealthChanged")]
int health;


void OnHealthChanged()
{
doStuff();
}

所以我的问题是,有什么办法可以自己实现吗?

我们想到的名称是 NetVar 和 NetHook。 要在网络上同步一个变量,只需写

[NetVar]
private int health;

void TakeDamage(int amount)
{
//Updates health for all clients
   health-=amount;
}

要做到这一点,需要做几件事。

  1. 生成新的 ShatNetIdentity 后,在服务器上注册它。
  2. 注册每个 class 附加到游戏对象的每个变量 该身份上的 ShatNetIdentity。 那样可能不可能,因为访问级别?在属性内使用 public 属性 ?
  3. 接收缓冲区填充后检查变量更新类型的消息,找到 ShatNetIdentity 和引用变量的索引。
  4. 在每个客户端上更新该变量

所以我需要得到提示的事情是

  1. 如何在 ShatNetIdentity 上引用私有变量(当然可以是任何 class)。支持的类型是基本类型,仅此而已,但必须有一个属性涵盖所有类型。不像 NetBool、NetInt...
  2. 如何使用属性引用回调函数并在字段值更改时触发它们。
  3. 我还可以使用哪些其他选项。

到目前为止我的想法: 为每个基本类型创建网络基本类型。 这看起来很难看,而且不会真正帮助维护简单的代码。

我希望我解释得足够好。需要什么细节就拍

非常感谢您的帮助。

编辑: 看起来 SynVar 只是要生成代码的标记。

http://blogs.unity3d.com/2014/05/29/unet-syncvar

如何做到这一点?

我相信,对于您的情况,您可以选择两条路线之一。广播式系统,或者注册式系统。

Broadcast 场景中,您的通信组件 (class) 可以声明如下内容:

public event Action<(string shatNetIdentity, MessageType messageType, string message)> Message;

然后在正确接收消息时调用:

Message?.Invoke((id, messageType, message));

接收对象然后必须确定消息是否是发给它的,如果是,它实际上是什么类型的消息以及如何处理它。

Register 场景中,您的对象可以注册到通信组件。你可以在你的通讯组件中有一个字典:

private Dicitionary<string, INetworkObject> Registrations;

public void Register(INetworkObject o)
{
  Registrations[o.ShatNetIdentity] = o;
}

private void ReceiveMessage()
{
  // receive and parse message.
  if ( Registrations.TryGet(message.Id, out INetworkObject o )
  {
    switch (message.messageType)
    {
      case MessageType.Health: 
      o.Health = message.Health;
      break;
      // etc..
    }
  }
}

其中 INetworkObject 类似于:

public interface INetworkObject
{
  string ShatNetIdentity {get; set;}
  float Health { get; set; }
  Vector3 Position {get; set;}
}

这里显然没有错误检查。如果是我,我可能会选择第二种情况。如果只有几个对象监听事件,第一个场景可能更容易实现,但如果有很多对象,则不是最佳方案,因为事件被广播到每个监听的对象。