Netcode - Clients to share control on the same gameObject

Hi all, I’m quite new to using Netcode-for-GameObjects and I tried to develop the core idea for later creating an app with.
The main idea is to have a server (or host) and multiple clients connected, and all of them be able to interact with the same object. For now, I’ve just started with rotating a simple cube and already found several problems.

  • Only the Host (I’m just testing my code with two clients, the host and a client) can rotate and “apply” the changes that the clients can see. I’ve read about Server Authority and tried also to call methods with RPC to be executed on the server side, but it seems it’s not enough to work.

Am I missing something key?

I’m dropping a screenshot to show what I’m working on, even though it’s pretty simple for now.
Any help is appreciated!

Just to clarify, intObj is the gameobject that I want to rotate with the clients, and objRot is the private variable that is being changed to later tell the manager (owned by the server) to apply those values on the rotation.

using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.PlayerLoop;

public class OwnerShip : NetworkBehaviour
{
    [SerializeField] private float rotSpeed;
    [SerializeField] private GameObject intObj;
    NetworkVariable<Vector3> objRot = new NetworkVariable<Vector3>(Vector3.zero,NetworkVariableReadPermission.Everyone,NetworkVariableWritePermission.Owner);
    //Vector3 objRot;
    public static GameManager manager;
    
    public void Update()
    {
        if (!IsOwner)
            return;
            
        //if (Input.GetKey(KeyCode.A)) objRot.Value += Vector3.up * Time.deltaTime * rotSpeed;//transform.Rotate(Vector3.up * Time.deltaTime * rotSpeed, Space.World);
        //if (Input.GetKey(KeyCode.D)) objRot.Value += -Vector3.up * Time.deltaTime * rotSpeed;//transform.Rotate(-Vector3.up * Time.deltaTime * rotSpeed, Space.World);
        //if (Input.GetKey(KeyCode.W)) objRot.Value += Vector3.right * Time.deltaTime * rotSpeed;//transform.Rotate(Vector3.right * Time.deltaTime * rotSpeed, Space.World);
        //if (Input.GetKey(KeyCode.S)) objRot.Value += -Vector3.right * Time.deltaTime * rotSpeed;//transform.Rotate(-Vector3.right * Time.deltaTime * rotSpeed, Space.World);
        
        UpdateObjTransServerRpc();
    }
    
    public void UpdateValues()
    {
        objRot.Value = intObj.transform.rotation.eulerAngles;
    }
    
    [ServerRpc(RequireOwnership = false)]
    void UpdateObjTransServerRpc()
    {
        //Debug.Log(OwnerClientId + " is sending rotation" + objRot);
        //objRot.Value = intObj.transform.rotation.eulerAngles;
        if (Input.GetKey(KeyCode.A)) objRot.Value += Vector3.up * Time.deltaTime * rotSpeed;
        if (Input.GetKey(KeyCode.D)) objRot.Value += -Vector3.up * Time.deltaTime * rotSpeed;
        if (Input.GetKey(KeyCode.W)) objRot.Value += Vector3.right * Time.deltaTime * rotSpeed;
        if (Input.GetKey(KeyCode.S)) objRot.Value += -Vector3.right * Time.deltaTime * rotSpeed;
        
        manager.UpdateInteractableTrans(objRot.Value);
    }
}

Same problem, did you find the solution?

My experience is with the newer way of doing netcode rpc, so the tags are different. For example, I don’t use [ServerRpc], but I assume that’s an rpc call from client to server, yes? In that case, your UpdateObjTransServerRpc() would be happening on the server, meaning that the server’s keyboard input would be checked for all those Input.GetKey calls you have in it, not the client’s key input.

What you would want to do instead is to do the Input.GetKey checks in the client’s Update, as it looks like you originally did in the commented out code. Then send the client’s input data to the server.

The way I would do that would be to collect the x/y axis data from the client and send that as rpc parameters, changing your UpdateObjTransServerRpc() to UpdateObjTransServerRpc(float xAxis, float yAxis).

void Update()
{
    if (!IsOwner)
        return;
    float xAxis = 0;
    float yAxis = 0;
    if (Input.GetKey(KeyCode.A)) xAxis -= 1;
    if (Input.GetKey(KeyCode.D)) xAxis += 1;
    if (Input.GetKey(KeyCode.W)) yAxis += 1;
    if (Input.GetKey(KeyCode.S)) yAxis -= 1;
    UpdateObjTransServerRpc(xAxis, yAxis);
}

[ServerRpc]
void UpdateObjTransServerRpc(float xAxis, float yAxis)
{
    objRot.Value += Vector3.up * -xAxis * Time.deltaTime * rotSpeed;
    objRot.Value += Vector3.right * yAxis * Time.deltaTime * rotSpeed;
}

Hopefully this allows you and the other person who responded to you to revive your projects.

Yes. :slight_smile:

Let’s use an analogy first. I have a sword. I hold it appropriately in my hand. Now you want to use (interact with) that sword at the same time as I do.

What do you expect the outcome will be?

a) neither of us really being in control over the sword
b) your hand suddenly hurts really badly and it’s covered in a dark red fluid
c) we’ll have the best sword fight ever with just one sword

I can tell you it’s not c) and this is to be expected, both in the real-world and virtual worlds.

Other question: have you ever played a game where two (or more) players interact with the same object simultaneously, and it wasn’t a goofy party game? I for sure haven’t. :wink:

In other words: there can only ever be one authoritative user OR you need custom logic.
You need to clearly define what “interact with” means and what kind of interactions each player will be able to perform at any given time. The interaction is something you need to design and program yourself.


Examples:
If you have three players and each player is able to slightly rotate the cube by clicking on it BUT each player can only rotate the cube around their designated axis. Thus player 1 rotates the cube around X, player 2 around Y and player 3 around Z. In this way, all three can simulatenously interact with the same object but they all interact with it differently.

A more reasonable scenario would be a battle ship, where players can take control of different stations. One player may be steering, another player operates the engine (speed), and the remaining two players each mount one of the available turrets. Perhaps two players can even operate the SAME turret but then you’d have to define something that makes sense, like one player is responsible for aiming and the other for firing and perhaps a third handles the reloading of shells.

Here’s an example game where multiple players take roles to control the same “spaceship” but here also each station can only be manned by one player at a time.

There is only a very limited use of the same object and same interaction for multiple players at the same time. Say every player clicking the cube makes it spin (your example), then more players clicking the cube could make it spin faster more quickly if you allow for that.

But when they provide conflicting instructions like spin right / spin left these will cancel each other out, much like a local multiplayer on the same keyboard where WASD and Arrows control the same character. It’s rare and rarely fun to design a game around such conflicting situations, although I wouldn’t rule it out. And something like this needs really good feedback too to make other players understand that player X is actively negating their combined effort.