在 Unity 中使用 Unet 更改 GameObjects material

Change material on GameObjects with Unet in Unity

当我在任何客户端中单击某个游戏对象时,我想在所有客户端上更改该游戏对象的 material。我是 UNET 的新手,我认为我有一个概念上的缺陷。所以基本上我想做的是:

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

我的播放器:

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();
        }
    }

}

发生的事情是: 对象上的文本更改为适当的颜色,但 material 永远不会更改。如何更改 material?

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

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

来自[SyncVar] Docu

These variables will have their values sychronized from the server to clients

-> 不要更改 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 并将其设为 [SyncVar]hook

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

请注意,您可能会点击 child 或 parent 而不是您想要点击的实际 object .. 所以 evtl。您必须使用 GetComponentInChildrenGetComponentInParent.

在 child 或 parent 中查找 RPC_ColorChange 组件

RpcFunctions 在 Command 函数被处理后被调用有点违反直觉:

在播放器预制件上:

[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) 同步颜色变量()
2) RpcNextColor()

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