[Unity Mirror] 远程操作

81 篇文章 31 订阅

英文原文:

https://mirror-networking.gitbook.io/docs/guides/communications/remote-actions

  网络系统具有跨网络执行动作的方法。这些类型的操作有时称为远程过程调用。网络系统中有两种类型的 RPC,Commands - 从客户端调用并在服务器上运行;和 ClientRpc 调用 - 在服务器上调用并在客户端上运行。

下图显示了远程操作采取的方向:

在这里插入图片描述


命令

  命令从客户端上的玩家对象发送到服务器上的玩家对象。为安全起见,默认情况下只能从您的玩家对象发送命令,因此您无法控制其他玩家的对象。您可以使用 [Command(requiresAuthority = false)] 绕过权限检查。

  要将函数变成命令,请为其添加 [Command] 自定义属性,并可选择添加“Cmd”前缀以进行命名约定。现在,当在客户端调用此函数时,它将在服务器上运行。任何允许数据类型的参数都将通过命令自动传递给服务器。

  命令函数应该有前缀“Cmd”并且不能是静态的。这是读取调用命令的代码时的提示 - 此函数是特殊的,不像普通函数那样在本地调用。

public class Player : NetworkBehaviour
{
    // 在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);
        }
    }
}

小心每一帧都从客户端发送命令!这可能会导致大量网络流量。

绕过权威

如果以下任何一项为真,则可以在非玩家对象上调用命令:

  • 该对象是使用客户端权限生成的
  • 该对象具有使用 NetworkIdentity.AssignClientAuthority 设置的客户端权限
  • 该命令将 requiresAuthority 选项设置为 false。
    • 您可以在 Command 方法签名中包含可选的 NetworkConnectionToClient sender = null 参数,Mirror 将为您填写发送客户端。
    • 不要尝试为此可选参数设置值…它将被忽略。

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

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 调用

  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 消息仅根据其网络可见性发送给对象的观察者。玩家对象总是自己的观察者。在某些情况下,您可能希望在调用 ClientRpc 时排除所有者客户端。这是通过 includeOwner 选项完成的:[ClientRpc(includeOwner = false)]。


TargetRpc 调用

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

上下文事项:

  • 如果 TargetRpc 方法的第一个参数是 NetworkConnection,那么无论上下文如何,该连接都会接收到消息。
  • 如果第一个参数是任何其他类型,那么具有包含 TargetRpc 的脚本的对象的所有者客户端将收到该消息。

  此示例显示客户端如何使用命令向服务器 (CmdMagic) 发出请求,方法是将另一个玩家的 connectionToClient 作为直接从该命令调用的 TargetRpc 的参数之一:

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(NetworkConnection target, int damage)
    {
        // 这将出现在对手的客户端,而不是攻击玩家的客户端
        Debug.Log($"Magic Damage = {damage}");
    }

    // 治愈自己
    [Command]
    public void CmdHealMe()
    {
        health += 10;
        TargetHealed(10);
    }

    [TargetRpc]
    public void TargetHealed(int amount)
    {
        // 没有 NetworkConnection 参数,所以它转到所有者
        Debug.Log($"Health increased by {amount}");
    }
}

远程操作的参数

  传递给命令和 ClientRpc 调用的参数被序列化并通过网络发送。您可以使用任何受支持的 Mirror 类型。

  远程操作的参数不能是gameobject的子组件,例如脚本实例或transform。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值