Mirror学习笔记(五)概念指南

文章目录


顶层脚本API:

Mirror是一个高级网络库,这以为着他包含了多人游戏常见的多数命令,使用都无需关心各中实现细节。

Mirror允许 :

1.使用Network Manager控制游戏网络状态

2.操作客户端游戏,其中主机也是玩家客户端。

3.使用通用序列化程序,序列化数据

4.发送和接受网络消息

5.将网络命令从客户端发送到服务器端

6.进行服务器到客户端的远程调用

7.将网络事件从服务器发送到客户端


底层脚本API:

Mirror需要底城的Transport才能在byte[]级别上连接/断开/发送/接收消息

引擎和编辑器集成

Mirror的网络集成在引擎和编辑器中,允许您使用组件和视频工具来构建您的多人游戏,它提供了以下功能:

1.用于网络对像管理的组件 :NetworkIdentity。

2.用于网络的脚本 :NetworkBehaviour.

3.对像转换可以配置自同步。

4.脚本变量的自动同步。

5.支持Unity场景中䛂不联网对象。

6.网络组件


一、Authority(权限)

Network Authority 权限是决定谁拥有一个对象控制权的标志。
服务端权限

意味为着由服务端控制该对象,一般用于控制移动平台,npc和其他非玩家网络对像。
客户端权限

意味着由客户端控制该对象,当客户端断开连接时该对像将自动销毁。

及时客户端拥有权限,服务器仍可以通过SyncVar控制其并序列化功能。组件将需要使用命令来更新服务器上的状态与其他客户端同步。
如何赋予权限

默认情况情况情况下,服务器对所有对像具有权限。服务器可以授予客户端需要的对象控制权限,比如你的玩家对象。

如果您使用NetworkServer.AddPlayerForConnection生成一个玩家对象,那么它将自动获得权限。
使用NetworkServer.Spawn

你可以在生成对象时授予客户端权限,这是通过将连接传递给生成消息来完成的

GameObject go = Instantiate(parfab);

NetworkServer.Spawn(go,connectionToClient);
使用identity.AssignClientAuthority

在你可以随时 使用AssignClientAuthority向客户端授予权限。这可以通过在要授予权限的对象上调用AssignClientAuthority来完成。

identity.AssignClientAuthority(conn);

当玩家拿物品时 ,你可以这样做:

void CmdPickupItem(NetworkIdentity item){

Item.AssignClientAuthority(connectionToClient)

}
如何移除权限:

你可以使用identity.RemoveClientAuthority从客户端移除权限。

无法从玩家对象中移除权限。相反你必须使用NetworkServer.ReplacePlayerForConnection替换player对象。

当给予或从对像中移除权限时,将向该客户端发送一条消息通知他们。这时候OnStartAuthority或OnStopAuthority函数。
检查权限:

客户端 : 使用identity.hasAuthority可以检查本地玩家是否具有权限。

服务端 : 可以使用identity.connectionToClient查看哪个客户端是否有权限。


二、IDs(身份编号)

Asset Id(资产ID)

Mirror使用GUID作为 Asset Id。每个带有Network Identity组件的prefab都有一个Asset Id,它是Unity的AssetDatabase.AssetPathToGUID转换为16字节。Mirror需要用他来知道生成哪些预制体。

Scene Id(场景ID)

Mirror使用uint作为场景ID.场景(层次结构)有NetworkIdentity组件的都在OnPosProcessScene中分配一个场景ID。Mirror需要它来区分场景对象,因为Unity没有针对场景中不同游戏对象的唯一ID

Network Instance id(网络实例Id)

Mirror将uint用于NetId.每个NetworkIdentity都会在NetworkIdentity.OnStartServer中或在生成NetId。Mirror在客户端和服务器之间传递消息使用id来判断哪个对象是消息的接收者。

Connection Id

每个网络连接都有一个连接id,由底层(传输层)分配。当服务器/主机连接id o时为本地连接。


三、Attributes(属性)

网络属性将添加到网络成员函数中,以使它们 在客户端或服务端上运行。

这些属性可以用于Unity游戏方法(如Start或Update)以及其他方法中。

当使用抽象方法或虚方法时,属性也需要应用于覆盖方法。

[Server] : 只有服务器可以调用该方法

[ServerCallback] : 与Server相同,但在客户端不会发出警告
[Client] : 只有客户端可以调用该方法
[ClientCallback] : 与Client相同,但服务器不会发出警告
[Command] : 从客户端调此函数时上运行此函数。
[ClientRpc] : 服务端使用远程调用(RPC)在客户端上运行该函数。
[SyncVar] : 用于将变量从服务器自动同步到所有客户端。不要从客户端分配他们这是没有意义的。不可以为空,会报错。你可以使用

int,long,float,string,vector3等所有简单类型,和Network Identity和游戏对象(如有Identity).

当客户端接收到来自服务器的更新时,可以使用SyncVar Hooks在客户端运行代码。


四、Time Synchronization(同步时间)

对于许多算法你需要在客户端和服务器之间同步时间。Mirror会自动为您操同步。

如要获取当前时间,请使用以下代码:

csharp 复制代码
double now  = NetworkTime.time;

它将在客户端和服务器中返回相同的值,它从0开始,时间是double类型,绝不应该投身到浮点数上。将其转换为浮点数上将会失去一定精度。

Mirror还将计算应用程序看到的RTT(往返时间):

csharp 复制代码
double rtt = NetworkTime.rtt;

您可以测量准确性:

csharp 复制代码
double time_standard_deviation = NetworkTime.timeSd;

如何返回0.2则表示时间测量值上下摆动大概0.2秒

通过使用EMA平滑值 来补偿网络间隔。

您可以家只发送ping的频率:

csharp 复制代码
NetworkTime.PingFrequency = 2 ;

您可以配置计算中使用的ping结果数:

csharp 复制代码
NetworkTime.PingWindowSize = 10;

五、Data types(数据类型)

客户端可以与服务器通过"远程操作","状态同步"或"网络消息"的方式相互传递数据

Mirror支持多重可用于数据类型,包括:

1.基本的Unity C#类型 : btye、short、int 、long、unit、ushort、ulong、float、double、char、string 等...

2.内置Unity 数据类型 : Vector3、Quaternion、Reck、Plane 等...

3.内置的Unity类型 即后台结构 Color、Sprite Texture2D、Ray 等...

4.URI (统一资源标识)

5.NetworkIdentity,NetworkBehaviourl

--5.1这些不应该在SyncVal或Sync*中使用,因为如果对像尚未生成,它们在客户端中将为空。

6.具有NetworkIdentity的网络对象(不是预制体)

7.具有上述任何一项结构

--7.1 你必须提花整个结构值,而不仅仅是更改其属性值

--7.2 建议用IEquatable以避免装箱,并将结构是指为只读,因为修改参数值不会重新同步

8.在类中,只要每个字段都有支持的数据类型

--8.1 必须替换整个类值,而不仅仅是更改期属性

--8.2 这将产生垃圾,因为每次分配时都会在接收器上重新实例化

9.ScriptableObject 只要每个字段都有接受支持的数据类型

--9.1 这将产生垃圾,因为每次分配时都会在接收器上重新实例化

10.上述任何一荐的数组

--SyncVars或SyncLists不支持

11.上太空任何一个的ArraySegments

Game Object(游戏对象)

SyncVars、SyncLists和SyncDictionaries中的游戏对像在某些情况并不可靠,用应慎重使用。

只要游戏对象在服务器和客户端上都存在,引用就不会有问题。

当同步数据到达客户端时,该客户端上可能还不存在引用的游戏对象,从而导致同步数据值为空。

这是因为内部Mirror从NetworkIdentity传递netId并尝试在客户端的NetworkIdentity.spawned字典中查到它。

如果该对象还没在客户端生成,找不到匹配的对象。它可以在相同的有效负载中,特别是对于加入的客户端,但在另一个对象同步数据后。

游戏对象由于网络可见性问题,它也可能是空的

你可能会发现,同步NetworkIdentity.netId(uint)并执行管理局 查找以获取对象会更可靠,也许可以使用协程NetworkClient.spawned查找获得对象。

Custom Data Types(自定义数据类型)

有时候你不想使用Mirror类型生成序列化,则可以通过 NetworkWriter和NetworkReader来添加支持的类型。

例如要对DataTime添加内容,可以参考以下例子:

然后你可以使用DateTime 在你的[Command] 或 SyncList

Inheritance and Polymorphism (继承 和 多态)

有时候您可能希望发送多态类型的数据命令。出于安全原因Mirror不会序列化类型名称以保持消息较小,因此Mirror无法通过查看来确定接收的类型。

以下代码不是开箱即用的

如果为类型提供自定义序列化程序,CmdEquipu将会起作用。例如:

ScriptableObjects

人们通常希望从客户端或服务端发送可以编写脚本的对对象。例如,你可能创建一堆可编辑脚本的对象,并且将装备的脸放在这里同步。

Mirror将通过ScriptableObject为脚本生成一个读取器和写入器。创建实力并封复制所有数据。

然而生成reader和writer并不适合所有场合。可编辑脚本的对象通常会引用其他资源,例如texture、prefab或其他无法序列化的类型。可编写脚本的对象通常存在Resources文件夹中。

可编写脚本对象有时候会包含大量数量,导致生成和写入器可能无法正常工作。

你可以传递名称,而不传递可编辑脚本的对象数据,而另一方面可以按名称 查找相同的对象。这样您可以在可编辑脚本对象中拥有的任何类型数据。可以通过提供自定义读取器和写入器来做。


六、Serialization(序列化)

Mirror使用Weaver为类型创建Serialize(序列化)和Deserialize(反序列化)函数。Weaver在unity中使用Mono.Cecil编译 dll后编辑他们。这使得Mirror有许多复杂的功能,例如SyncVar、ClientRpc和消息。而无需要用户手动设置所有内容。


七、Synchronization(同步)

状态同步是指脚本对Int、float、string和bool值进行同步。

状态同步是从服务器到远程客户端完成的。本地客户端没有序列化到它的数据,它不需要,因为它与服务器共享场景。但是在本地客户端上用SyncVar挂钩。

数据不会以相反方向同步(从远端到服务器).炙此您只需要使用命令。

SyncVars : 是继承自NetworkBehaviour脚本的变量,从服务器同步到客户端
SyncEvents(Obsolete) : 类型于ClientRpc网络事件,但它们是会在游戏对像上调用函数,而是出发事件。18.0.0版本后弃用。
SyncLists : 包含值列表并将数据从同步到客户端。
SyncDictionary : 是一个关联数组,其中包含键值对的无序列表
SyncHashSet : 一组不重复无序值。
SyncSortedSet : 一组排序的不重复值

同步到所有者:

假设场景中有50个人,都有各自的背包。这时候玩家A拾取了一个道具。

这时Mirror默认会将此道具同步到场景中50个玩家A的背包中,然后其他玩家并不能看到获取的内容,这很浪费,也是个安全问题。

如果这里你将Inventory组件中的Setwork Sync Mode设置为Owner则玩家A的库存将仅与客户端A同步。

典型的例子是包括任务、纸牌、技能、经验等不需要别的客户端知道的消息。

高级状态同步:

多数情况下,SyncVars足够装状态同步到序列化客户端。但是某些下可能需要更为浮渣的序列化代码。此页面仅适用于需要自定义同步解决方法的高级开发人员。

超出了Mirror常规的SyncVar功能。

自定义同步状态:

使用自定义同步状态,可以在NetworkBehaviour上实现于SyncVar序列号的虚函数。这些函数包括:

使用该标志可以区分首次和后续系列化,首次同步时必须有完整状态快照后面则不用,有助于节省宽带。


八、Communications(通讯)

多人游戏开发时,除了同步网络游戏对像的属性外,你可能还要发送和信息。在Mirror网络传输中,有三种主要的方式来传达此类信息。
远程操作:

远程操作允许您通过网络用脚本中的方法。您可以专门在"所有的客户端"或"单个客户端"上创建"服务器"调用方法。还可以让客户端用服务器上的方法,使用远程操作,您可以将数据作为参数传递 给你的方法,与本地调用非常类似。
Network Callback(网络回调):

网络回调允许您 挂载在游戏过程中发生的内置Mirror事件,例如玩家加入或离开时、游戏对象创建或销毁时,你可以实现两种网络回调:

网络管理回调,用于本身相关的回调(例如客户端连接或断开连接时)

网络行为回调,用于与单个网络对像相关的调用(例如,当它的Start 函数被调用时,或是新玩家加入时需要做特定的事)
网络消息:

网络消息是发送消息的"低级"方法(尽管它们扔被归类为网络高级API的不部分)。它们允许您使用脚本直接在客户端和服务器之间发送数据。

可以发送数据类型(int、string、及unity基本类型Vector3等).由于您自己实现了这个,这些消息不会直接与任何特定的游戏对象或unity相关联。


九、GameObject(游戏对象)

网络游戏对像是由Mirror的网络系统控制同步。使用同步联网对像您 可以为别的客户端创建共享的内容。

Mirror中的多人游戏通常使用包含游戏对象和常规游戏对象的场景构建。

联网游戏对像是在游戏过程中需要在所有一起玩游戏的相互间需要同步移动或改变参数的对象。
非联网游戏对像在游戏过程 中完全不移动或变化的,如场景中的山,岩石,树木、云等。

网络对像是附加了网络身份组件的对象。但并不是只有网络身份对象就能起作用。Network Identity组件是同步的起点为它允许网络管理器同步创建销毁,别的参数同步没有指定。

网络游戏中每个对像如何同步,取决于游戏类型及对像的用途。您可能想要同步的一些内容比如是:

1.移动及放置

2.动画游戏对象的动画状态

3.参数的值

其中一些东西可以通过Mirror自动同步。联网游戏对像的同步创建和销毁由NetworkManager管理,称为Spawning.

可以使用Network Transform组件来同步放置,也可以使用Network Animator来同步游戏的动画。

同步参数变量则需要你用脚本实现。

游戏玩家对象:

Mirror处理玩家游戏与非玩家的方式不同。当新玩家加入游戏时(或连接到服务器时),该玩家的游戏成为该玩家客户端上的本地玩家游戏对象。

Unity为每个玩家关联一个玩家对象,并将网络命令路由到该单独的游戏对象。玩家不能在其他玩家游戏上调用命令,只能在自己的对象上调用命令。

NetwrokBehaviour类(用于创建网络脚本的派生类),有个isLocalPlayer属性。在每个客户端玩家对像上Mirror在NetworkBehaviour脚本上该属性为true.并调用OnStartLocalPlayer()回调。

这以为着每表客户端都有个与此不同的对象,因为在每个客户端上一个不同的为本地玩家,余都是别的玩家。

可以通过isLocalPlayer判断是否本地玩家,设置摄像头跟进。

默认情况下新玩家加入NetworkManager组件会自动创建玩家预设,要精致创建可以取消勾选AutoCreate Player.

自定义角色生成:

Mirror允许用户对默认玩家进行自定义.默认情况 下会为你创建预设,你可以使用自定义继承NetworkManager进行修改。