Unity Mirror 从入门到入神(一)

Mirror从入门到成神

简介

Mirror是一个unity网络同步框架,基于MonoBehaviour生命周期的回调的基础上进行数值的同步,将通讯方式和协议剥离,顶层业务只需关注写入读取流,具体传输协议由底层不同协议实现模块完成,使用Mirror编写网络联网游戏,需要掌握分布式编程,要习惯异步的思维,一个方法执行之后无法立刻拿到结果,可以利用协程来检查或者依托特定对象生命周期,也可以使用服务器回调的形式来做,先来看看unity原本的生命周期,关键是easy to use,国外倒是挺火的,国内没啥声响,我们团队在鉴于自己的实际情况,看重的就是一套代码管理,无需额外专职后端开发(项目小)

Unity的MonoBehavior生命周期事件包括Awake、Start、FixedUpdate、Update、LateUpdate、OnEnable、OnDisable和OnDestroy。
Mirror的NetworkManager,NetworkBehavior 图片来源于官网
在这里插入图片描述

在这里插入图片描述

在Mirror中相对本身的生命周期会多几个,可以在Mirror的官网查看详情NetworkBehaviour Callbacks

为了方便理解这里我们首先抽离一个概念,客户端(Client)和服务器(Server),在mirror中他们都是同一个项目的编译产物,在源头上是共生的,内部通过一些特定的标志来判定对应的逻辑代码是否应该执行,比如扣减用户血量,这种安全性要求较高的逻辑内容理应放置到服务器中执行,也有叫前端,后端的,我们选择mirror的原因是因为可以同项目管理,不需要额外的项目和开发人员来支撑服务端,大部分的游戏逻辑都可以公用,比如游戏功能道具啊,Server发布的时候非特殊要求的情况下使用Dedicated Server的方式进行发布,如果形象的形容是Headless无头,如果接触过爬虫的就会知道 无头浏览器的概念。及没有渲染能力的游戏客户端。

启动游戏服务端的时候有几个模式,分别是Host,Server Only,Client Only。主机模式,仅启动服务端,仅启动客户端,说白了我们把游戏逻辑写道一起,如果要完成的进行游戏,就必须同时具备Server,Client 都在线的情况。Host 启动后运行服务器代码,也运行客户端代码。Server Only仅运行服务端代码,Client仅运行客户端代码,下图为Host模式的结构性说明

在这里插入图片描述

RemoteClient表示通过网络通讯协议接入的其他客户端,Local Client在Mirror中做了特殊处理,并不会走网络协议,而是直接将数据写入一个本地队列,另外还有一些执行差异,后面会提到。这里总结下能运行完整游戏的方式

  1. 以HOST模式启动
  2. 连接其他HOST模型启动的程序
  3. 连接其他Server模式启动的程序

下图展示的是数据流的流动方向,对应Spawn出来的单位,服务器和所有客户端都会存在一个实例,由Mirror控制并将对应的数据分发给这些实例对象,其中通过NetworkIdentity的NetId来区分作为全网的唯一标识(全网指的是本局游戏类,连进Server的Client组成的网络)

在这里插入图片描述

下图展示的是Mirror 的层级构成,这个图上包含的类就是Mirror的核心类,这个图最好能背下来,有利于后续的Mirror学习, 对于只是利用Mirror完成联网游戏的改造和编写只需要知道NetworkClient向下的内容,现在来介绍下这些核心组件
在这里插入图片描述

NetworkClient

负责客户端上的和服务端的网络通讯,通常对于开发人员是不需要关注的,除非有定制化的需求

完整的api文档 这里介绍下几个可能会用到的

RegisterPrefab

该函数用于注册预制体,所有需要利用Mirror进行动态生成单位,都需要完成预制体的注册,有两种方式一种是在NetworkManager中RegisteredSpawnablePrefabs列表中添加对应的预制体,要添加成功需要该预制体存在NetworkIdentify组件,否则无法添加,也可以点击Inspector界面的PopulateSpawnablePrefabs,该功能可以自动扫描当前项目中所有添加了NetworkIdentify的预制体,能够自动将预制体添加到列表中,这里很容易犯一个小错误,如果预制体的名字一致,但是错误提示没有注册,可以检查是否是名字一样实则不同的预制体,方式二就是点在运行期间通过

RegisterPrefab (GameObject prefab, Guid newAssetId, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler)

该方法进行注册,需要注意的是注册的预制体需要在各个端都存在,否则注册之后会出问题。后面的SpawnHander UnSpawnDelegate 可以拦截系统的Spawn 和UnSpawn事件,找一个很容易理解的例子 Instantiate (实例化)Desotry (销毁)。客户端在收到UnSpawn默认的行为是Desotry掉,如果想要池化联网对象,就需要依靠上面的SpawnHander方法,另外该方法一个AssetId只能调用一次,不可以重复调用,如果需要替换请使用其他方法,该方法还有其他的重载,想要细致了解的化可以查看官方文档

Connect (string address)

链接远程服务器,address地址通过是一个ip,具体的端口信息在Transport.active中持有,通过这种方式可以修改端口,也就说明了Transport.active是单例的

    if (Transport.active is PortTransport portTransport)
    {
        // use TryParse in case someone tries to enter non-numeric characters
        portTransport.Port = ushort.Parse(port);
    }

根据源码具体看看这部分是怎么做的

     public static void Connect(string address)
     {
         Initialize(false);

         AddTransportHandlers();//
         connectState = ConnectState.Connecting;
         Transport.active.ClientConnect(address);
         connection = new NetworkConnectionToServer();
     }

Transport是一个接口interface,不同的传续协议都会实现,这里我们抽一个KcpTransport看一下

在这里插入图片描述

//KcpTransport.cs#ClientConnect

public override void ClientConnect(string address)
{
    client.Connect(address, Port);
}
//KcpConnect.cs#ClientConnect
 public void Connect(string address, ushort port)
 {
     if (connected)
     {
         Log.Warning("[KCP] Client: already connected!");
         return;
     }

     // resolve host name before creating peer.
     // fixes: https://github.com/MirrorNetworking/Mirror/issues/3361
     if (!Common.ResolveHostname(address, out IPAddress[] addresses))
     {
         // pass error to user callback. no need to log it manually.
         OnError(ErrorCode.DnsResolve, $"Failed to resolve host: {address}");
         OnDisconnectedCallback();
         return;
     }

     // create fresh peer for each new session
     // client doesn't need secure cookie.
     Reset(config);

     Log.Info($"[KCP] Client: connect to {address}:{port}");

     // create socket
     remoteEndPoint = new IPEndPoint(addresses[0], port);
     socket = new Socket(remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
     active = true;

     // recv & send are called from main thread.
     // need to ensure this never blocks.
     // even a 1ms block per connection would stop us from scaling.
     socket.Blocking = false;

     // configure buffer sizes
     Common.ConfigureSocketBuffers(socket, config.RecvBufferSize, config.SendBufferSize);

     // bind to endpoint so we can use send/recv instead of sendto/recvfrom.
     socket.Connect(remoteEndPoint);

     // immediately send a hello message to the server.
     // server will call OnMessage and add the new connection.
     // note that this still has cookie=0 until we receive the server's hello.
     SendHello();
 }

当然也可以调用Connect的其他重载

// 	Connect client to a NetworkServer by address. More...
static void 	Connect (string address)
//Connect client to a NetworkServer by Uri. More...
static void 	Connect (Uri uri)
//字如其名
static void 	ConnectHost ()

Disconnect ()

断开连接

active

在不想将MonoBehavior 改成NetworkBehavior时非常有用,使用NetworkBehavior可以在对象内使用isServer,IsClient来判断是否是服务器,还是客户端,但是这会增加带宽的负担,及时一个变量都不需要同步,所以该静态变量在全局判断时非常有用

activeHost

联网游戏存在单机模式,我们用Mirror变成完成之后使用Host模式即可将游戏变为单机模式,这里有一个技巧即可以使用 NetworkServer.dontListen = true;来不启动端口监听。因为Host模式调用远程ClientRpc时,时不会执行的,还有Spawn事件也不会执行,所以需要依靠这些判断该字段来附加执行属于Host的业务逻辑

NetworkServer

接下来看看NetworkServer,听名字就知道这个东西适合服务器相关的,这个普通开发人员也不需要关注,但是任然需要对他有所了解,如果是在不想管,那就记一个方法就行Spawn

Spawn

// 	Spawn the given game object on all clients which are ready. More...
static void 	Spawn (GameObject obj, NetworkConnection ownerConnection=null)
// 	Spawns an object and also assigns Client Authority to the specified client. More... 
static void 	Spawn (GameObject obj, GameObject ownerPlayer)
// 	Spawns an object and also assigns Client Authority to the specified client. More... 
static void 	Spawn (GameObject obj, Guid assetId, NetworkConnection ownerConnection=null)

一般使用他的流程是,在服务端上执行,首先实例化某个Prefab,Instantiate创建好GameObject obj = Instantiate(Prefab),在设置好属性位置啥的(除了transform以外其他都需要是同步属性,否则需要在客户端自行设置)然后调用NetworkServer.Spawn(obj)这样就可以把obj同步给所有端了。第二个 第三个参数是为了标识当前这个obj是属于那个客户端的,该逻辑会在Client2Server的传输模式非常有用,客户端会自行判断如果自己是owner就会同步数据给服务器,否则会直接跳过数据传输部分,服务器也会校验该对象是否允许客户端进行修改。服务器具有权威性,从游戏的数据安全角度考虑,Mirror设计了自己的鉴权逻辑,后面会说到。

Client2Server是数据传输的方向,该方向仅仅对 OwnerClient与Server之间有影响,其他关系都是客户端接收服务器的同步

ClientRpc 是一种注解,可以明确告知Mirror这是一个存在与客户端上的远程方法,Mirror会自动根据方法名调用所有客户端 对应对象的obj执行

Mirror从入门到入神(二)

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在`pyautocad`库中,并没有直接提供`Mirror`函数来实现对象的镜像操作。`pyautocad`库主要用于与AutoCAD进行交互,而具体的镜像操作需要使用AutoCAD的API来完成。 如果你想在`pyautocad`库中添加`Mirror`函数,你需要借助AutoCAD的COM接口来实现。COM接口允许你通过`pyautocad`库与AutoCAD进行通信,并调用AutoCAD的API。 以下是一个示例代码,演示如何使用COM接口来在`pyautocad`库中添加`Mirror`函数: ```python import win32com.client import pyautocad # 连接到AutoCAD应用程序 acad = win32com.client.Dispatch("AutoCAD.Application") # 获取AutoCAD的模型空间 msp = acad.ActiveDocument.ModelSpace # 选择要镜像的对象 objects = msp.SelectAll() # 定义镜像轴点 mirror_point = (0, 0, 0) # 定义镜像轴向量 mirror_vector = (1, 0, 0) # 以X轴为镜像轴 # 镜像对象 msp.Mirror(mirror_point, mirror_vector, objects) # 关闭AutoCAD应用程序 acad.Quit() ``` 在上述代码中,我们首先使用`win32com.client`库创建与AutoCAD的COM接口的连接。然后,通过`Dispatch`函数指定连接到AutoCAD应用程序。接下来,我们获取AutoCAD的模型空间,并选择要镜像的对象。然后,我们定义镜像的轴点和轴向量。最后,使用`Mirror`函数对选定的对象进行镜像操作。 请注意,为了运行上述代码,你需要安装`pywin32`库来支持COM接口的操作。你可以使用`pip install pywin32`命令来安装该库。 希望以上信息对你有所帮助。如有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值