检测属性中的更改而不更改其源代码

时间:2017-06-18 13:00:07

标签: c# components entity

所以情况是我正在制作一个控制网络的游戏的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属性的值的副本,但我不知道如何使用反射执行此操作我目前正在尝试将其转换为我可以循环遍历所有实体组件的一个点,如果它们具有该属性,那么我将检查它的ageist一个巨大的表(字典),其中包含属性实例的某种标识符,然后保存最后一个的副本价值属性是,然后它会去,如果匹配它不会做任何事情,如果他们不匹配,那么我将标记另一个bools表索引与我使用的标识符(我还不知道如何获得)这个会告诉我什么是脏的,需要发送给客户。

1 个答案:

答案 0 :(得分:1)

简化:您尝试做的是"举办活动" (值)当值发生变化时,不修改组件本身。您有两个(简单)选项:

  1. 提高写作。例如每次写入时,写入调用者在写入时设置脏标志/添加到列表等。构建一个写作工具。也许是交易系统等。
  2. 天真地检测更改。创建一个存储最后一个值并执行diff的包装器,并自动引发事件。
  3. 使用面向数据的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实体组件系统。将所有数据保存为一种格式,应用有关如何读/写的规则。网络化变得更加微不足道。

    属性和反思&#34;魔术&#34; (也就是使代码工作的未知事物)是一种难以使用的东西,并且通常对性能和内存非常不利。保持简单,构建一个for循环,将来自BitArray的更改聚合为一种策略。