How do I sync non-player GameObject properties in UNet/Unity5?
Asked Answered
H

4

6

I'm working on and learning some basics of Unity 5, UNET, and networking. I made a simple 3D game where you go around and change the colors of objects. But I want to make it multiplayer now, and I am having lots of trouble figuring out how to send the changes over the network so all players can see a single player's color change.

Part of the issue is that it has been difficult to find the answer using the newer UNET networking engine. And sometimes I come across answers that are for the older way.

So the main question is, how do I network non-player GameObject property changes? Color, shape, size, etc..

Here is some code I have now - and I've had many different versions so I'll just post the current one:

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

I've tried a bunch more solutions, including writing a separate script on the objects whose color I want to change and including [SyncVar] and hook functions. I've also tried Debug.Log on each of the functions I'm expecting to update the objects on the clients and they are executing with the expected data.

I really don't know what else to do. I feel like it is a VERY simple thing I want to do, but I haven't come across syncing non-player GameObject's in any questions, tutorials, or other resources. Any ideas at all would be helpful, thank you.

Hardie answered 2/11, 2015 at 2:40 Comment(2)
"I feel like it is a VERY simple thing I want to do, but I haven't come across syncing non-player GameObject's in any questions, tutorials, or other resources." My frustrations EXACTLY.Blinker
@MichaelS or anyone reading, it's remarkably simple "once you know how to do it". Here's a really really good explanation forum.unity.com/threads/…Lurie
H
12

I found my answer. And it was very difficult, as almost every single question, post, example, etc... I could find was about player objects, and not non-player objects.

So, I needed to use the AssignClientAuthority function. Which I had tried a couple of times, but wasn't using it correctly. Here is the functioning C# script to apply to the player:

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

!!!Important!!! Each object that you want to affect must have a NetworkIdentity component, and it must be set to LocalPlayerAuthority

So this script is just to change a random color, but you should be able to change the actual stuff to apply this to any change in the materials or anything else you want to network with a non-player object. "Should" being the optimal word - I haven't tried with any other functionality yet.

EDIT - added more comments for learning purposes.

Hardie answered 4/11, 2015 at 2:48 Comment(8)
This is the answer I've been looking for! Thank you. The AssignClientAuthority and the RemoveClientAuthority were essential, and I was also using them incorrectly.Blinker
Great!! I'm glad I could help! I was seriously surprised I couldn't find this answer anywhere online. I also tried this same script with simply replacing the functionality to resize instead of "repaint" and it worked as simple as that.Hardie
@MichaelS can I get a rundown on exactly how this works, as I have an object in the game that a player can look at and another object moves location while they are looking at it, when then look away it moves back to it's original position. After many hours I am unable to get this to sync to both players or have it so either player can actually affect the object.Felicafelicdad
@Alan-DeanSimonds I added more comments here. In my script I was changing the color of the object so I used "painting" - so anywhere it says painting or paint, it would be moving or move for you. Is the problem that the object moves back to it's original position? Or is that what it's supposed to be doing and it's just not moving for both players?Hardie
@MichaelS The problem is that it updates on both connected clients if the server computer interacts with the button but the client computer only updates on it's own machine. I just need a better understanding how updating transforms on an object that is moved via script on another game object. If you want more information I will create a new question and add images.Felicafelicdad
@Alan-DeanSimonds sorry I missed your last comment. The only thing I can think of is that maybe the object doesn't have a NetworkIdentity component and/or it isn't set to LocalPlayerAuthority. If you do have that set, then I'd say create a new question with your code (if you didn't already)Hardie
I'm trying to understand why exactly it is necessary to "AssignClientAuthority" in the first place? My understanding of a command is that it is executed on the server. Doesn't the server always have authority to make modifications? I would expect that this code would only have to be called by a client, not by the server. My reasoning is that local authority means a client can interact with an object directly, without requiring it to go through the server. Where's the flaw in my thinking?Lucre
It's been a while since I look at this (or any Unity stuff). But if I remember correctly, the client (the player) is modifying the object (in RpcPaint()), so that is what they need the authority for. Then we RemoveClientAuthority so that (I think) the client can't make any more changes and the server can regain Authority and transmit those changes to all clients.Hardie
B
2

Unity 5.3.2p3 Assign Client Authority to Non Player Objects

For anyone interested in setting this up this is my approach

Client Side OnLocalPlayer component -> Call commands to assign and remove object authority by passing through the objects NetworkInstanceId. You can add any UI to call these methods on this component

Server Side

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

Client Side 3. Object component ->
OnStartAuthority() - allowed to send commands to server OnStopAuthority() - not allowed to send commands to server

That's all there is to it!

Bartholemy answered 19/2, 2016 at 11:30 Comment(1)
Indeed, don't forget that only the server can do it - so the client has to ask the server to do it. That's the key to understanding the problem.Lurie
L
1

For 2018:

Rather than using "Assign Object Authority",

I really recommend simply using

.SpawnWithClientAuthority

It's really very easy.

In fact it's this simple!

  [Command]
  void CmdPleaseSpawnSomething() {
 
        GameObject p = Instantiate(some_Prefab);
        NetworkServer.SpawnWithClientAuthority(p, connectionToClient);
    }

{In that code, note that "connectionToClient" is magically available with no effort - it means "the client" who called this command.}

On the client (the one you want to "own" the thing), just call CmdPleaseSpawnSomething().

I mean - that's all there is to it, thank goodness.

There's a long clear explanation here:

https://forum.unity.com/threads/assign-authority-to-local-client-gameobject.371113/#post-3592541

Lurie answered 14/8, 2018 at 16:54 Comment(3)
Hi Fattie. I have seen that view expressed before, but it's a minority view as far as I can tell. Your questions are significantly more chatty than readers will expect for technical writing, and - of course - I would rather your material did not need editing at all. I'll give way on the headings, but "hope that helps" really is quite redundant (104 of these).Cis
For what it's worth, the community is quite conflicted about serial editing - some people say it's OK, some people say it isn't. My policy is to group serial edits into small clumps so that users are not swamped with edits. Unfortunately, the only complainants so far (maybe four or five folks? from memory) are extraordinarily wilful authors who would not give way on anything (two of whom are presently enjoying lengthy bans for general awkwardness).Cis
The bottom line is that volunteer editors are here to improve quality - feel free to examine the ~50K edits I have made so far. I believe they are in line with community's expectations.Cis
P
0

i make a small modification to this code and add the possibility the script if we place the player he can make the change through the 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
    }
}
Parse answered 2/7, 2016 at 2:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.