Mirror是一个简单高效的开源的Unity多人游戏网络框架。 官方文档链接: https://mirror-networking.gitbook.io/docs
NetworkBehaviour 是Mirror中的核心脚本基类之一,有必要熟悉它。
就像MonoBehaviour 有许多参数接口(transform )和生命周期钩子函数(Awake / Start ), 继承自MonoBehaviour 的NetworkBehaviour 也提供了很多接口供我们使用, 这里对常用的进行介绍。 大多数参数都可以根据接口注释(进入NetworkBehaviour的类定义,看标签)顾名思义,不过有些用法还是得要注意一下。
参数接口
isServer
如果是此对象是在服务器上运行的(包括服务器+客户端一体的主机),则返回true。 True if this object is on the server and has been spawned.
isServerOnly
如果是此对象仅在服务器运行的(而非包含客户端的主机),则返回true。 True if this object is on the server-only, not host.
isClient
如果此对象已经存在,且处于客户端上(包括主机的客户端),则返回true。 True if this object is on the client and has been spawned by the server.
isClientOnly
如果此对象已经存在,且仅处于客户端上(不包括主机的客户端),则返回true。 True if this object is on the client-only, not host.
isLocalPlayer
如果此对象是客户端本地属于自己的玩家,则返回true。 True if this object is the the client’s own local player. 注意 这里本地属于自己的玩家 特指由NetworkManager 在连接成功后自动生成的这一个槽位里面的对象: 如果像上一个博客的代码里面直接生成的预制体,虽然由本地控制生成,但是访问此参数将返回false , 这一点很重要,会在下一个博客里单独提及。
hasAuthority
如果本客户端对此对象具有权限,则返回true (此参数对于服务器无意义,因为服务器对所有对象有权限)。 This returns true if this object is the authoritative version of the object in the distributed network application.
注意 这里权限是一个很重要的概念,它决定了谁(如某个客户端)对一个对象有所有权并且能够控制它。 默认情况下只有**isLocalPlayer为true **的对象,客户端对它有权限。 具体情况下需要通过权限给与 赋予客户端对某个对象的所有权。 会在下一个博客具体解释。
netId
返回本对象的netIdentity 的标识ID,这是它在网络对象字典表NetworkIdentity.spawned 中的定位标识。 The unique network Id of this object (unique at runtime). public static readonly Dictionary<uint, NetworkIdentity> spawned
connectionToServer
仅 提供给isLocalPlayer为true的本地玩家对象 访问,得到本客户端与服务器之间连接通道。 The Network Connection associated with the Network Identity component attached to this game object. This is only valid for the local player object on the client, and is null for other player objects that may exist on the client.
connectionToClient
提供给服务器上的某些对象访问,这些对象要求是被某一个客户端所拥有的(具有权限 的),得到服务器与拥有本对象的客户端之间的连接通道。 The Network Connection associated with the Network Identity component attached to this game object. This is valid for game objects on the server that have been assigned to a specific client, e.g. player objects, pets, henchmen, or other objects a single client “owns”.
netIdentity
得到此对象的NetIdentity 。 Returns the NetworkIdentity of this object
ComponentIndex
得到此对象的挂载此脚本组件的组件序号 Returns the index of the component on this object
方法接口
void SetSyncVarDirtyBit(ulong dirtyBit)
在服务器上,标记该对象的某些SyncVar为脏 。 参数是用一个64位的二进制标识的标记变量, 每一位从低位到高位(从右往左)依次代表脚本中从上到下的某一个[SyncVar] 变量(或SyncList 等任何SyncObject)。 所谓脏 就表示这个变量需要更新同步(即使其值没有发送变化无需更新,也能通过该方法强制更新)
下面顺便用这个将上一章博客里的问题解决一下,代码如下: 就没有用到别人写的NewNetworkIdentityReference ,而是当其值为空时手动强制让服务器重新同步一下。 注意代码中调用SetSyncVarDirtyBit(2); 标识从上到下第二个[SyncVar] 变量, (SetSyncVarDirtyBit(4); 表第3个,SetSyncVarDirtyBit(6); 表第2和3都需要重新同步)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using Cinemachine;
public class PlayerController : NetworkBehaviour
{
Rigidbody rb;
CinemachineVirtualCamera cv;
public GameObject prefabWeapon;
[SyncVar]
bool isWeaponHolded = false;
[SyncVar]
//private NewNetworkIdentityReference weaponSpawned;
private NetworkIdentity weaponSpawned;
[Command]
private void SyncWeaponSpawnedAgain(){
SetSyncVarDirtyBit(2);
}
[Command]
void GetWeaponHold(){
if(isWeaponHolded)return;
isWeaponHolded = true;
GameObject tmp = GameObject.Instantiate(prefabWeapon);
NetworkServer.Spawn(tmp);
//weaponSpawned = new NewNetworkIdentityReference(tmp.GetComponent<NetworkIdentity>());
weaponSpawned = tmp.GetComponent<NetworkIdentity>();
}
private void Start() {
rb = GetComponent<Rigidbody>();
if(isLocalPlayer){
cv = GameObject.FindGameObjectWithTag("VCAM").GetComponent<CinemachineVirtualCamera>();
cv.Follow = this.transform;
cv.LookAt = this.transform;
}
}
private void Update() {
if(Input.GetKeyDown(KeyCode.Space)){
GetWeaponHold();
}
float moveDirX = Input.GetAxis("Horizontal");
float moveDirY = Input.GetAxis("Vertical");
rb.velocity = new Vector3(moveDirX * 3f, moveDirY * 3f, 0);
if(weaponSpawned){
//weaponSpawned.Value.transform.position = new Vector3(transform.position.x, transform.position.y+2, transform.position.z);
weaponSpawned.transform.position = new Vector3(transform.position.x, transform.position.y+2, transform.position.z);
}else{
}
}
}
bool IsDirty()
如果存在脏数据还未被同步,则返回true。 true if syncInterval elapsed and any SyncVar or SyncObject is dirty
void ClearAllDirtyBits()
将所有同步数据设为非脏。一般都是同步完后会自动调用的。 Clears all the dirty bits that were set by SetDirtyBits()
生命周期钩子函数
下面所有Start 类的回调,执行顺序是依次向下的。 Awake() 会早于下列所有,而Start() 一般会排在下列各start类回调之后,但不排除特殊情况。
void OnStartServer()
在调用此方法时,可以认为服务器上所有的对象数据均已经完成构造,可以访问。 在服务器启动时,服务器对象会调用此方法(包括纯服务器和主机的服务器)。 Like Start(), but only called on server and host.
void OnStopServer()
在服务器关闭时,服务器对象会调用此方法(包括纯服务器和主机的服务器)。 Stop event, only called on server and host.
void OnStartAuthority()
无论是创建时给予权限,还是中途给予权限,在拥有权限后会立即调用此方法。 如果是创建时给予,会早于OnStartClient 调用。 此时客户端拥有权限的对象的网络数据均完整,适合进行初始化其它脚本。 当某个物件被服务器批准权限给某个客户端后,在这个客户端上,会被调用,类似Start()。 called on owner client when assigned authority by the server. hasAuthority will be true for such objects in client context. Like Start(), but only called for objects the client has authority over.
void OnStopAuthority()
当某个物件被服务器从某个客户端取消权限后,在这个客户端上,会被调用。 called on owner client when authority is removed by the server.
void OnStartClient()
此时客户端对象的网络数据均完整,适合进行初始化其它脚本。 在客户端启动时,客户端对象会调用此方法(包括纯客户端和主机的客户端)。 Like Start(), but only called on client and host.
void OnStopClient()
在客户端关闭时,客户端对象会调用此方法(包括纯客户端和主机的客户端)。 Stop event, only called on client and host.
void OnStartLocalPlayer()
在OnStartClient 之后调用,仅在isLocalPlayer为true 的对象上调用。 called on clients after OnStartClient for the player game object on the local client
void OnStopLocalPlayer()
在OnStopClient 之前调用,仅在isLocalPlayer为true 的对象上调用。 called on clients before OnStopClient for the player game object on the local client
还有一些回调函数,涉及到自定义序列化反序列化,以后再说
bool OnSerialize(NetworkWriter writer, bool initialState)
void OnDeserialize(NetworkReader reader, bool initialState)
|