在Unity中使用Unet更改GameObjects上的材质

时间:2018-08-30 21:21:50

标签: unity3d gameobject unity3d-unet

我想在所有客户端上单击GameObject时在所有客户端上更改其材质。我是UNET的新手,我认为我在概念上存在缺陷。所以基本上我想做的是:

  1. 在NetworkPlayer上向场景中的对象发射光线
  2. 从播放器发送[Command]
  3. 在此[Command]中,调用对象上的[ClientRpc]
  4. [ClientRpc]中更改该对象的材料

我的播放器:

using UnityEngine;
using UnityEngine.Networking;

// This script is on my Game Player Prefab
// (removed the cam movement part)
public class CamMovement : NetworkBehaviour
{
    void Update()
    {
        if (!isLocalPlayer)
        {
            return;
        }

        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = cam.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
                CmdNextColor(hit.transform.gameObject);
        } 
    }

    [Command]
    public void CmdNextColor(GameObject hitObject)
    {
        RPC_ColorChange colorChange = hitObject.GetComponent<RPC_ColorChange>();
        if (colorChange != null)
        {
            colorChange.RpcNextColor();
        }
    }
}

我的对象

using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class RPC_ColorChange : NetworkBehaviour {

    public Material[] material;
    [SyncVar]
    int curColOfThisObject;
    Text text;

    private void Start()
    {
        text = GetComponentInChildren<Text>();
    }


    [ClientRpc]
    public void RpcNextColor()
    {
        if (!isClient)
            return;

        if (material.Length > 0)
        {
            Material curMaterial = this.GetComponent<MeshRenderer>().material;

            curColOfThisObject++;
            if (curColOfThisObject >= material.Length)
                curColOfThisObject = 0;

            curMaterial = material[curColOfThisObject];
        }
    }

    private void Update()
    {
        if (isClient)
        {
            text.text = "new color of this object: " + curColOfThisObject.ToString();
        }
    }

}

会发生什么: 对象上的文本将更改为适当的颜色,但是材质永远不会更改。如何更改材料?

奖金问题: 如果有人知道如何设计UNET游戏的好教程,请告诉我。

2 个答案:

答案 0 :(得分:0)

您的问题是您在客户端计算了curColOfThisObject的值,但同时使用了[SyncVar]

[SyncVar] Docu

  

这些变量的值将从服务器同步到客户端

->不要在RpcNextColor中更改在客户端上的值,而是已经在CmdNextColor中更改在服务器上。否则curColOfThisObject将立即被服务器上从未更改的默认值覆盖。我将把值作为[ClientRpc]中的参数传递给客户端,因此从技术上讲,您甚至根本不需要[SyncVar]

CamMovement

[Command]
public void CmdNextColor(GameObject hitObject)
{
    RPC_ColorChange colorChange = hitObject.GetComponent<RPC_ColorChange>();
    if (colorChange != null)
    {
        colorChange.NextColor();
        // after calculating a new curColOfThisObject send it to clients (doesn't require [SyncVar] anymore)
        colorChange.RpcNextColor(curColOfThisObject);
    }
}

RPC_ColorChange

// Make the calculation of the value on the server side
[Server]
private void NextColor()
{
    if (material.Length > 0)
    {
        Material curMaterial = this.GetComponent<MeshRenderer>().material;

        curColOfThisObject++;
        if (curColOfThisObject >= material.Length)
            curColOfThisObject = 0;

        // set the material also on the server
        curMaterial = material[curColOfThisObject];
    }
}

[ClientRpc]
public void RpcNextColor(int newValue)
{
    if (!isClient) return;

    // easier to debug if you keep the curColOfThisObject variable
    curColOfThisObject = newValue;

    if(newValue=> material.Length)
    {
        Debug.LogError("index not found in material");
        return;
    }

    // instead of curColOfThisObject  you could also just use the newValue
    // but this is easier to debug
    curMaterial = material[curColOfThisObject];
}

如果您想坚持使用[SyncVar],也可以完全跳过ClientRpc,而将hook设为[SyncVar]

CamMovement

[Command]
public void CmdNextColor(GameObject hitObject)
{
    RPC_ColorChange colorChange = hitObject.GetComponent<RPC_ColorChange>();
    if (colorChange != null)
    {
        colorChange.NextColor();
    }
}

RPC_ColorChange

[SyncVar(hook = "OnNextColor")]
private int curColOfThisObject;

// Make the calculation of the value on the server side
[Server]
private void NextColor()
{
    if (material.Length > 0)
    {
        Material curMaterial = this.GetComponent<MeshRenderer>().material;

        curColOfThisObject++;
        if (curColOfThisObject >= material.Length)
            curColOfThisObject = 0;

        // set the material also on the server
        curMaterial = material[curColOfThisObject];
    }
}

// This method automatically gets called when the value of
// curColOfObject is changed to newValue on the server
private void OnNextColor(int newValue)
{
    if (!isClient) return;

    // easier to debug if you keep the curColOfThisObject  variable
    curColOfThisObject = newValue;

    if(newValue=> material.Length)
    {
        Debug.LogError("index not found in material");
        return;
    }

    // instead of curColOfThisObject  you could also just use the newValue
    // but this is easier to debug
    curMaterial = material[curColOfThisObject];
}

一点点额外的东西:在通过网络发送内容之前,我会先检查RPC_ColorChange组件的存在。

if (Input.GetMouseButtonDown(0))
{
    Ray ray = cam.ScreenPointToRay(Input.mousePosition);
    RaycastHit hit;
    if (Physics.Raycast(ray, out hit))
    {
        if(hit.GetComponent<RPC_ColorChange>()!=null)
        {
            CmdNextColor(hit.transform.gameObject);
        }
    }
}

请注意,您可能击中了孩子或父母,而不是您想击中的实际物体。您将必须使用RPC_ColorChangeGetComponentInChildren在子代或父代中寻找GetComponentInParent组件。

答案 1 :(得分:0)

在处理Command函数之后调用RpcFunctions是有点反常理的:

在播放器预制板上:

[Command]
    public void CmdNextColor(GameObject hitObject)
    {
        RPC_ColorChange colorChange = hitObject.GetComponent<RPC_ColorChange>();
        if (colorChange != null)
        {
            int curColor = colorChange.GetCurColor();

            // change color +1 on the clients
            colorChange.RpcNextColor(curColor);

            // assign a SyncVar with the current color
            colorChange.SyncColorVar();
        }
    } 

现在按以下顺序调用函数:
1)SyncColorVar()
2)RpcNextColor()

测试项目:gitlab.com/KlausUllrich/networkTest