【问题标题】:Detect Changes in property without changing its source code在不更改其源代码的情况下检测属性更改
【发布时间】:2017-11-20 18:23:18
【问题描述】:

所以情况是我正在为控制网络的游戏制作 api,并降低所需的带宽,我需要知道哪些属性会发生变化。我正在使用实体组件系统,因此从事游戏玩法的程序员会编写这样的代码

public class PosComponent{
    public int x {get; set;}
    public int y {get; set;}
}

所以我的目标是,让程序员标记他们想要同步的内容以及应该如何同步,此时网络系统将接管并处理检测更改并将数据实际发送到客户端以及其他内容。所以程序员会做这样的事情。

public class PosComponent{
    [KeepSynced(Reliability.Loose, SyncDirection.ToClient)]
    public int x {get; set;}

    [KeepSynced(Reliability.Loose, SyncDirection.ToClient)]
    public int y {get; set;}
}

我发现的所有类似问题都是人们说的好,只需将代码添加到属性中即可。我不喜欢这个解决方案,因为它会使程序员添加一堆代码来跟踪他们自己的更改,我不希望这样,因为它增加了引入非常难以追踪的错误的能力,而且还可以在另一部分处理它该程序让我可以优化所有内容,而无需重构大量代码。

所以我不想只做类似的事情:

public class PosComponent{
    public int _x;
    public bool _xChange;
    [KeepSynced(Reliability.Loose, SyncDirection.ToClient, ref _xChange)]
    public int x {get{return _x;} set{
        if(value!=_x) _yChange = true;
        _x=value;
    }}

    public int _y;
    public bool _yChange;
    [KeepSynced(Reliability.Loose, SyncDirection.ToClient, ref _yChange)]
    public int y {get{return _y;} set{
        if(value!=_y) _yChange = true;
        _y=value;
    }}
}

现在这并不是说如果有人可以告诉我如何将一些代码注入到属性或其他东西中,我反对将其作为有效代码,但主要是我想在源代码中保持那个漂亮干净的简单签名超级易读的代码。

我已经接受了这样一个事实,即我可能只需要保留所有具有 KeepSynced 属性的值的副本,但我不确定如何通过反射来做到这一点,我目前正试图让它达到一个点我可以遍历所有实体组件,如果它们具有属性,那么我将检查它是一个巨大的表(字典),其中包含属性实例的某种标识符,然后保存最后一个值的副本属性是,然后它会去,如果匹配它不会做任何事情,如果它们不匹配,那么我将标记另一个布尔表,用我使用的任何标识符(我还不知道如何获取)索引这会告诉我什么是脏的,需要发送给客户。

【问题讨论】:

  • 不确定现在要调查它@mjwills
  • 这可能会有所帮助,但我看到的主要问题是涉及操作程序集的构建步骤,我希望最好远离它。
  • 主要原因是它的另一个构建步骤,如果它出现问题,那么我不知道如何调试它,因为我不知道它是如何工作的,而且组件被回收所以我需要以某种方式清除所有听众。此外,它是一个实体组件系统,该事件将通过对该组件的引用来调用,因此我必须查找它所属的实体,如果我每次都在任何地方发生任何更改时都这样做,我认为会变得非常昂贵。 ..

标签: c# components entity


【解决方案1】:

为了简化:您尝试做的是在值更改时“引发事件”(某种意义上),而不修改组件本身。您有两个(简单)选项:

  1. 在写作时提出。每次写入时,在写入调用者的写入时设置脏标志/添加到列表等。构建一个写作工具。也许是交易系统等。
  2. 天真地检测更改。 创建一个包装器,用于存储最后一个值并执行差异,并引发事件本身。

使用面向数据的 ECS,您通常将组件存储为集合,这使得这两个选项都变得非常简单。如果我是你,我会让写入组件自动设置一个标志。例如。 (简体)

// Example ECS component storage. Simple, and pretty damn fast.
public class ComponentStorage<T> where T : struct
{
    private T[] m_data;
    public BitArray m_replacedThisFrame;

    ...

    // Ideally, we'd inline this. 
    public void ReplaceComponent(int entityId, T newVal)
    {
        if(m_data[entityId] == newVal) return; 

        m_data[entityId] = newVal;
        m_replacedThisFrame[entityId] = true;
    }
}

// Use case.
componentStorageInstance.ReplaceComponent(entityId, new PosComponent { ... });

m_replacedThisFrame 有效地为您提供了此框架中所有已更改实体的查询。使用您想要的任何策略迭代集合以生成可序列化(可联网)的更改集合。

就个人而言,对于网络代码,我强烈建议您研究 PURE 实体组件系统。将所有数据保存在一种格式中,应用规则来读取/写入它们。网络变得更加琐碎。

属性和反射“魔法”(也就是使代码工作的未知东西)是一种痛苦,通常对性能和内存非常不利。保持简单,构建一个 for 循环,将 BitArray 的更改聚合为一种策略。

【讨论】:

    猜你喜欢
    • 2018-05-09
    • 2020-11-24
    • 2015-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-28
    相关资源
    最近更新 更多