UNET - 允许任何玩家向同一NetworkIdentity发送命令

4

Unity 版本: 5.5

场景示例:

  • 灯光 [一个带有 Light 组件的 GameObject]
  • 灯开关 - [包含: BoxCollider|NetworkIdentity|从 NetworkBehaviour 继承的脚本,在点击其 BoxCollider 时切换灯光的开关状态]

LightSwitch.cs

public class LightSwitch : NetworkBehaviour
{
    public GameObject roomLight;

    void OnMouseDown () {
        CmdToggleLight(!roomLight.activeSelf);
    }

    [Command]
    void CmdToggleLight (bool status) {
        RpcToggleLight(status);
    }

    [ClientRpc]
    void RpcToggleLight (bool status) {
        roomLight.SetActive(status);
    }
}

我该如何让任何玩家点击那个开关,从而切换灯的开/关状态呢?

编辑: 参考这个示例,这是我需要构建的代码:

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class LightSwitch : NetworkBehaviour
{
    public GameObject roomLight;

    [SyncVar(hook="setLight")]
    private bool status;


    void OnMouseDown()
    {
        // my player's singleton
        Player.singleton.CmdToggleSwitch(this.gameObject);
    }

    public void toggleLight()
    {
        status = !status;
    }

    void setLight(bool newStatus)
    {
        roomLight.SetActive(newStatus);
    }

    [ClientRpc]
    public void RpcToggleSwitch(GameObject switchObject) 
    {
        switchObject.GetComponent<LightSwitch>().toggleLight();
    }
}

Player.cs 代码:

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;
using System;

public class Player : NetworkBehaviour {

    public static Player singleton;

    void Start () {
        singleton = this;
    }

    //... my player class code ....//

    [Command]
    public void CmdToggleSwitch(GameObject gObject)
    {
        gObject.GetComponent<LightSwitch>().RpcToggleSwitch(gObject);
    }
}

一个简单的灯光切换,却需要一个巨大的翔,感谢Unity。
1个回答

3

很遗憾,命令只能从客户端玩家对象发送。

根据文档:

[Command]函数是在与连接相关联的玩家GameObject上调用的。

你可以在玩家对象上放置一个切换功能,该功能传递你想要切换的对象的网络标识。例如:

[Command]
void CmdToggleSwitch (GameObject switch) //switch must have network identity component
{
  //Set the switch state on the server
  switch.GetComponent<LightSwitch>().ToggleMethod();
}

然后在服务器上,您可以调用RPC或将灯开关状态设置为SyncVar。后一种方法可能更容易和更可靠(即新玩家将自动设置正确的灯开关状态)。

以下是一个(未经测试的)示例:

using UnityEngine;
using UnityEngine.Networking;

public class RaycastExample : NetworkBehaviour

    void ToggleSwitch()
        {
            RaycastHit hit;
            //Use raycasting to find switch
            if (Physics.Raycast(transform.position, transform.forwards, out hit, 5.0f))
            {
                if (hit.GetComponent<LightSwitch>())
                {
                    //Send the command to toggle the switch of the server, passing in the LightSwitch gameobject as the parameter
                    CmdToggleSwitch (hit.transform.gameObject);
                }
            }
    }

    //This will be run on the server version of our player
    [Command]
    void CmdToggleSwitch (GameObject switch) //switch must have network identity component
    {
      //Set the switch state on the server
      //Server light switch will need some way to notify the clients of the change. RPC or SyncVar.
      switch.GetComponent<LightSwitch>().ToggleMethod();
    }

编辑:

这是我项目中的一段代码片段。这个片段来自于附加在网络玩家对象上的脚本,我用它来控制玩家的健康状况。

    [SyncVar(hook="OnHealth")]
    public float currentHealth = 100f;

首先,我声明了一个变量来保存玩家的健康值,并将其分配给SyncVar属性。通过分配这个属性,服务器将在服务器上更改此变量时,在所有客户端上更新此变量。我还添加了钩子参数,导致在更新此变量时调用OnHealth()函数。

void OnHealth (float newHealth) {
    currentHealth = newHealth;
}

这里是OnHealth函数。每当服务器通知客户端当前生命值(currentHealth)发生变化时,都会调用此函数。它还将新的currentHealth值作为参数传递,我们只需要手动将此传递的值分配为新的currentHealth即可。

    public void TakeDamage (float damage) {
        if (!isServer)
            return;

        currentHealth -= damage;
        if (currentHealth < 0)
            currentHealth = 0;
    }

我还有一个公共函数,允许玩家受到伤害。请注意该函数的第一行中的检查,我只关心伤害是否发生在服务器上。SyncVar属性会导致当前健康状态的更改在客户端之间同步。在这个特定的游戏中,只有当玩家被抛射物击中时才会受到伤害。通过这种方式调用伤害函数,服务器成为真正的来源(如果客户端延迟,则可能出现抛射物看起来错过了但实际命中或反之的情况- 在这种情况下,使用[Command]属性可能会导致冲突的结果)。
    [Command]
    public void CmdHeal(float heal) {
            currentHealth += heal;
    }

在这个游戏中,还有一个治疗功能,当玩家按下某个键时会触发。由于服务器不知道何时按下这个键,我不能像处理伤害函数那样更新健康值。因此,我使用了 [Command] 属性。因此,流程如下:玩家按下键 -> 客户端通知服务器已调用 CmdHeal -> CmdHeal 在服务器上执行 -> 服务器通知所有客户端当前健康值已更改。


很抱歉,我不理解你的解决方案。有了这个示例场景:
  • 我将您在LightSwitch.cs中制作的CmdToggleSwitch放置在同一GameObject中,因为该脚本附加在NetworkIdentity所在的GameObject上。
  • 我如何从任何玩家调用该函数?正如您所说,[Command]方法只能由NetworkIdentity所有者调用:s
- FrakyDale
抱歉我没有表达清楚。CmdToggleSwitch应该位于附加到玩家对象的脚本中,而不是在灯开关上。然后,在服务器版本的玩家上执行该命令,然后调用服务器灯开关上的ToggleMethod()。然后,通过RPC或SyncVar将灯开关的更改发送给客户端。 - Chairs
需要记住的主要事项是,每当您在脚本中使用[Commad]时,它必须附加到您的网络播放器上。 - Chairs
问题已更新,发布了最终代码。 我讨厌为了切换一个灯而不得不编写如此多的代码。使用Photon Network只需两种方法就可以完成... - FrakyDale
@Chairs,我想知道是否可以提供一个完整的示例,包括单独的脚本和包含SyncVar的对象,因为我对它应该放在哪里的理解不足。先感谢您。 - Alan-Dean Simonds
@Alan-DeanSimonds 我更新了帖子,包括我自己项目中的一个示例脚本。虽然在这个示例中SyncVar和[Command]函数在同一个对象上,但它们也可以在不同的脚本中。 - Chairs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接