Mirror是一个简单高效的开源的Unity多人游戏网络框架。 官方文档链接: https://mirror-networking.gitbook.io/docs
在之间的几章博客里,已经基本介绍了UnityMirror的各种有用的概念。 但是并没有详细介绍如何在Mirror中,随心所欲地控制任何一个对象。 如果有读者仿照我之前的实验代码去自己实验,会发现很多问题, 比如rigidbody.velocity ,transform.position 无法在本地直接设定等。
当然可以通过[Command] 等通知服务器进行控制,间接控制本地, 不过个人觉得不太好,有可能导致严重的延迟,客户端还是应该优先强调本地的控制。
本节将权限和控制一起介绍,旨在能实现各个客户端随心所欲控制自己想要控制的对象,而保持良好的同步~
权限
权限是Mirror中一个很重要的概念,它用来描述一个客户端对某个对象的所有权。
运动控制
在Mirror中,由于需要在服务器上获取速度计算物理效果,使用本地的Rigidbody 组件是无法完成这个效果的,而且也无法实现同步控制。 因为Mirror不会自动去序列化RigidBody 的信息进行同步。
因此此时只会出现两种状况: 服务器可以实现控制,并通过服务器->客户端的同步实现同步运动,这是因为对象默认自带了NetworkTransform 组件。 客户端可以实现控制,但由于没有从客户端->服务器的同步,服务器看不到对象的运动。
我们需要挂载NetworkRigidbody 组件, 来实现客户端对有权限的物体的运动控制 向服务器的同步。
挂载脚本后,直接控制自己的RigidBody即可 这个脚本暂时属于Mirror的Experimental命名空间中,如果需要在自己的脚本中访问控制,需要using Mirror.Experimental;指定 可能遇到出乎意料的问题,大可以往Mirror的github里扔Issue https://github.com/vis2k/Mirror
使用
在玩家预制体上挂载NetworkRigidbody 组件, 并且勾选Client Authority ,这样才能从客户端向服务器同步
代码案例如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using Cinemachine;
public class PlayerController : NetworkBehaviour
{
Rigidbody rb;
CinemachineVirtualCamera cv;
MaterialPropertyBlock prop;
public GameObject prefabWeapon;
[Command]
void spawnWeapon(){
GameObject tmp = GameObject.Instantiate(prefabWeapon);
NetworkServer.Spawn(tmp);
tmp.transform.position = new Vector3(transform.position.x, transform.position.y+2, transform.position.z);
}
private void Start() {
rb = GetComponent<Rigidbody>();
if(isLocalPlayer){
cv = GameObject.FindGameObjectWithTag("VCAM").GetComponent<CinemachineVirtualCamera>();
cv.LookAt = this.transform;
prop = new MaterialPropertyBlock();
GetComponent<Renderer>().GetPropertyBlock(prop);
prop.SetColor("_Color", (Color.HSVToRGB(Random.Range(0,360), 0.8f, 0.8f)));
GetComponent<Renderer>().SetPropertyBlock(prop);
}
}
float moveDirX;
float moveDirY;
private void Update() {
if(isLocalPlayer){
if(Input.GetKeyDown(KeyCode.Space)){
spawnWeapon();
}
moveDirX = Input.GetAxisRaw("Horizontal");
moveDirY = Input.GetAxisRaw("Vertical");
rb.velocity = new Vector3(3 * moveDirX, 3*moveDirY, 0);
}
}
}
运行一个纯服务器和一个客户端,在客户端上使用键盘控制实体,可以实现同步:
多端同步也是一致的:
权限给予
角色控制脚本里已经写了按下空格键生成武器的代码。 我们先不让武器跟随,而是将相同的这个角色控制脚本挂载到武器预制体上,同时也将NetworkRigidbody 挂载上,且勾选Client Authority :
虽然期望武器同自己的角色能一起运动,但事实是无法跟随:
这里有两个问题需要修改:
isLocalPlayer 与 hasAuthority
isLocalPlayer为true的对象,在客户端全体对象里,只有唯一一个。(由连接到服务器生成的本地玩家)
因此想要本地控制的其它对象需要必须使用hasAuthority ,而不能使用isLocalPlayer 。 我们这里直接将isLocalPlayer 替换成hasAuthority :
//在 第36行 Update()中
if(isLocalPlayer)
=>
if(hasAuthority)
服务器给予客户端对象权限
此时运行依然无法两个都控制, 原因是isLocalPlayer为true 的对象默认hasAuthority为true ,
但是其它在服务器生成并同步到客户端的对象默认在所有客户端hasAuthority为false 。包括主机的客户端
因此需要在服务器上给予某个客户端对某个对象的权限: 使用NetworkServer.Spawn 方法同时携带到客户端的连接通道,是最简单的设置方式。
//在 第18行 spawnWeapon()中
NetworkServer.Spawn(tmp);
=>
NetworkServer.Spawn(tmp,connectionToClient);
此时就可以完成两个都控制了。
权限中途分配与取消
有的时候某个对象不是一直属于一个客户端的。
可以通过identity.AssignClientAuthority(conn); 进行对象生成完毕后,后续权限的分配。
可以通过identity.RemoveClientAuthority(); 让这个对象不属于任何一个客户端。
注意localPlayer不可以被取消权限,但是可以通过NetworkServer.ReplacePlayerForConnection(conn, newGameObj); 被替换
同一对象多客户端权限是不被允许的,即使中途分配,也只认第一个为主人,先Remove再Assign
|