简单状态同步在Unity使用的学习小结
目录
- 一、技术概述
- 二、技术详述
- 客户端
- 服务端
- 三、学习问题总结与解决
- 服务端如何管理角色不同数据?
- 对优化的思考
- 四、总结
- 五、参考资料
一、技术概述
多人在线游戏需要同步玩家数据,许多公司都拥有至少一款此类游戏。对于状态同步,首先需要掌握两层的设计架构思想以及计算机网络的原理,其难点在于:1. 网络延迟。2. 数据量.3. 安全性。
状态同步和帧同步的不同这里不做额外说明,可在参考资料中了解。
二、技术详述
这里不涉及客户端的本地服务器和服务端的实现,只涉及对状态同步的学习小结,包括角色的添加/删除/更新,并省略Debug与校验等工作。
客户端
为了实现和使用状态同步,首先要应用ECS架构对实体进行设计,将数据与操作分离,并维护一个实体的管理器,方便对从服务端接收的实体数据进行更新,所学的客户端的一种实现如下:

- 在图中,客户端的服务监听服务端发送的实体数据,根据类型可分为实体的生成与销毁、实体更新两类操作。
- 当需要 增加 角色时,首先更新CharacterManager中维护的角色定义数据Dictionary(策划所配的表),
CharacterManager.AddCharacter():调用EntityManager添加新的游戏实体,Character character = new Character(cha); this.Characters[cha.Id] = character;
CharacterManager.AddCharacter():同时,需要在地图中生成对应的角色,通过GameObjectManager统一管理所有的游戏对象并根据预制体生成。由于层次结构的设计,Manager层只关心同层的其他Manager,而不直接与GameObjectManager(处于实体层)交互,通过事件进行解耦。EntityManager.Instance.AddEntity(character);
CharacterManager.AddCharacter():这里要注意的是,网络中传输整个表数据会浪费带宽,因此传输的时表内容的Id属性,客户端拥有角色定义,根据Id使用不同的角色信息,但我认为会遭到数据篡改,因此可能需要进行额外的安全性校验。if(OnCharacterEnter!=null) { // 定义的公共委托,GameObjectManager注册其生成方法 OnCharacterEnter(character); }
另外,玩家多次进入与离开可能会产生多次游戏对象的创建/删除工作,可以使用对象池优化。
具体何时调用增加/删除/更新操作,此处不做探讨。删除的思路和添加同理。 - 当只需要更新数据时,则不需要通过CharacterManager,可直接调用EntityManager,进而同步对应的游戏对象,因为不需要增加与删除操作,同时也与实体层进行解耦,具体流程和上述过程类似。
MapService:EntityManager.OnEntitySync:private void OnMapEntitySync(object sender,MapEntitySyncResponse response) { foreach(var entity in response.entitySyncs) { Managers.EntityManager.Instance.OnEntitySync(entity); } }if (notifiers.ContainsKey((int)data.Id)) { // 基于接口的观察者模式,调用字典中注册的处理者来更新数据 notifiers[entity.entityId].OnEntityChanged(entity); notifiers[entity.entityId].OnEntityEvent(data.Event); }
- 当需要 增加 角色时,首先更新CharacterManager中维护的角色定义数据Dictionary(策划所配的表),
服务端

服务端的同步工作,是保存客户端发来的状态信息并转发,所以实际操作和客户端类似,只不过不需要渲染出真正的游戏对象,所以只需要维护一份角色数据,在角色刚进入游戏时即可在CharacterManager中创建,只需要更新状态信息,并需要向其他客户端发送所更新的信息。
当收到客户端发送的添加角色请求,分为进入游戏与进入地图两个阶段,进入游戏时和客户端一样,交给CharacterManager-》EntityManager生成新的角色数据,并返回给客户端所生成的角色数据,保证一定的安全性,这里是独立的。
而进入游戏后,进入地图时,又会让MapService接收到信息,将character信息交给MapManager处理:
MapService:MapManager.Instance[request.mapId].CharacterEnter(sender, character);Map:
// 向Response添加需要增加的角色 message.Response.mapCharacterEnter.Characters.Add(character.Info); // 添加地图中的其他角色并向其他玩家告知角色增加 foreach (var kv in this.MapCharacters) { // 添加地图中的其他角色 message.Response.mapCharacterEnter.Characters.Add(kv.Value.character.Info); // 向这个角色告知角色添加至地图 this.SendCharacterEnterMap(kv.Value.connection, character.Info); }
当收到客户端发送的状态更新请求:
- MapService回调更新请求的角色
MapService:Map:void OnMapEntitySync(NetConnection<NetSession> sender, MapEntitySyncRequest request) { Character character=sender.Session.Character; // 更新对应地图Map中的信息 MapManager.Instance[character.Info.mapId].UpdateEntity(request.entitySync); }internal void UpdateEntity(NEntitySync entitySync) { // 遍历当前地图的角色 foreach (var character in this.MapCharacters) { // 如果是需要更新的角色则更新 if (character.Value.character.entityId == entitySync.Id) { // 更新位置、方向等 …… } else { // 如果是其他客户端的角色,则直接发送更新消息 MapService.Instance.SendEntityUpdate(character.Value.connection, entitySync); } } }
- MapService回调更新请求的角色
三、学习问题总结与解决
实现的同步功能比较基础,未进行难点的实现,故没有遇到较大困难。
服务端如何管理角色不同数据?
由于客户端所生成的角色定义是服务端传递的,故在角色刚进入游戏时,会依次发送两次请求
- 进入游戏请求
- 在这个请求处理时,服务器接收客户端传递的角色数据id,并在服务端维护一份对应的数据,而角色离开时又会从中移除,这份数据保存了角色的数据库信息、存档、道具等。
- 进入地图请求
- 根据目前所见,进入地图时,服务端将角色的同步数据交给另一条数据流处理,这条数据流将频繁地更新数据,所以独立于其他数据进行处理。
对优化的思考
- 优化网络传输
- 使用算法优化网络的传输效率,如对消息进行序列化与反序列化(protobuf)、减少字符串的传输等。
- 客户端实现状态的预测,根据上次状态和本次状态预测分析下一次的状态来减少延迟(所以怎么预测?)
四、总结
状态同步需要同步客户端的状态数据,如位置、状态、伤害等,其主要逻辑在服务端,但也有些状态同步基于客户端,比如小型的联机游戏。
状态同步虽然只是网络游戏中的一种同步方式,想要完全掌握还是很复杂,特别是要考虑网络的各种情况,需要一定的经验。而其他的同步方式比如帧同步、状态帧同步,似乎水更是深。作为一名客户端程序,也要明白其大概模型才能更好的和后端人员沟通。
五、参考资料
状态同步与帧同步
谷歌Protobuf通信详解
如有问题,欢迎指正!
相关推荐
unity帧同步技术demo
网络游戏的经典同步方式帧同步的实现demo。里面有两个untiy工程(c#),对应服务端和客户端代码。很不错的教程。可以了解实现原理。
Unity网络同步方案帧同步和状态同步
刚开始我们团队立项的休闲足球游戏,很多人应该都玩过之前的CoolGoal!- Soccer这个游戏,根据手势划线来想办法进球的一个游戏,有点像点球达人(去掉联网的部分)这样一款游戏,后来在老板的兴趣爱好下逐步扩大到多人足球模式,1v1、2v2、3v3、4v4、5v5、6v6等多人匹配模式。后面才有了我们选状态同步和帧同步方案。
unity帧同步物理碰撞
基于Unity的帧同步精确物理碰撞,可以模拟Unity客户端的精确的物理碰撞,保证客户端的的一致同步
Photon Unity Networking(PUN)的简单使用
Photon Unity Networking(PUN)的简单使用 博客原文http://blog.csdn.net/xiaoge132/article/details/65628220
简单的帧同步框架
unity简单的帧同步框架简单的帧


