ECS方式实现帧同步

0x00. 引言

ECS是Entity-Component-System(实体-组件-系统) 的缩写,是一种很是好用的框架思想,能够提升代码复用率,游戏逻辑开发中使用这种组合因为继承的方式能够很大程度上简化复杂度,并且在性能上也是有很大提高的。Entiy是一个包含惟一ID的容器对象,在Entity内部能够绑定不少Component,每一个Component只负责存储数据,正是由于Component的功能单一性,它能够很方便的作数据快照。System内部负责操做Component数据,从而最终做用于Entity。
 
帧同步的应用场景不少, (帧同步与状态同步的区别,感谢知乎做者云影)从格斗游戏,Rts,到如今的几乎全部Moba类游戏,他们各自的实现方式细节各不相同,帧同步对不一样类型的游戏也有各类变种。列举一下例子,如格斗游戏,因为参与角色很少,玩家对角色操做反馈及时性要求很高,所以这种帧同步须要有预测能力,在预测对方玩家行为以后还须要有回退矫正的能力。有预测能力意味着操做反馈会很流畅,也是因为角色很少,出现预测错误的概率也很少回退矫正形成的视觉影响也不会很大,所以格斗游戏的帧同步就要采用这种方式定制。再好比魔兽争霸游戏,这种早期局域网环境下的对局方式,网络的环境比较好,各个客户端采用的是锁帧方式的帧同步,若是其中一个客户端出现小延迟状况,客户端本身会迅速根据关键帧标记跟上服务器的步伐,若是出现大延迟甚至断线,那么全部的客户端须要一块儿暂停等待,所以这种方式对于多控制单位的游戏类型很好用也很简单。
 

0x01.开始结合 

准备将帧同步与ECS合起来工做,帧同步逻辑实现的第一步是要将逻辑和显示完全的分离,能够把不少逻辑放到线程里去,这点也是ECS能够带来的便利。
首先须要实现一个模拟器,这部分的代码功能主要是实现一个stopwatch的机制,在线程中使用stopwatch记录时间流逝,在设定逻辑帧帧率参数一致下同时启动模拟器保证在通过相同的一段时间内全部的模拟器执行的帧数同样。模拟器还须要有添加行为的功能,各类行为以互相排列组合的方式共同协助模拟器完成整个模拟过程, 下图能够看到一个模拟器中每一帧须要依次执行下列几个模块。
#逻辑帧记录:主要负责记录当前的逻辑帧编号;
#网络数据回滚:负责收取服务器广播的带帧编号的网络数据,这部分也是帧同步的核心,收到的逻辑帧数据须要和本地的数据进行回滚并向后继续演算,而且记录下操做以便播放战报;
#实体操做:在这里会是全部ECS中System的执行入口,如MoveSystem等;
#用户输入:完成监听并记录用户操做,用于最后一步的请求同步;
#数据备份:对当前帧的全部数据进行快照保存,以便用户回滚;
#请求同步:将操做发送给服务器;
整个顺序过程会在一个逻辑帧内执行,由于上述全部逻辑都放置与线程中执行,因此在显示方面和这部分逻辑是没有调用关系的,所以须要在主线程中对于当前的数据作出响应,这就是一个数据驱动显示的过程。这样作的好处有不少,首先就是逻辑显示分离后服务器也能够执行逻辑代码进行验证,其次线程里执行的逻辑运算功能能够分担主线程压力。
 
上述的模块还能够继续扩展,好比能够在用户输入模块后继续加上AI输入模块,在实体操做模块中添加子系统,子系统以ECS中的System形式出现,负责根据需求改变数据。
 
关于ECS的实现能够有不少种,具体状况因地制宜,固然也能够本身DIY一下。我在这里把ECS扩展成ECSR,多出来的R表示Render,Render会根据Component数据内容用于主线程画面的表现。

0x02. 扩展一下

上述完成了对帧同步模拟过程,还有一项帧同步特有的功能就是战报回放,这是状态同步开发下不管如何也完成不了的。战报回放的replay文件能够以不一样速率完整的模拟出整个帧同步过程,这依赖于逻辑帧命令的记录和保存。一样是用模拟器去完成只是在模拟器中添加不一样的模块。
#逻辑帧记录:这里和上述的稍做改变,做用类似,也是记录逻辑帧编号;
#实体操做:同上
#回放输入:根据回放的replay文件,读取逻辑帧命令;
 

0x03. GIF效果展现

效果展现为多用户移动命令下的帧同步效果。
 

0x04. 后续

关于帧同步的网络通讯方面的选择,最好仍是使用UDP方式,采用发送冗余信息,外加丢帧请求的方式。我这里为了简单实现同步的过程采用的是TCP方式。后续的修改应该是往UDP方式改进。
另外帧同步中的数学运算官方提供了 Unity.Mathematics数学库 。
相关文章
相关标签/搜索