UNET - 让任何玩家向同一个 NetworkIdentity 发送命令
UNET - Let any player send Commands to the same NetworkIdentity
Unity 版本: 5.5
场景示例:
- 灯光[带有灯光组件的游戏对象]
- LightSwitch - [包含:BoxCollider|NetworkIdentity|继承自 NetworkBehaviour 的脚本,当有人点击 BoxCollider 时切换灯光 on/off]
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);
}
}
¿如何让任何玩家单击那个 LightSwitch 并切换灯光 on/off?
编辑:
按照示例,这是我必须构建的代码:
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,只是为了切换灯光而已。
不幸的是,命令只能从客户端播放器对象发送。
来自文档:
[Command] functions are invoked on the player GameObject associated with a connection.
您可以做的是在播放器对象上放置一个切换函数,该函数传递您要切换的对象的网络标识。例如:
[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;
}
我还有一个public功能可以让玩家受到伤害。注意这个函数第一行的检查,我只关心服务器是否发生损坏。 SyncVar 属性将导致 currentHealth 中的更改在客户端之间同步。在这个特定的游戏中,玩家只有在被射弹击中时才会受到伤害。通过以这种方式调用损坏函数,服务器成为真实的来源(如果客户端滞后,射弹可能会在应该击中时出现错过,反之亦然 - 在这种情况下,使用 [Command] 属性可能会导致结果冲突).
[Command]
public void CmdHeal(float heal) {
currentHealth += heal;
}
在这个游戏中还有一个治疗功能,当玩家按下某个键时会发生。由于服务器不知道该键何时被按下,我无法像使用损坏功能那样更新健康状况。因此,我使用了 [Command] 属性。所以这是流程:玩家按下键 -> 客户端通知服务器调用了 CmdHeal -> CmdHeal 在服务器上执行 -> 服务器通知所有客户端 currentHealth 已更改。
Unity 版本: 5.5
场景示例:
- 灯光[带有灯光组件的游戏对象]
- LightSwitch - [包含:BoxCollider|NetworkIdentity|继承自 NetworkBehaviour 的脚本,当有人点击 BoxCollider 时切换灯光 on/off]
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);
}
}
¿如何让任何玩家单击那个 LightSwitch 并切换灯光 on/off?
编辑: 按照示例,这是我必须构建的代码:
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,只是为了切换灯光而已。
不幸的是,命令只能从客户端播放器对象发送。 来自文档:
[Command] functions are invoked on the player GameObject associated with a connection.
您可以做的是在播放器对象上放置一个切换函数,该函数传递您要切换的对象的网络标识。例如:
[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;
}
我还有一个public功能可以让玩家受到伤害。注意这个函数第一行的检查,我只关心服务器是否发生损坏。 SyncVar 属性将导致 currentHealth 中的更改在客户端之间同步。在这个特定的游戏中,玩家只有在被射弹击中时才会受到伤害。通过以这种方式调用损坏函数,服务器成为真实的来源(如果客户端滞后,射弹可能会在应该击中时出现错过,反之亦然 - 在这种情况下,使用 [Command] 属性可能会导致结果冲突).
[Command]
public void CmdHeal(float heal) {
currentHealth += heal;
}
在这个游戏中还有一个治疗功能,当玩家按下某个键时会发生。由于服务器不知道该键何时被按下,我无法像使用损坏功能那样更新健康状况。因此,我使用了 [Command] 属性。所以这是流程:玩家按下键 -> 客户端通知服务器调用了 CmdHeal -> CmdHeal 在服务器上执行 -> 服务器通知所有客户端 currentHealth 已更改。