Как синхронизировать неигровые свойства GameObject в UNet/Unity5?

Я работаю и изучаю некоторые основы 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 для каждой из функций, которые, как я ожидаю, будут обновлять объекты на клиентах, и они выполняются с ожидаемыми данными.

Я действительно не знаю, что еще делать. Я чувствую, что это ОЧЕНЬ простая вещь, которую я хочу сделать, но я не сталкивался с синхронизацией неигровых GameObject в каких-либо вопросах, учебниках или других ресурсах. Любые идеи вообще будут полезны, спасибо.


person Michael S    schedule 02.11.2015    source источник
comment
Я чувствую, что это ОЧЕНЬ простая вещь, которую я хочу сделать, но я не сталкивался с синхронизацией неигровых GameObject в каких-либо вопросах, учебниках или других ресурсах. Мои разочарования ТОЧНО.   -  person Daryl Bennett    schedule 12.12.2015
comment
@MichaelS или любой читающий, это удивительно просто, если вы знаете, как это сделать. Вот очень хорошее объяснение forum.unity.com/threads/   -  person Fattie    schedule 14.08.2018


Ответы (4)


Я нашел свой ответ. И это было очень сложно, так как почти каждый вопрос, пост, пример и т. д., которые я мог найти, касался игровых объектов, а не неигровых объектов.

Итак, мне нужно было использовать функцию 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.

Таким образом, этот скрипт предназначен только для изменения случайного цвета, но вы должны иметь возможность изменить фактические вещи, чтобы применить это к любым изменениям в материалах или чему-либо еще, что вы хотите связать с объектом, не являющимся игроком. "Должен" - оптимальное слово - я еще не пробовал с какой-либо другой функциональностью.

РЕДАКТИРОВАТЬ - добавлено больше комментариев для учебных целей.

person Michael S    schedule 04.11.2015
comment
Это ответ, который я искал! Спасибо. AssignClientAuthority и RemoveClientAuthority были необходимы, и я также использовал их неправильно. - person Daryl Bennett; 12.12.2015
comment
Большой!! Я рад, что смог помочь! Я был серьезно удивлен, что не смог найти этот ответ нигде в Интернете. Я также попробовал этот же скрипт, просто заменив функциональность на изменение размера вместо перерисовки, и это сработало так же просто. - person Michael S; 15.12.2015
comment
@MichaelS, могу ли я получить краткое изложение того, как именно это работает, поскольку у меня есть объект в игре, на который игрок может смотреть, и другой объект перемещается, пока они смотрят на него, а когда он отводит взгляд, он возвращается в исходное положение. . По прошествии многих часов я не могу синхронизировать это с обоими игроками или иметь его, чтобы любой игрок мог фактически повлиять на объект. - person Alan-Dean Simonds; 08.08.2016
comment
@Alan-DeanSimonds Я добавил здесь больше комментариев. В моем сценарии я менял цвет объекта, поэтому я использовал рисование — так что везде, где написано рисование или рисование, оно будет двигаться или двигаться для вас. Проблема в том, что объект возвращается в исходное положение? Или это то, что он должен делать, и он просто не двигается для обоих игроков? - person Michael S; 08.08.2016
comment
@MichaelS Проблема в том, что он обновляется на обоих подключенных клиентах, если серверный компьютер взаимодействует с кнопкой, но клиентский компьютер обновляется только на своем собственном компьютере. Мне просто нужно лучше понять, как обновление преобразует объект, который перемещается с помощью скрипта на другой игровой объект. Если вам нужна дополнительная информация, я создам новый вопрос и добавлю изображения. - person Alan-Dean Simonds; 09.08.2016
comment
@Alan-DeanSimonds, извините, я пропустил ваш последний комментарий. Единственное, о чем я могу думать, это то, что, возможно, у объекта нет компонента NetworkIdentity и/или для него не установлено значение LocalPlayerAuthority. Если у вас есть этот набор, я бы сказал, создайте новый вопрос с вашим кодом (если вы еще этого не сделали) - person Michael S; 24.08.2016
comment
Я пытаюсь понять, почему именно AssignClientAuthority необходимо в первую очередь? Я понимаю команду так, что она выполняется на сервере. Разве сервер не всегда имеет право вносить изменения? Я ожидаю, что этот код должен вызываться только клиентом, а не сервером. Я полагаю, что локальные полномочия означают, что клиент может напрямую взаимодействовать с объектом, не требуя, чтобы он проходил через сервер. Где ошибка в моем мышлении? - person Tovi7; 03.09.2017
comment
Прошло некоторое время с тех пор, как я смотрел на это (или на любой другой материал Unity). Но если я правильно помню, клиент (игрок) модифицирует объект (in RpcPaint()), так что для этого ему нужны полномочия. Затем мы RemoveClientAuthority так что (я думаю) клиент больше не может вносить изменения, а сервер может восстановить полномочия и передать эти изменения всем клиентам. - person Michael S; 05.09.2017

Unity 5.3.2p3 Назначение полномочий клиента неигровым объектам

Для всех, кто заинтересован в настройке, это мой подход

Компонент OnLocalPlayer на стороне клиента -> Команды вызова для назначения и удаления полномочий объекта путем передачи через объекты NetworkInstanceId. Вы можете добавить любой пользовательский интерфейс для вызова этих методов в этом компоненте.

Сторона сервера

    [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() - запрещено отправлять команды на сервер

Вот и все!

person GXMark    schedule 19.02.2016
comment
Действительно, не забывайте, что это может сделать только сервер, поэтому клиент должен попросить сервер сделать это. Это ключ к пониманию проблемы. - person Fattie; 14.08.2018

На 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

person Fattie    schedule 14.08.2018
comment
Привет Фатти. Я видел это мнение, выраженное раньше, но, насколько я могу судить, это мнение меньшинства. Ваши вопросы значительно более болтливы, чем читатели ожидают от технического письма, и, конечно же, я бы предпочел, чтобы ваш материал вообще не нуждался в редактировании. Я уступлю заголовки, но надежда на то, что это действительно поможет, совершенно излишняя (104 из них). - person halfer; 15.08.2018
comment
Что бы это ни стоило, сообщество весьма противоречиво относится к серийному редактированию — некоторые люди говорят, что это нормально, некоторые говорят, что это не так. Моя политика состоит в том, чтобы группировать последовательные правки в небольшие группы, чтобы пользователи не были завалены правками. К сожалению, единственные жалобщики пока (может быть, четыре или пять человек? по памяти) - это необычайно своенравные авторы, которые ни в чем не уступят (двое из которых в настоящее время пользуются длительными банами за общую неуклюжесть). - person halfer; 15.08.2018
comment
Суть в том, что редакторы-добровольцы здесь, чтобы улучшить качество — не стесняйтесь проверять ~ 50 000 правок, которые я сделал до сих пор. Я считаю, что они соответствуют ожиданиям сообщества. - person halfer; 15.08.2018

Я делаю небольшую модификацию этого кода и добавляю возможность скрипта, если мы разместим игрока, он сможет внести изменения через raycasting.

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
    }
}
person Julio Quintero    schedule 02.07.2016