总述
本文 翻译 自Unity 5.2的官方文档。如对翻译有任何建议,欢迎留言。Unity从5.1开始改进了网络系统功能,提供了一个比之前版本更灵活更强大的网络系统。它提供了一个NetworkTransprot类,作为基本的套接字和有很多有用的多人游戏特性的高级组件之间的中间层。
两类网络功能用户
用Unity制作多玩家游戏的用户,这类用户应该从NetworkManager或者高级API章节开始。搭建网络架构或制作高级的多玩家游戏的用户,这类用户应该从网络传输层API开始阅读。
- 高级API
Unity的网络功能提供了高阶的脚本API(HLAPI)。使用它们能满足大多数的多玩家游戏的需求,并且不需要关系底层的实现细节。HLAPI可以让你:
- 控制游戏的网络状态,通过Network Manager
- 操作客户端主持的游戏,主持游戏的客户端同时也是一个玩家
- 使用一个通用的序列化器序列化数据
- 发送和接收网络消息
- 从客户端向服务器发送网络命令
- 在服务器上远程调用客户端提供的过程(RPC)
- 从服务器向客户端发送网络事件
引擎和编辑器整合
Unity的网络功能已经集成进了引擎和编辑器中,这允许你用组件的方式可视化地构建你的多玩家游戏。它提供了以下功能:- 为联网的物体提供了网络标识组件(NetworkIdentity)
- 为网络脚本提供的NetworkBehaviour基类
- 提供可对物体位置信息自动进行同步的组件
- 自动同步脚本中的变量
- 支持将网络物体放置到场景中
- Network组件
网络服务
Unity可为你的游戏提供整个生命周期内的网络服务,包括:- 比赛的组织
- 创建和广播比赛
- 列出所有有效的比赛和加入比赛
- 接替服务
- 在没有专用服务器的情况下也可以联网游戏
- 在参赛者间路由消息
实时传输层
实时传输层可提供:- 优化的UDP的协议
- 多通道设计,避免线头阻塞问题
- 支持为每个通道配置不同的服务质量(QoS)等级
- 弹性的网络拓扑,支持点对点或客户机-服务器架构
高级API
高级API(HLAPI)是为构建Untiy多玩家联网游戏而提供的一套系统。构建在底层的实时通讯层之上,提供了多玩家游戏需要的很多通用功能。同时,传输层能支持各种不同的网络拓扑。HLAPI是一个服务器命令式的系统,它允许其中一个成员同时作为客户端和服务器,所以不需要专用的服务器。和Unity的网络服务器一起使用的话,开发者只需要很少的工作就能让多玩家 游戏 在因特网上运行起来。HLAPI现在使用了一个新的命名空间UnityEngine.Networking。他专注于易用性和迭代开发,为多玩家游戏提供了如下服务:
- 消息处理
- 通用的高效序列化器
- 分布的物件管理
- 状态同步
- 网络相关类:Server,Client,Connection等
HLAPI的层次和功能结构图如下:
网络系统基本概念
服务器(Server)和伺服器(Host)
在Unity的网络系统中,每个 游戏 有一个服务器和多个客户端。在没有专用服务器的情况下,其中一个客户端会同时担任服务器的角色,这台客户端被叫做伺服器(Host)。伺服器 上客户端和服务器运行在同一个进程里面。伺服器使用了一个特殊类型的客户端,叫做本地客户端(LocalClient),而其他的客户端叫做远程客户端(RemoteClient)。本地客户端与服务器的通讯是通过直接的函数调用和消息队列,因为他们在同一个进程中。实际上,他和服务器共享同一个场景。远程客户端使用常规的网络连接与服务器进行通讯。
网络系统的一个设计目标是,本地客户端和远程客户端使用同一份代码,这样开发者大多数时候只需要考虑一种类型的客户端的实现。
实例化(Instantiate)和派生(Spawn)
在Unity中,GameObject.Instantiate函数可以创建新的游戏物体。但是在网络环境下,游戏物体还必须在网络上被派生出来。这需要在 服务器 上创建后,同时通知所有连接的客户端也创建该物体。一旦物体被派生出来,派生系统会负责这些分布的物体的生命周期管理和状态同步。更多信息可参考“游戏物体的派生” 一节
玩家(Player),本地玩家(Local Player)和授权(Authority)
在网络系统中,玩家物体是特殊的,每个玩家物体和一个玩游戏的真实玩家相关联,来自这个玩家的所有命令都发送给这个玩家物体。一个玩家不能操作其他玩家的玩家物体,只能操作他自己的。所以这里有一个“我的”玩家物体的概念。但一个玩家加入 游戏 并和一个连接相关联之后,这个玩家物体会在那个玩家的客户端上成为一个本地玩家物体。有一个属性isLocalPlayer会被设置为True,同时这个物体上的一个回调函数OnStartLocalPlayer会被调用到。下图显示了两个客户端和他们各自的本地玩家对象。只有 玩家 自己的物体才会被标记上isLocalPlayer标记。这可以被用来过滤用户输入,控制绑定的相机,或者其他应该只对本地玩家进行的处理。
除了isLocalPlayer,一个玩家物体还可以拥有本地的授权。意思是拥有这个物体的那个客户端负责控制这个物体-拥有授权。这通常被用来控制物体的运动,但是也可以用作其他的用途。NetworkTransform组件会使用他,如果在客户端上被设置,他将从客户端想服务器发送移动数据。NetworkIdentity组件有一个选择框可以设置localPlayerAuthority属性。
对于非玩家物体,比如怪物,他们没有关联的客户端,所以授权是在服务器上。
在NetworkBehaviour组件中,有一个isAuthority属性,他用来控制一个物体是否有授权,所有的非玩家物体在 服务器 上有授权,有localPlayerAuthority的玩家物体在他们自己的客户端上有授权。
非玩家物体在客户端上的授权
从Unity 5.2开始,可以在客户端上对一个非玩家物体授权。有两种方法可以实现。- 使用NetworkServer.SpawnWithLocalAuthority派生出物体,并传入进行授权的客户端的网络连接;
- 使用NetworkIdentity.AssignClientAuthority函数修改拥有者,传入目标客户端的网络连接。
指定授权给客户端会导致这个物体中的NetworkBehaviours类中的OnStartAuthority()函数被调用,同时属性hasAuthority将被设置为true。在其他的客户端上,hasAuthority属性将保持为false。拥有客户端授权的非玩家物体可以发送命令,像玩家对象一样。这些命令将被执行在服务器上,而不是在关联的 玩家 客户端上。
用客户端授权的非玩家对象,必须将NetworkIdentity中的LocalPlayerAuthority选项选中。
下面的例子派生出一个物体,并且将授权指定给派生他的客户端:
[Command]
voidCmdSpawn()
{
var go =(GameObject)Instantiate(otherPrefab, transform.position + new Vector3(0,1,0),Quaternion.identity);
NetworkServer.SpawnWithClientAuthority(go,connectionToClient);
}
网络上下文
在NetworkBehaviour类中有一些属性,允许脚本在任何时候获得关于物体的网络上下文信息:- isServer – true 如果物体是在服务器(或 伺服器 )上而且已经被派生出来;
- isClient – true 如果物体是在客户端上,而且已经在 服务器 上被创建;
- isLocalPlayer – ture 如果物体是当前客户端的 玩家 对象
- isAuthority – true 如果物体的拥有者是本地进程
上面就是Unity的新版网络系统中的基本概念,下一节,我们将介绍如何使用Unity为我们提供的高级组件来构建我们的多玩家 网络游戏 。