实现一个简易的Unity网络同步引擎Netgo
实现一个简易的Unity网络同步引擎Netgo
目前GOLANG有大行其道的趋势,尤其是在网络编程方面。因为和c/c++比较起来,虽然GC占用了一部分机器性能,但是出错概率小了,开发效率大大提升,而且应用其原生支持的协程很容易就能开发出高并发的服务端程序。笔者接触VR行业两年有余,接触了一些商业unity网络引擎,总觉的用的东西都落伍了,于是自己写了一个简单的引擎。目前实现了的基本功能:
- 支持房间概念。
- 支持灵活的数据同步方式,包括帧同步和RPC。
- 支持自定义事件的发送。
也实现了一个简单的demo,同步效果见下图,后面会有更详细的介绍。
下面是一个简单的项目复盘。
数据通信格式
数据通信格式的定义是整个项目的基石。我们这里的客户端和服务端是跨平台,跨语言通信。因此要定义一种语言无关,平台无关并且简单易用,高效不费流量的数据格式。这里我们选用了Google的 Protobuf,详细介绍参考这篇帖子。
Protobuf的C#代码库有两种选择,一种是protobuf-net,一种是protobuf-csharp-port,前者的接口书写更加符合C# 语法规范,会让人看起来更舒服一些。如果需要跨平台的话,推荐使用后者,因为不同语言的接口书写比较类似,开发起来会更容易一些。看看原作者的回复。
定义proto文件
如何使用protobuf呢,首先要书写proto文件,定义自己的结构化数据,在netgo中,下面是netgo中定义的消息体的一部分:
enum CacheOptions{ AddToRoomCache = 0; RemoveFromRoomCache = 1; } message NGVector3{ float x = 1; float y = 2; float z = 3; } message NGQuaternion{ float x = 1; float y = 2; float z = 3; float w = 4; } message NGColor{ float r = 1; float g = 2; float b = 3; float a = 4; }
完整定义参考。
生成c#和golang API接口文件
更新好命名空间后,执行下面的命令生成API文件:
- golang
protoc --go_out=. *.proto
-
c#
protoc --csharp_out=. *.proto
服务端网络模型
一个Unity网络同步引擎的实现包括服务端和客户端两部分。Nego 是Unity网络同步引擎的服务端,使用golang实现,充分利用了它的原生协程来实现高并发。其网络模型基于gotcp来实现。
参考上图,netgo会为每个socket链接建立一个协程,一个socket协程内部建立三个协程:
- ReadLoop 用于从网络端读取数据并放入Channel中。
- HandleLoop 用于解析应用层数据并完成相应处理,并将处理后的数据通过Channel发送给WriteLoop。
- WriteLoop 负责将处理结果forward给其它客户端或者response给本客户端。
参考代码:
func (c *Conn) Do() { if !c.srv.callback.OnConnect(c) { return } asyncDo(c.handleLoop, c.srv.waitGroup) asyncDo(c.readLoop, c.srv.waitGroup) asyncDo(c.writeLoop, c.srv.waitGroup) }
客户端代码结构
写API基本上是面向用户编程,笔者以为,清晰的代码结构,好的命名方式能省掉大部分注释,代码写的乱只能靠注释来拯救,代码结构看下图:
按照命名空间,分为 Library,网络层和应用层(以后用户接口层会分出来).
相关概念
数据同步
这里的同步是指一个房间内的数据同步,一个房间内存在着来自网络上的多个终端用户,每个Client都会将房间内其它人的数据在本地做一个Clone,而数据同步是指将你自己的数据同步到其他Cient你自己的Clone上面,因此发送范围是其它用户都会接收。
数据同步分为一下两种:
- View Sync
View Sync是毫秒级别的数据同步。可用于虚拟角色动作同步。
- RPC
每次同步由用户手动触发。可用于换装等同步。
Custom Event
Custom Event不是向所有其它Client的Clone实体发送同步消息,而是向一个或者几个指定的Client发送消息。
接口介绍
房间相关接口
请求接口
//加入或者创建房间 public static void JoinOrCreateRoom(string roomid,uint maxnumber) //创建房间 public static void CreateRoom(string roomid, uint maxnumber) //加入房间 public static void JoinRoom(string roomid) //离开房间 public static void LeaveRoom()
回调接口
//创建房间成功 void OnGreatedRoom(); //创建房间失败 void OnGreateRoomFailed(string errmsg); //加入房间成功 void OnJoinedRoom(); //加入房间失败 void OnJoinRoomFailed(); //离开房间成功 void OnLeftRoom();
Player相关接口
//实例化一个物体 public static void Instantiate(string prefabname, Vector3 position, Quaternion rotation, uint[] viewids) //有其它用户进入房间 void OnOtherPlayerEnteredRoom(NGPlayer player); //有其它用户离开房间 void OnOtherPlayerLeftRoom(NGPlayer player);
CustomEvent接口
请求接口
//发送事件 public static void SendCustomEvent(uint eventid, uint[] targetpeerids, NGAny[] customdata)
回调接口
//接收事件 void OnCustomEvent(uint eventID, NGAny[] data);
View Sync
视图同步需要自己实现组件脚本,实现序列化反序列化接口,并且需要挂载到物体上:
public interface INGSerialize { void SerializeViewComponent(NGViewStream stream); void DeserializeViewComponent(NGViewStream stream); } public class CubeViewComponent : NGIncomingEvent, INGSerialize { public void SerializeViewComponent(NGViewStream stream) { stream.Send(this.transform.position); stream.Send(this.transform.rotation); } public void DeserializeViewComponent(NGViewStream stream) { mCorrentPosition = (NGVector3)stream.Receive(); mCorrentRotation = (NGQuaternion)stream.Receive(); } }
Clone实体接受数据反序列化后在Update中实时更新即可:
void Update() { if (!view.IsMine) { transform.position = mCorrentPosition;//Vector3.Lerp(transform.position, mCorrentPosition, Time.deltaTime * 5); transform.rotation = mCorrentRotation;//Quaternion.Lerp(transform.rotation, mCorrentRotation, Time.deltaTime * 5); } }
RPC
使用RPC需要在视图脚本中写一个RPC函数:
[NGRPCMethod] public void OnColor(NGAny[] c) { mMat.color = c[0].NgColor; }
调用下面的接口向其它Clone实体发送RPC调用:
public static void SendRPC(uint viewID, string methodname, RPCTarget target, params NGAny[] parameters)
有关RPC,View Sync和Custom Event 的详细使用方法 参考源码
Demo演示
服务端部署
Clone代码
git clone https://github.com/netgo-framework/netgo.git
安装依赖
go get -d ./...
更新监听IP
打开main.go
tcpAddr, err := net.ResolveTCPAddr("tcp", "192.168.0.104:8686")
启动服务
go run main.go
客户端编译安装
客户端支持windows/MacOS/Andorid/IOS多平台。下面在Android和MacOS上测试:
配置IP和端口
切换Android平台
编译生成APK
安装APK后的初始化界面如下:
功能测试
两个Client进入同一个房间,每个Client会实例化出来两个Cube,一个为本机实体(Mine Cube),一个为对方的实体(Clone Cube)。
View SYnc
点击按钮Move后,会通过视图同步的方式进行postion和rotation同步。也就是文章刚开始的动图展示的样子:
RPC
点击Mine Cube之后,Cube的颜色会发生变化,同时同步到别的机器上,这里的颜色同步是通过RPC来实现的。
Custom Event
点击Clone Cube之后,会向对方实体发送消息,效果是对方的Mine Cube Scale会增加。
Road Map
接下来考虑会加入或者需要优化的功能:
- [ ] 支持大厅功能
- [ ] 支持负载均衡
- [ ] 增加支持UDP等网络传输协议
- [ ] 增加支持json等多种数据编码格式
- [ ] View Sync数据传输优化
- [ ] .....
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Navicat 连接 RDS数据库的步骤
随着业务量逐渐增加,公司数据库压力也会越来增大,使用自己买的esc创建的mysql的话,还得考虑dba维护,也比较繁琐,说不定个人技术水平导致做的并不够好,这时阿里云RDS就该上场了。其实一般稍大的公司都不会在自己的线上ecs上搭建mysql,而是使用RDS云数据库,毕竟这是由阿里云最顶尖的dba团队进行维护的,而且还有自动备份功能,既省去了线上ecs的io消耗,又省去了很多繁琐的维护操作,当然还是看具体情况。小型初创公司也并没有必要去多花成本去使用RDS,因为没那个必要,自己的ECS上创建的mysql已经足够用了。 1、进入RDS实例后,设置阿里云RDS白名单(也就是允许什么ip地址访问你的RDS实例) 这里为了方便,设置成所有ip地址都可以访问 这里的图片比较长,右侧找【修改】 2、创建账号 点击账号管理,进行新建账号,我这里直接开通了最高权限账号,如果不开通最高权限账号,也可以创建普通账号,好处是易于管理,分配任何对某个数据库的增删改查权限只需要也只能在控制台里操作即可,不需要命令grant。 注:新开通rds实例默认是没有开通高权限账号的,但是创建新账号也是在账号管理一栏,点进...
- 下一篇
全面上云的第一家快递,果然是它!
目前申通订单平台、巴枪系统等核心业务系统已稳定运行在阿里云上,云上日处理订单量近3000万。 申通技术总架构师醉易表示,申通选择全面上云,不只是变革基础架构,更是变革研发模式。“从内部看,基于传统架构的数据孤岛效应明显,导致信息不能共享,业务模式创新受阻。从外部看,包裹流转如何借助数据技术和IoT等技术提升效率已经成为核心议题。” 举例来说,基于传统IOE架构构建的系统无法支撑业务高速增长后的数据量膨胀,受限于容量订单系统只能保留3-6个月的信息查询,且无法对历史包裹进行在线搜索,相关应用都会受阻。 上云过程中,申通对云服务供应商开展了梳理、设计、验证和切换等环节在内的全面选型考察。阿里云具备稳定的基础架构、强大的数据计算能力,且菜鸟网络的基础架构及物联网能力同样构建在阿里云上,申通最终选择全面迁移至阿里云。 目前,包括订单平台、巴枪系统等申通核心业务已经运行在阿里云上,日处理订单量近3000万。在刚刚过去的2019年双11,申通快递拿下了包括“揽收率、签收率、服务质量综合得分”等在内的多项服务指标行业,通达系第一。 除了业务上的价值,全面上云还推动了申通内部的技术体系创新。醉易表示:...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS关闭SELinux安全模块
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Red5直播服务器,属于Java语言的直播服务器