(005)Mirror 远程调用

网络系统有多种方式进行指令的调用,通常被称为 **Remote Procedure Calls **,简称 RPCmirror 有两种方式:

  • Commands:从客户端发出指令,在服务器执行。
  • ClientRpc:从服务器发出指令,在客户端执行。

远程行为执行图:

在这里插入图片描述

Command

1.所有Command标记的函数,名字都要 Cmd 开头不能是静态函数

2.通常玩家自能控制自己的 Player,绕过权限限制使用 [Command(requiresAuthority = false)]

3.Command 函数支持的函数参数类型见 Data types

4.不要每帧调用 Command 函数,会造成比较大的网络开销。

public class Player : NetworkBehaviour
{
    // assigned in inspector
    public GameObject cubePrefab;

    void Update()
    {
        if (!isLocalPlayer) return;

        if (Input.GetKey(KeyCode.X))
            CmdDropCube();
    }

    [Command]
    void CmdDropCube()
    {
        if (cubePrefab != null)
        {
            Vector3 spawnPos = transform.position + transform.forward * 2;
            Quaternion spawnRot = transform.rotation;
            GameObject cube = Instantiate(cubePrefab, spawnPos, spawnRot);
            NetworkServer.Spawn(cube);
        }
    }
}

绕过权限限制

满足下列条件的时候,也可以在非 Player 游戏对象上,调用 Command 指令。

  • 对象是在客户授权下生成的。
  • 该对象已通过 NetworkIdentity.AssignClientAuthority 设置了客户权限。
  • 命令的 requirementsAuthority 选项设置为 false。
    • 命令方法签名中加入一个可选的 NetworkConnectionToClient sender= null 参数,Mirror 会为你设置 sender 变量。
    • 请勿尝试为该可选参数设置值…它将被忽略。

从这些对象发送的命令会在服务器实例对象上运行,而不是在客户端的实例对象上运行。

public enum DoorState : byte
{
    Open, Closed, Locked
}

public class Door : NetworkBehaviour
{
    [SyncVar]
    public DoorState doorState;
    
    [Client]
    void OnMouseUpAsButton()
    {
        CmdSetDoorState();
    }

    [Command(requiresAuthority = false)]
    public void CmdSetDoorState(NetworkConnectionToClient sender = null)
    {
        bool hasDoorKey = sender.identity.GetComponent<PlayerState>().hasDoorKey;
        
        if (doorState == DoorState.Open)
        {
            doorState = hasDoorKey ? DoorState.Locked : DoorState.Closed;
            return;
        }
        
        if (doorState == DoorState.Locked && hasDoorKey)
        {
            doorState = DoorState.Open;
            return;
        }
        
        if (doorState == DoorState.Closed)
            doorState = DoorState.Open;
    }
}

ClientRpc Calls

ClientRpc 调用是从服务器上的对象向客户端对象发送的。它们可以从任何已生成的具有 NetworkIdentity 的服务器对象发送。由于服务器具有权限,因此服务器对象发送这些调用不存在安全问题。要将一个函数转化为 ClientRpc 调用,可为其添加 [ClientRpc] 自定义属性,并可选择 添加 “Rpc ”前缀以符合命名规则。现在,在服务器上调用该函数时,客户端将运行该函数。任何允许数据类型的参数都将通过 ClientRpc 调用自动传递给客户端。

ClientRpc 函数的前缀应为 “Rpc”,并且不能是静态的。这是在阅读调用该方法的代码时的一个提示–该函数比较特殊,不会像普通函数那样在本地调用。

public class Player : NetworkBehaviour
{
    int health;

    public void TakeDamage(int amount)
    {
        if (!isServer) return;

        health -= amount;
        RpcDamage(amount);
    }

    [ClientRpc]
    public void RpcDamage(int amount)
    {
        Debug.Log("Took damage:" + amount);
    }
}

以主机身份运行带有本地客户端的游戏时,ClientRpc 调用将在本地客户端上调用,即使它与服务器处于同一进程中。因此,本地客户端和远程客户端的 ClientRpc 调用行为是一样的。

排除拥有者

ClientRpc 消息只会根据对象的网络可见性发送给对象的观察者。Player 对象始终是自己的观察者。在某些情况下,您可能希望在调用 ClientRpc 时排除所有者。这可以使用 includeOwner 选项来实现:[ClientRpc(includeOwner=false)]

TargetRpc 的调用

TargetRpc 函数由服务器上的用户代码调用,然后在 NetworkConnection 指定的客户端对象上调用。RPC 调用的参数在网络上进行序列化,因此客户端函数的调用值与服务器上的函数相同。这些函数应以前缀 “Target ”开头,以符合命名规则,并且不能是静态的。

public class Player : NetworkBehaviour
{
    public int health;

    [Command]
    void CmdMagic(GameObject target, int damage)
    {
        target.GetComponent<Player>().health -= damage;

        NetworkIdentity opponentIdentity = target.GetComponent<NetworkIdentity>();
        TargetDoMagic(opponentIdentity.connectionToClient, damage);
    }

    [TargetRpc]
    public void TargetDoMagic(NetworkConnectionToClient target, int damage)
    {
        // This will appear on the opponent's client, not the attacking player's
        Debug.Log($"Magic Damage = {damage}");
    }

    // Heal thyself
    [Command]
    public void CmdHealMe()
    {
        health += 10;
        TargetHealed(10);
    }

    [TargetRpc]
    public void TargetHealed(int amount)
    {
        // No NetworkConnection parameter, so it goes to owner
        Debug.Log($"Health increased by {amount}");
    }
}

远程调用的参数

传递给命令和 ClientRpc 调用的参数会被序列化并通过网络发送。支持类型见 supported mirror type.

远程调用操作的参数不能是游戏对象的子组件,如脚本实例或 Transform 组件。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值