【问题标题】:How do I sync non-player GameObject properties in UNet/Unity5?如何在 UNet/Unity5 中同步非玩家游戏对象属性?
【发布时间】:2016-02-01 21:10:51
【问题描述】:

我正在研究和学习 Unity 5、UNET 和网络的一些基础知识。我制作了一个简单的 3D 游戏,您可以在其中四处走动并更改对象的颜色。但我现在想将其制作成多人游戏,而且我在弄清楚如何通过网络发送更改以便所有玩家都可以看到单个玩家的颜色变化时遇到了很多麻烦。

部分问题在于使用较新的 UNET 网络引擎很难找到答案。有时我会遇到旧方法的答案。

所以主要问题是,我如何网络非玩家 GameObject 属性更改?颜色、形状、大小等。

这是我现在拥有的一些代码 - 我有许多不同的版本,所以我只发布当前版本:

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

 public class Player_Paint : NetworkBehaviour {

     private int range = 200;
     [SerializeField] private Transform camTransform;
     private RaycastHit hit;
     [SyncVar] private Color objectColor;
     [SyncVar] private GameObject objIdentity;

     void Update () {
         CheckIfPainting();
     }

     void CheckIfPainting(){
         if(Input.GetMouseButtonDown(0)) {
             if (Physics.Raycast (camTransform.TransformPoint (0, 0, 0.5f), camTransform.forward, out hit, range)) {
                 string objName = hit.transform.name;
                 CmdPaint(objName);
             }
         }
     }

     [ClientRpc]
     void RpcPaint(){
         objIdentity.GetComponent<Renderer>().material.color = objectColor;
     }

     [Command]
     void CmdPaint(string name) {
         objIdentity = GameObject.Find (name);  //tell us what was hit
         objectColor = new Color(Random.value, Random.value, Random.value, Random.value);
         RpcPaint ();
     }
 }

我尝试了更多解决方案,包括在我想要更改颜色的对象上编写单独的脚本,包括 [SyncVar] 和挂钩函数。我还尝试了 Debug.Log 对我期望更新客户端上的对象的每个函数,并且它们正在使用预期的数据执行。

我真的不知道还能做什么。我觉得这是我想做的一件非常简单的事情,但我在任何问题、教程或其他资源中都没有遇到过同步非玩家游戏对象的情况。任何想法都会有所帮助,谢谢。

【问题讨论】:

  • “我觉得这是我想做的一件非常简单的事情,但我在任何问题、教程或其他资源中都没有遇到过同步非玩家游戏对象的情况。”正是我的挫败感。
  • @MichaelS 或任何阅读的人,“一旦你知道如何去做”就非常简单。 这是一个非常好的解释 forum.unity.com/threads/…

标签: c# unity3d multiplayer unity5 unity-networking


【解决方案1】:

我找到了答案。这非常困难,因为我能找到的几乎每个问题、帖子、示例等……都是关于玩家对象的,而不是非玩家对象。

所以,我需要使用AssignClientAuthority 函数。我尝试了几次,但没有正确使用它。以下是适用于播放器的功能 C# 脚本:

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

public class Player_Paint : NetworkBehaviour {

    private int range = 200;
    [SerializeField] private Transform camTransform;
    private RaycastHit hit;
    [SyncVar] private Color objectColor;
    [SyncVar] private GameObject objectID;
    private NetworkIdentity objNetId;

    void Update () {
        // only do something if it is the local player doing it
        // so if player 1 does something, it will only be done on player 1's computer
        // but the networking scripts will make sure everyone else sees it
        if (isLocalPlayer) {
            CheckIfPainting ();
        }
    }

    void CheckIfPainting(){
        // yes, isLocalPlayer is redundant here, because that is already checked before this function is called
        // if it's the local player and their mouse is down, then they are "painting"
        if(isLocalPlayer && Input.GetMouseButtonDown(0)) {
            // here is the actual "painting" code
            // "paint" if the Raycast hits something in it's range
            if (Physics.Raycast (camTransform.TransformPoint (0, 0, 0.5f), camTransform.forward, out hit, range)) {
                objectID = GameObject.Find (hit.transform.name);                                    // this gets the object that is hit
                objectColor = new Color(Random.value, Random.value, Random.value, Random.value);    // I select the color here before doing anything else
                CmdPaint(objectID, objectColor);    // carry out the "painting" command
            }
        }
    }

    [ClientRpc]
    void RpcPaint(GameObject obj, Color col){
        obj.GetComponent<Renderer>().material.color = col;      // this is the line that actually makes the change in color happen
    }

    [Command]
    void CmdPaint(GameObject obj, Color col) {
        objNetId = obj.GetComponent<NetworkIdentity> ();        // get the object's network ID
        objNetId.AssignClientAuthority (connectionToClient);    // assign authority to the player who is changing the color
        RpcPaint (obj, col);                                    // usse a Client RPC function to "paint" the object on all clients
        objNetId.RemoveClientAuthority (connectionToClient);    // remove the authority from the player who changed the color
    }
}

!!!重要!!! 您想要影响的每个对象都必须有一个 NetworkIdentity 组件,并且它必须设置为 LocalPlayerAuthority

所以这个脚本只是为了改变一个随机的颜色,但你应该能够改变实际的东西来将它应用到材料的任何变化或你想与非玩家对象联网的任何其他东西上。 “应该”是最合适的词 - 我还没有尝试过任何其他功能。

编辑 - 添加更多 cmets 用于学习目的。

【讨论】:

  • 这就是我一直在寻找的答案!谢谢你。 AssignClientAuthority 和 RemoveClientAuthority 是必不可少的,我也错误地使用了它们。
  • 太棒了!!我很高兴能帮上忙!我很惊讶我在网上的任何地方都找不到这个答案。我还尝试了同样的脚本,只是简单地替换了调整大小而不是“重绘”的功能,它就这么简单。
  • @MichaelS 我能否详细了解它的工作原理,因为我在游戏中有一个玩家可以查看的对象,而另一个对象在他们查看时移动位置,然后查看离开它会回到原来的位置。几个小时后,我无法将其同步到两个玩家或让任何一个玩家都可以实际影响对象。
  • @Alan-DeanSimmonds 我在这里添加了更多的 cmets。在我的脚本中,我改变了对象的颜色,所以我使用了“绘画”——所以任何地方说绘画或绘画,它都会为你移动或移动。是物体移回原来位置的问题吗?或者这就是它应该做的事情,只是对两个玩家都没有动静?
  • @MichaelS 问题在于,如果服务器计算机与按钮交互,它会在两个连接的客户端上更新,但客户端计算机仅在其自己的计算机上更新。我只需要更好地了解如何更新通过脚本移动到另一个游戏对象上的对象上的变换。如果您想了解更多信息,我将创建一个新问题并添加图片。
【解决方案2】:

Unity 5.3.2p3 将客户端权限分配给非玩家对象

对于有兴趣进行此设置的任何人,这是我的方法

客户端 OnLocalPlayer 组件 -> 通过传递对象 NetworkInstanceId 调用命令来分配和删除对象权限。您可以添加任何 UI 以在此组件上调用这些方法

服务器端

    [Command]
    void CmdAssignObjectAuthority(NetworkInstanceId netInstanceId)
    {
        // Assign authority of this objects network instance id to the client
        NetworkServer.objects[netInstanceId].AssignClientAuthority(connectionToClient);
    }

    [Command]
    void CmdRemoveObjectAuthority(NetworkInstanceId netInstanceId)
    {
        // Removes the  authority of this object network instance id to the client
        NetworkServer.objects[netInstanceId].RemoveClientAuthority(connectionToClient);
    }  

客户端 3. 对象组件 ->
OnStartAuthority() - 允许向服务器发送命令 OnStopAuthority() - 不允许向服务器发送命令

仅此而已!

【讨论】:

  • 确实,不要忘记只有服务器才能做到这一点 - 所以客户端必须要求服务器做到这一点。这是理解问题的关键。
【解决方案3】:

2018 年:

而不是使用“分配对象权限”,

我真的建议简单地使用

.SpawnWithClientAuthority

真的很简单。

其实就是这么简单!

  [Command]
  void CmdPleaseSpawnSomething() {
 
        GameObject p = Instantiate(some_Prefab);
        NetworkServer.SpawnWithClientAuthority(p, connectionToClient);
    }

{在该代码中,请注意“connectionToClient”神奇地可用 - 它表示调用此命令的“客户端”。}

在客户端(您想“拥有”事物的那个人)上,只需致电CmdPleaseSpawnSomething()

我的意思是 - 就是这样,谢天谢地。

这里有一个很清楚的解释:

https://forum.unity.com/threads/assign-authority-to-local-client-gameobject.371113/#post-3592541

【讨论】:

  • 嗨,胖子。我以前看到过这种观点,但据我所知,这是少数人的观点。你的问题比读者对技术写作的期望要多得多,而且——当然——我宁愿你的材料根本不需要编辑。我会在标题上让位,但“希望有帮助”确实是多余的(其中 104 个)。
  • 对于它的价值,社区对连续编辑非常矛盾——有人说可以,有人说不行。我的策略是将连续编辑分组为小块,这样用户就不会被编辑淹没。不幸的是,到目前为止,唯一的投诉者(可能是四五个人?凭记忆)是非常任性的作者,他们不会在任何事情上让步(其中两个目前正在享受长期禁令,因为普遍的尴尬)。
  • 最重要的是,志愿编辑在这里是为了提高质量 - 请随意检查我迄今为止所做的约 50K 编辑。我相信它们符合社区的期望。
【解决方案4】:

我对这段代码做了一个小修改,并添加了脚本,如果我们放置玩家,他可以通过光线投射进行更改。

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

public class Raycasting_Object : NetworkBehaviour {

    private int range = 200;
//  [SerializeField] private Transform camTransform;
    private RaycastHit hit;
    [SyncVar] private Color objectColor;
    [SyncVar] private GameObject objectID;
    private NetworkIdentity objNetId;

    void Update () {
        if (isLocalPlayer) {    
            CheckIfPainting ();
        }
    }

    void CheckIfPainting(){

        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        Debug.DrawRay (ray.origin, ray.direction * 100, Color.cyan);

        if(isLocalPlayer && Input.GetMouseButtonDown(0)) {
            if (Physics.Raycast (ray.origin, ray.direction, out hit, range)) {
                objectID = GameObject.Find (hit.transform.name);                                    // this gets the object that is hit
                Debug.Log(hit.transform.name);
                objectColor = new Color(Random.value, Random.value, Random.value, Random.value);    // I select the color here before doing anything else
                CmdPaint(objectID, objectColor);
            }
        }

    }

    [ClientRpc]
    void RpcPaint(GameObject obj, Color col){
        obj.GetComponent<Renderer>().material.color = col;      // this is the line that actually makes the change in color happen
    }

    [Command]
    void CmdPaint(GameObject obj, Color col) {
        objNetId = obj.GetComponent<NetworkIdentity> ();        // get the object's network ID
        objNetId.AssignClientAuthority (connectionToClient);    // assign authority to the player who is changing the color
        RpcPaint (obj, col);                                    // usse a Client RPC function to "paint" the object on all clients
        objNetId.RemoveClientAuthority (connectionToClient);    // remove the authority from the player who changed the color
    }
}

【讨论】:

    猜你喜欢
    • 2016-12-21
    • 2017-02-13
    • 2018-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-05
    • 1970-01-01
    相关资源
    最近更新 更多