如何在UNet / Unity5中同步非玩家GameObject属性?

时间:2015-11-02 02:40:45

标签: c# unity3d multiplayer unity5 unity-networking

我正在研究和学习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]和钩子函数。我还尝试过调试。我希望更新客户端上的对象并使用预期数据执行的每个函数。

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

4 个答案:

答案 0 :(得分:11)

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

所以,我需要使用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

因此,此脚本只是为了更改随机颜色,但您应该能够更改实际内容以将其应用于材质的任何更改或您要与非玩家对象联网的任何其他内容。 “应该”是最佳词 - 我还没有尝试过任何其他功能。

编辑 - 为学习目的添加了更多评论。

答案 1 :(得分:2)

Unity 5.3.2p3将客户端权限分配给非播放器对象

对于有兴趣设置这个的人来说,这是我的方法

客户端 OnLocalPlayer组件 - &gt;通过传递对象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.对象组件 - &gt;
    OnStartAuthority() - 允许向服务器发送命令     OnStopAuthority() - 不允许向服务器发送命令

这就是它的全部!

答案 2 :(得分:1)

对于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

答案 3 :(得分:0)

我对这段代码进行了一些小的修改,并添加了脚本的可能性,如果我们放置播放器他可以通过光线投射进行更改。

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
    }
}