Unity MOBA 多人竞技手游制作教程
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.3 登录逻辑实现

3.3.1 基础知识

在登录逻辑开发过程中,主要实现两大功能:显示服务区列表;组队匹配。在此过程中,会涉及一些基础知识点。下面通过示例对这些内容进行简单介绍,熟悉后再根据所学的知识将整个逻辑功能实现出来。

◎按钮触发函数

在游戏运行过程中,经常会和界面进行交互。最直接的就是通过单击按钮,触发某个功能,本质上是执行某个或多个函数。在通过示例演示此功能的实现过程,可以分为以下三步。

第一步:定义功能函数

首先,创建一个ButtonEvent脚本;然后,在此脚本中定义一个Public类型的功能函数。代码如下所示:

第二步:添加组件

具体操作步骤如下。

Step 01 创建一个Test场景,在场景中创建一个Sprite对象,用于存放按钮皮肤,在Sprite对象中再创建一个Label对象用于显示文字,如图3-8所示。

图3-8

Step 02 单击Sprite对象,将其改名为Button,并添加碰撞器(BoxCollider)与按钮组件(UIButton),可通过如图3-9所示的Add Component按钮添加组件。

Step 03 修改碰撞器的大小,使其与按钮大小一致。此组件的主要作用是触发此按钮。

第三步:绑定功能函数

给按钮添加点击事件。具体操作步骤如下。

Step 01 将挂载ButtonEvent脚本的对象拖曳到Notify栏中,如图3-10所示。

图3-9

图3-10

由于在此场景中,把ButtonEvent脚本挂载到了Button按钮对象上,因此,拖曳的对象便是Button对象。

Step 02 指定要执行的函数,在Method处,选择定义功能函数的类,并选取对应的功能函数,如图3-11所示。

图3-11

最后,运行游戏,可以在Console控制台中看到“开始游戏事件被调用”,说明此函数被调用。

小提示

(1)UI Button组件是NGUI中的脚本,框架中包含了NGUI插件,所以能够直接添加此组件。

(2)按钮点击事件的修饰符为public。

游戏中包含多个界面,界面与界面的切换可以利用GameObject类中的函数SetActive。此函数可以停止渲染对象,而且场景中看不到停止渲染的对象,使用方式如下所示(参数表示是否显示此对象,True表示激活显示此对象,False表示禁用隐藏此对象)。

◎字典的使用

当程序中包含多种同类元素,并需要快速定位某个元素时,可以用集合中的Dictionary(字典)。它可以快速地基于键值的方式查找元素。Dictionary包含在System.Collections.Generic命名空间中。因此,使用它时,需要导入命名空间。其结构如下:

特点

□ 它是键值对的映射,每个键都会有对应的值。

□ 每个键都必须是唯一的。

□ 键不能为空引用null,若值为引用类型,则可以为空值。

□ 键和值可以是任何类型(string、int、custom class等)。

使用方法

Step 01 创建字典并初始化。代码如下:

Step 02 添加元素。代码如下:

Step 03 通过Key查找元素。代码如下:

将上述代码写入脚本中,完整示例代码如下所示:

保存脚本,并将脚本挂载到场景对象上,运行游戏,可以看到如图3-12所示的结果。

图3-12

3.3.2 完善登录逻辑

◎创建游戏管理器

了解过基础知识点后,现在来完善登录逻辑。打开登录场景。此场景中包含着所有的UI界面。首先激活开始游戏的背景图,如图3-13所示,此界面就是登录界面。先来创建第一个脚本,实现登录过程。

图3-13

如图3-14所示,新建一个脚本,通过单击菜单栏Assets→Create→C#Script命令创建一个脚本,将其命名为GameStart。此脚本可以作为游戏管理器,负责处理界面逻辑与消息,所以将此脚本挂载在场景中对象上。新建一个空物体,同样命名为GameStart,并将脚本直接拖曳到此对象上。为了使资源整洁,先将开发的脚本存储在Study文件夹中,再双击打开。

图3-14

打开脚本后,里面存在两个方法。Start与Update,这两个函数在开发中都会被用到。Start函数在游戏开始时会被系统调用,并且只调用一次,一般此类函数是程序执行的入口。Update函数的每一帧都会被系统调用,每秒钟大约60次。登录的基本逻辑就在此脚本中完成。

◎开始登录事件

登录界面中包含“开始”按钮,现在为“开始”按钮添加一个事件函数,函数名称可以命名为OnPlaySubmit,代码如下所示(“开始”按钮的功能是连接服务器并登录,实际上就是通知服务器有一个客户端登录了。所以在此函数中需要连接服务器并输出一句话,这是为了单击按钮时方便观察)。

“开始”按钮的目的就是连接服务器,但是服务器有多种,最终处理功能的服务器是网关服务器GateSever,但当用户数量过于庞大时,需要利用平衡服务器来分配区域,所以开始游戏时首先需要连接的是平衡服务器。

◎网络消息接收处理

想要在GameStart中接收服务器返回的消息,需先在Awake初始化函数中注册接收消息的事件,代码如下所示(其中,GameEvent_NotifyNetMessage是消息类型,HandleNetMsg是事件函数,这句话表示为消息接收添加了一个监听器,当客户端接收到此消息时,会直接调用HandleNetMsg来处理消息。此消息是在框架中NetWorkManager中接收到消息后广播的,这部分内容读者不用进行操作,只需要注册此事件即可)。

在脚本中创建一个名为HandleNetMsg的函数,并为此函数添加两个参数,如下所示(Stream代表消息体,n32ProtocalID代表消息类型。如果想清楚服务器返回的消息类型,可以直接在此函数中输出n32ProtocalID)。

小提示

Stream是系统提供的数据类型,如果想要使用Stream,需要在类的最上方引用IO的命名空间:using System.IO。

◎网络消息处理

服务器连接成功之后,返回连接结果。消息的接收在GameStart中的HandleNetMsg()完成,因为接收服务器端的消息很多,所以并不在此函数中处理,而是转到了MessageHandler进行处理,消息处理流程如图3-15所示。

图3-15

在GameStart类中接收到消息后,将此消息转到MessageHandler类中进行处理。如果在处理过程中需要用到GameStart中的数据或函数,可以将此消息再广播回来。接下来详细介绍网络消息的处理过程。

HandleNetMsg

在HandleNetMsg中接收消息。定义时,包含两个参数:消息体与消息类型。代码如下(服务器端传回的所有消息都在此函数中进行处理,所以根据消息类型进行不同的操作。但如果在此函数中处理消息会使得此函数过于庞大。因此,在接收到消息之后,在MessageHandler中定义相应函数进行处理,MessageHandler是新定义的一个类,稍后详解)。

MessageHandler

MessageHandler是消息处理中心。主要作用是新建的一个类,代码如下所示(此类中专门定义消息处理函数。MessageHandler中的函数定时都是以大写On开始,并结合消息类进行命名的。此类创建完成后继承UnitySingleton,这样可以直接通过MessageHandler.Instance来调用消息处理函数)。

服务器接收到客户端的连接网关服务器的请求后,就会返回网关服务器的IP地址与端口,消息体中包含了网关服务器的IP、端口等。用于连接网关服务器,但是本游戏的所有服务器的IP与端口是相同的,所以没有使用返回的IP地址与端口。

然而,网关服务器的IP与端口定义在GameStart中,所以返回GameStart中进行连接,那么如何调用GameStart中的函数呢?这里用到了框架中的消息机制。

◎连接网关服务器

到此,onNotifyGateServerInfo函数被调用,此函数目的就是连接网关服务器,代码如下所示。在连接新的服务器之前,要先断开之前的连接,才能重新开始连接服务器。断开连接是通过NetWorkManager中的Close()函数来实现的。然后通过Init()函数重新设置网关服务器的IP与端口,连接的服务器类型为GateServer,最后一个参数依然是true,代表在此类中接收消息。

总结

单击“开始游戏”按钮后,最终的目的是连接网关服务器,最终连接函数在GamaStart中的onNotifyGateServerInfo中,由此连接完成。当连接完成后。网关服务器端依然会返回消息,接下来,所有的逻辑都是由服务器的消息来驱动进行的。

◎换区事件

换区功能就是将服务器端返回的登录服务区列表在客户端显示出来。完成此功能大概需要以下三步:

Step 01 切换界面。

Step 02 获取服务器信息。

Step 03 显示服务器列表。

下面通过这三步来完成显示服务区列表的功能。

切换界面

登录界面中还有一个功能就是换区,游戏开始时显示默认的服务区,单击“开始”按钮直接进行游戏。单击“换区”按钮时,可以切换到服务器窗口并且显示所有的服务器列表。为此,在界面中找到换区对象ChangeSever并为此添加BoxCollider与UIButton、设置与此按钮绑定的事件OnPlayServer。在GameStart中,此事件主要负责切换窗口,代码如下所示。

mRootLogin与mRootSever是窗口的根节点,在GameStart中定义的公开的变量、所有的窗口的根节点或者预制体等,都是公开的变量并且在外部指定,如图3-16所示。将对象拖到指定的变量中去,脚本中定义的变量就有值了。

图3-16

小知识

定义的变量可以在外部指定,也可以利用GameObject.Find来寻找场景中的对象。

SetActive函数是GameObject类中的函数,所有的对象都是GameObject的子类,所以子类可以直接使用父类中公有的函数。此函数用于激活或禁用对象,激活时,对象在场景中显示;禁用时,对象在场景中隐藏。由此,可以利用此函数对界面进行切换。隐藏开始界面mRootLogin,显示mRootSever服务器界面。ShowSeverItem函数负责显示服务器列表,但是列表信息是由服务器返回的,所以需先获取服务器列表的信息。

获取服务器信息

游戏运行后,单击“换区”按钮便可以显示服务器列表,因此在游戏一开始时就需要获取服务器列表的信息。服务器列表信息在连接登录服务器信息时返回,也就是说在游戏一开始时,就要连接登录服务器。

在GameStart中的初始化函数Start中,通过Init函数连接登录服务器,IP地址与端口通过变量已经设置好,与其他服务器的IP是一样的,服务器类型选择为LoginServer。代码如下所示。

连接上登录服务器后,服务器返回所有的服务器列表。消息接收在HandleNetMsg中。接收到服务器列表的消息之后,将消息转到MessageHandler中,定义一个函数OnNotifyServerAddr用来处理此消息。代码如下所示。

pMsg消息体中包含着所有服务器列表的信息。所以利用循环将列表信息获取,并添加到存储服务器信息的集合中。但是在添加之前首先要清空集合中所有的信息,以防信息重复。

小知识

字符串分割可以使用Split函数,此函数将字符串分割成数组。

显示服务器列表

切换到服务器界面的目的是显示服务器列表并提供玩家选择。当界面切换到服务器选择界面时,定义的ShowSeverItem被调用,此函数负责生成服务器列表。mAreaItem是服务器列表单元格,定义完成之后在外部指定,此对象指的是Scroll View父节点下Grid下的AreaItem子对象。利用Instantiate函数生成对象,此函数是GameObject类中的函数,负责生成对象。紧接着将生成的对象obj的位置、大小、父物体以及名称重新设置。创建完成所有的服务器单元格后,利用mAreaGrid(网格)重新排列所有的单元格。代码如下所示。

在生成的所有的服务器单元格中,每一个单元格都包含BoxCollider与UIButton组件,目的就是使每一个单元格可以实现按钮的功能。