Изменить материал на GameObjects с Unet в Unity - PullRequest
0 голосов
/ 31 августа 2018

Я хочу изменить материал 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 голосов
/ 06 сентября 2018

Немного не интуитивно понятно, что 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

0 голосов
/ 03 сентября 2018

Ваша проблема в том, что вы вычисляете значение curColOfThisObject на стороне клиента, но в то же время используете для него [SyncVar].

Из [SyncVar] Документ :

Значения этих переменных будут синхронизированы от сервера к клиентам

-> Не изменять значения на клиентах в 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);
        }
    }
}

Имейте в виду, что вы можете ударить ребенка или родителя вместо реального объекта, который вы хотели бы ударить .. так что evtl. вам придется искать компонент RPC_ColorChange в потомках или родителях, используя GetComponentInChildren или GetComponentInParent.

...