天天动画片 > 八卦谈 > UNITY 网络游戏开发

UNITY 网络游戏开发

八卦谈 佚名 2023-05-01 09:18:45


本期内容:unity网络游戏开发。

关键字:操作同步,防外挂,断线重连,弱网设计。

课程目标:为单机游戏,增加网络对战功能。

学完本课程之后,收获网游开发的核心技术。


## 本期内容建议结合视频观看

视频观看地址:[点击进入]

之前的教程,讲解了从零开发战棋游戏(单机)

(基于AssetBundle)资源管理和更新

(基于ILRUNTIME)C#代码热更


大家也对之前的课程作出了大力的支持

比如**点赞**,**评论,收藏,转发**


正因如此:本期视频诞生了


从单机到网络,最核心的两步

1:增加网络通信模块

2:设计同步方案


网络框架用ET6,原因:

知名度高,上线项目做过验证,C#语言开发


支持分布式部署

因为

单台电脑无论性能再高,CPU和内存都有上限

所以

分布式可以让多台电脑协同工作,从而突破硬件上限

容纳更多人数


服务器采用C#编程

客户端的代码可被后端重用,因此减少了后端的开发时间


ET6有着很多功能和特色


课程的侧重点在于网络同步和设计

在这里只是讲需要使用到的部分

比如:(网络协议生成,网络通信模块)


关于ET6更多的内置功能

大家可以进入ET官网,学习和查阅

[https://et-framework.cn/](https://et-framework.cn/)





目录:


## 1:ET的搭建部署


 参照ET-master\Book\1.1运行指南.md 

演讲视频z01(2021-11-13制作完成)

 


## 2:前后端相互发送“HelloWorld”

消息的发送,请求,广播

    演讲视频z02(2021-11-13制作完成)


前端向后端发送消息"helloWorld"

protobuf协议生成

调用session.send接口


后端收到消息后显示在控制台

配置Nlog.config


前端向后端 请求 回复


后端向前端发送消息"helloWorld"



## 3:分布式


分布式的应用非常广泛,比如

1:让多台电脑协同工作,突破硬件性能上限

2:单个服务器损坏时,备用服务器进行支援

3:根据玩家的地区和延时,选择网速最优的服务器等等


功能模块的分布,本质上是为了更好的运营和管理


本节通过制作聊天功能,学习ET6分布式的开发流程


划分聊天模块,编辑ET-master/Excel/StartSceneConfig.xlsx

SceneType.cs增加聊天服枚举 Chat

SceneFactory.cs增加聊天服所需组件UnitComponent



登录聊天服

发送聊天信息

广播聊天信息


分布式处理业务消息的模板


## 4:整合战棋客户端和ET6


之前我们已经用U3D2019完成了战棋游戏的客户端框架

但是

因为ET运行指南第2条明确表示需要U3D2020版本

所以

客户端进行了2019->2020版本升级,

大部分是编辑器工具的API需要升级

(比如A星地图编辑器)


ET6的分支版本选择

因为需要用到c#热更,用到的是ET6的支持ILRUNTIME版本

[https://www.lfzxb.top/et-6-with-ilruntime/](https://www.lfzxb.top/et-6-with-ilruntime/)

并且按照ET6的框架要求,对项目重新进行了目录调整

(详见本文的客户端环境搭建)


这次增加PVP模式的实现策略是设计模式中的 开闭原则

少修改或不做修改的情况下实现需求

这样做的好处是新需求实现时不会影响到以前的功能


框架的初始化的拓展:

ET的启用入口在Init.cs脚本

所以增加 InitSlg.cs,增加了战棋框架必要的初始化方法

比如C#热更ILRUNTIME框架需要用到的语法适配


开发效率:

InitSlg下ilruntimeMode勾选策略,对开发效率很重要

开发模式不要勾选,有利于开发效率,错误信息的查看

发布移动平台时才勾选



## 5:客户端对战系统的协议处理


第一季的开发过程中,我们已经熟练的掌握了战棋游戏的业务逻辑

要加入网络对战,虽然有很多事情需要解决

但只要选择关键点切入,很多事情都会水到渠成


协议就是切入开发的关键点

第一步:定制协议

战斗场景生成,移动,攻击,技能,行动顺序。

战斗场景生成 包含 初始化信息,比如人物位置,技能,属性,门派等

移动 包含 起始id , 终点id , 2个字段

攻击 包含 起始id , 终点id , 目标id,3个字段

技能 包含 起始id ,终点id , 目标id, 技能id,4个字段

行动顺序 包含 门派信息


第二步实现协议的处理;

为了提高开发效率,客户端先本做好本地测试,然后才接入服务端。

这样就不用为了测试单个功能反复打开服务端。


详见视频.......


## 6:后端对接-跑通对战流程


服务器配置了比赛服,比赛对战的相关业务由它负责

这就意味了比赛服需要进行战斗计算得出结果

用于处理客户端的断线重连,防作弊,胜负判断等


战斗功能都在客户端里写好了,只要把相关的代码 引用,或者复制到服务器,稍作修改即可。

虽然服务器和客户端都是C#语言,但是对于服务器来说:对战过程是不需要动画效果的

所以只要把动画效果相关的代码处理掉即可

方法一: #if !SERVER  利用宏剔除服务器不需要的代码

比如我们的技能系统,原封不动的引用了客户端的代码文件

有蓝色箭头的C#文件,代表文件引用


方法二:代码拷贝到服务器后,删除掉不必要动画代码

没有蓝色箭头的C#文件,则是修改

因为通过宏来区分代码,在模块比较复杂的时候代码的阅读性会下降

开发者根据项目的具体情况做出选择即可


服务器解决了战斗模块之后,

再结合第3课的聊天服务的案例,

同步方案的到这里已经成形了

剩下的问题,只是工作量


最终设计整体流程


## 7: 登录超时和断线重连


客户端处理 链接错误和链接超时,断线重连


断线模拟工具

模拟现实中,坐在车上进入信号不好的隧道,30秒后恢复了网络。


怎么做呢?

首先找到当前客户端连接的网关端口

要么10003,要么10004

或者,用客户端打印出连接的端口号

用弱网工具把网关的收发数据强行丢包,在这期间服务器和客户端是无法进行通信的

30秒后,客户端出现掉线重连界面,关闭丢包测试。

客户端会重新连接到服务器,重新同步状态。


阻断10004端口的消息


tcp.DstPort == 10004 or tcp.SrcPort == 10004


阻断10003和10004端口

(tcp.DstPort <=10003 or tcp.SrcPort == 10003) 

or

 (tcp.DstPort <=10004 or tcp.SrcPort == 10004)


## 9:弱网测试


两个真实客户端的弱网环境测试


网络传输协议一般有两种,可靠的(tcp)和不可靠的(upd)

使用不同的协议,客户端处理细节的方式也会有所不同


upd在一些比较老的帧同步项目会比较常见,需要处理丢包和顺序打乱的问题

ET6使用的是KCP协议,可靠性传输,会确保消息不丢失,顺序不会被打乱


测试:


为了快速测试功能,我们先关闭第二季的资源热更框架

模式下的打包设置

勾选Main场景Init Slg 的ab包只通过streaming 模式加载


打开2个客户端,弱网测试截断


通过丢包测试可以得出结论,KCP协议丢包之后会进行消息重发,确保客户端收到



## 安全和防外挂设计


因为我们的设计是操作同步,大部分的计算都在客户端里

假如玩家使用外挂修改客户端的数值,怎么应对呢?


客户端修改了攻击力

服务器和客户端对比关键数值

只要不一致,客户端同步服务器的数值

客户端修改可控单位:

服务器先做逻辑判断再处理



修改了移动力:技能范围

因为计算过程涉及了A星算法,想要安全验证,就必须实现服务器A星的算法

因为这已经是服务器开发的进阶部分了,已经超出课程的讨论范围了


## 安卓发布,真机测试


测试内容, 移动平台,热更,网络

1:修改服务器的IP为局域网或者外部的地址

配置文件Excel/StartMachineConfig


2:生成代码和素材资源

 U3D 顶部菜单-Window->AssetBundle Browser->选择安卓平台->Build

  U3D 顶部菜单->资源部署->生成资源清单


生成的资源在../AssetBundle/Android


3:构建资源服务

拷贝AssetBundle/Android目录到 Nginx/html

(Nginx软件,可以搭建HTTP服务器)

cmd->运行Nginx



4:配置客户端

修改登录服务IP

把生成的全部资源 拷贝到StreamingAssets

(如需灵活控制首包体积,或加密资源,详见第二季内容)


3:发布安卓平台


测试模式,使用mono,不勾选IlruntimeMode,打包时间快

发布模式,根据平台要求勾 是否使用IL2CPP,IlruntimeMode,打包时间慢3倍

比如上线谷歌平台,因为平台政策原因,只能是用64位+IL2CPP

假如是ILRUNTIME模式,需要先成一次CLR绑定,

可以避开运行时的一些错误

最终可以通过网络进行对战



## 第四季:视频讲解**完**


下面是备课阶段整理的资料,可以选择观看


## 客户端环境搭建


热更程序集的生成和《u3d 代码热更解决方案 ILruntime》有所不同


天地劫项目的热更代码是用VS解决方案发布的,和U3D解决方案分离(2个解决方案)


ET框架把Asset下的若干个目录设置为程序集,热更代码发布是由脚本控制(1个解决方案)


ET6热更程序集工作流 :

优点

分散的目录设置为程序集,**提升U3D编译速度**

**方便编辑器插件的开发**(热更和主工程的程序集在编辑器下都可以被U3D编辑器加载)


缺点:

新手会经常遇到代码提示缺少引用,是因为引用关系没设置好。

所以需要我们熟悉项目的代码,才能正确设置每个程序集之间的引用关系。


分离解决方案的工作流

优缺点和ET形成反差


最终为了能够兼容ET6的开发流程


 战旗代码的目录重新改动,便于适应ET6的开发工作流


所以我们要把战旗的代码目录设置为不排除(去掉目录名字的~)


并且按照ET框架进行调整目录(插件放到第三方目录,编辑相关的目录放到Editor下)


IL2CPP发布时,最好先用ILRUNTIME的 生成CRL绑定和注册,避免运行时的各种错误


提升开发效率的配置:

**ILRuntimeMode** 表示热更的C#代码采用ilruntime模式

**开发模式下不打钩,方便断点和异常信息的查看**




## 客户端的网络消息处理

首先需要了解三个概念:

请求:需要回复         举例:(询问服务器的名字) 

发送:不需要回复      举例:(把名字告诉服务器)

通知:被动收到消息   举例:(服务器主动把名字说出来)


所以知道服务器的名字有三种情况


客户端问了

客户端没问服务器自己说了出来

其他客户端问了,服务器把名字告诉了所有人(广播)


至于什么是广播?那是服务端的工作,前端可以无须理解


天地劫案例移动指令同步方案:

方案一

客户端A 点击鼠标后 发送 移动坐标指令

服务端通知 客户端B

客户端B 收到指令 更新人物坐标


方案二

客户端A 点击鼠标后 发送 移动坐标指令

服务器收到消息后校验移动坐标的合法性,假如作弊,封号

服务端通知 客户端B

客户端B 收到指令 更新人物坐标


方案三

客户端A 点击鼠标后 请求移动 

服务端计算寻路的路径后 

广播通知 A.B

客户端A,B 收到指令 更新人物坐标


方案一更高性能,是帧同步首选,至于帧同步的防作弊,有空我们再单独开一个章节讲解


方案二,三 都可以作为天地劫的解决方案


## ET的ECS组件开发到底是不是前端需要的?

简单来说ECS就是组件式开发+三层架构(MVC)

站在前端的角度来说U3D Monobehavior就拥有了组件式的开发能力

那么ET为什么还要开创自己的组件开发方式?



原因有很多

比如:ET的设计出发点是打造一套代码“前后端通用”,

考虑到后端脱离了U3D引擎自然不能使用Monobehavior


站在ILRUNTIME代码热更的角度:

不使用Monobehavior,解决了热更代码跨域继承的性能问题

  

  《U3D天地劫》**完成的项目**,目的是**增加网络状态同步**

  

  所以我们需要问自己

  项目需不需要前后端代码复用?

  需不需要MVC方式开发?

 你是否愿意花额外的时间把已经写好的游戏进行框架重构?


我们的需求是,用**最少的时间成**本去完成这件事

如果使用ET的组件开发思想,需要大量代码的重构,必定要额外花费时间。


所以权衡利弊之后,最终做出选择

我们的游戏代码还是保持原来的风格,ET只是作为一个网络通信库来使用

仅仅是需要ET**收发消息功能**而已

ET组件式开发的在本课程里尽量少用或者不用

只保留网络协议处理的核心用法(比如广播消息的接收)



## 匹配对战请求设计


前后端商量协议格式


客户端的请求C2G_MatchPvp  (匹配其他玩家)

服务器的返回G2C_MatchPvp    (匹配操作结果)


直到匹配人数为2,服务器通知:M2C_EnterPvp(对局开始)  


详细过程

客户端点击匹配对战   发出请求C2G_MatchPvp   

 C=Client=客户端,G=Gate=网关

至于网关是啥前端不用理解,一般由后端定制


所以这条消息就是代表 客户端 发给 服务器的 网关服


进入匹配状态 给客户端发回 G2C_MatchPvp   



匹配玩家数等于2,服务器派发通知 M2C_EnterPvp(对战开始)

  



  前端 需要关注前缀有C的协议

  C2G_******       G2C_*******        

  C2M_******      M2C_*******


服务端关注的协议的比较多

因为它需要处理分布式的实现

需要处理客户端的消息 C2*   ,也要处理服务器之间的消息  G2* , *2G

例如 C2G_LoginGate,R2G_GetLoginKey,G2M_CreateUnit

//网关和客户端的消息,网关和地图服的分布处理消息 等

当然客户端无需理解服务器的处理过程



U3D 实现

发送 Session.Send(new T1(msg="你好服务器"))

请求  T msg= await Session.Call(new T1(msg="你好服务器")) as T; 

msg则是服务器返回的消息

//msg.info="你好客户端"


消息的广播通过ET的事件架构订阅

class G2C_HelloWolrd_Handler:AMHandler<G2C_HelloWolrd>

{

      Run(message){

        ///message=你好客户端,服务器在给你推送消息。

}

{

![在这里插入图片描述](https://img-blog.csdnimg.cn/7c9aa50a3901410e8a63b99ea873ab79.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA57yW56iL5LmL5Yqb,size_20,color_FFFFFF,t_70,g_se,x_16)

指令同步:移动


A客户端转发**指令50到35**,服务器广播该消息,B客户端收到后播放动画

这里只是转发指令


## 客户端修改移动数值怎么处理?

服务器会返回操作结果,假如指令不合法,会强制客户端重新同步数据

大家可以看下图的协议设计



攻击逻辑的设计

MMO:发出攻击请求,服务器返回数据,一般会包含哪些人物被攻击,受到了多少伤害,以及派生的效果


天地劫:发出攻击请求,客户端自行计算伤害以及派生效果。

为什么要这样设计呢?因为第一季的课程已经开发完了整套游戏的逻辑

服务器的代码只是在客户端移植过去的。所以计算结果不用服务器另外通知

而且还能减少数据传输的开销,攻击的协议只包含3个int参数from,to,target 


因为客户端自行计算了结果,这里为了防止恶意修改数值,比如攻击力=9999


所以要做操作指令安全验证:


攻击指令发出后后(比较客户端和服务器的md5码)


如果两边不同步,客户端同步服务器数据


由于这些计算大部分都是服务器的工作,所以前端无须关注


课程为了**前后端细分**

课程会做出**策划设定限制**(PVP模式,攻击距离只能为0-1,不能配备AOE技能)


因为这些设定包含了大多属于后端的工作,前端是不需要学习的,

所以我们会在另开一遍服务器的教程,详细讲安全数据的验证和实现


下图结果显示,每次指令执行后,都会比较状态同步码来确保数据永远以服务端的为准,

实际开发中可以生成 MD5码,减少数据包的大小


## 如何编写前后端通用的代码

下图是服务器的代码,Skill目录下的引用了客户端的技能模块

可以看到在加血技能释放时,服务器只需要数据的计算,不需要特效部分


## 掉线检测

在网络不好的情况下,数据收发会有延时,或者丢失。

ET6内置了心跳包检测机制,默认是超时是30秒,30秒内收不到服务器回应视为断线




## 掉线重连



断线一般发生在两种情况,用户强制关闭的客户端。或者坐在车上,进入隧道(瞬间的网络不好)


第一种情况的处理:由服务器端编写当用户重新登录时,重新回到战场的逻辑

第二种情况的处理:订阅连接超时事件,超时后进行重新登录和状态同步,

假如重新登录还是超时,则放弃重连,返回登录界面


用clumsy弱网测试工具。进行丢包测试





## 丢包处理

因为ET采用了kcp协议,即使丢包也会重传保证客户端能收到

 弱网测试工具clumsy。丢包测试通过

 **所以ET6框架不需要考虑丢包问题**


下面讲述的是前端在面对使用UDP广播的服务端时如何处理丢包的处理方法。

(毕竟工作中不是每个项目都会用到kcp协议)


坐在车上的我们即将进入隧道,通过时间是5秒,然后恢复了网络。

在这短短的5秒里对方刚好又做了移动操作。

而客户端却因为隧道里网络不好丢了这条指令.

因为服务器消息的广播采用UDP协议,不确保客户端能收到消息。


那也就说,我们无法得知我们自己是否丢包了。

由于丢包时长不足30秒,所以无法采用断线重连的处理方式,


所以,因为只是丢了一个移动消息包,游戏状态得不到同步。


解决方案:服务器连续发送消息包号,客户端存起来,

假如的客户端的包号+1和 服务器的对不上,我们就知道丢包了


最后我们对项目进行弱网,无网测试通过。


课程关键字:

unity网络框架,unity网络通信,unity网络同步,unity网络实战,unity网络游戏教程,unity网络游戏开发,unity网络实战




本文标题:UNITY 网络游戏开发 - 八卦谈
本文地址:www.ttdhp.com/article/29581.html

天天动画片声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
扫码关注我们