2
\$\begingroup\$

I am new to Unity networking. I've set up my scene with NetworkManager and the Player prefab having NetworkIdentity. I originally had my players set up to sync with a NetworkTransform. This worked, but the objects were very choppy whenever they were children of a moving object.

In order to fix this issue, I've replaced NetworkTransform with a class extending NetworkBehavior, using UnityEngine.Networking, and containing the following code to sync localPosition:

void Update () {
    CmdSyncPos (transform.localPosition, transform.localRotation);
}

[Command]
protected void CmdSyncPos (Vector3 localPos, Quaternion localRotation) {
    transform.localPosition = localPos;
    transform.localRotation = localRotation;
}

This works really well! The only problem is that this is one-way, the server now has the updated position of all clients but the clients only have the updated versions of themselves.

How can I do this in reverse: Manually sync the location of non-local players from server to client?

\$\endgroup\$
1
\$\begingroup\$

I ended up designing my code with help from /u/Cell-i-Zenit on Reddit. Final code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine.Networking;
using UnityEngine;

public class SyncPosition : NetworkBehaviour {

    private GameObject playerBody;
    private Rigidbody physicsRoot;

    void Start () {
        playerBody = transform.GetChild (0).gameObject;
        physicsRoot = GetComponent<Rigidbody> ();
    }

    void Update () {
        if (isLocalPlayer) {
            CmdSyncPos (transform.localPosition, transform.localRotation, playerBody.transform.localRotation, physicsRoot.velocity, transform.parent.name); 
        }
    }

    // Send position to the server and run the RPC for everyone, including the server. 
    [Command]
    protected void CmdSyncPos (Vector3 localPosition, Quaternion localRotation, Quaternion bodyRotation, Vector3 velocity, string parentName) {
        RpcSyncPos (localPosition, localRotation, bodyRotation, velocity, parentName);
    }

    // For each player, transfer the position from the server to the client, and set it as long as it's not the local player. 
    [ClientRpc]
    void RpcSyncPos (Vector3 localPosition, Quaternion localRotation, Quaternion bodyRotation, Vector3 velocity, string parentName) {
        if (playerBody == null) {
            return;
        }
        if (!isLocalPlayer) {
            transform.localPosition = localPosition;
            transform.localRotation = localRotation;
            playerBody.transform.localRotation = bodyRotation;
            physicsRoot.velocity = velocity;

            if (!transform.parent.name.Equals(parentName)) {
                transform.parent = GameObject.Find (parentName).transform;
            }
        }
    }
}
\$\endgroup\$
0
\$\begingroup\$

Edit:

You could use ClientRPCs:

[ClientRpc]
public void RpcSyncedPos(Vector3 syncedPos, Quaternion syncedRotation)
{
    transform.position = syncedPos;
    transform.rotation = syncedRotation;
}

[Server]
private void UpdateClientsPos()
{
  RpcSyncedPos(transform.position, transform.rotation);
}

void Update()
{
   UpdateClientsPos();
}

The method with the [ClientRpc] attribute is only invoking on client side, but it's important to write it correctly, because it's invoking by reflection: Rpc_____

The [Server] attribute ensure that the method below is only called/invoked by the server. You could also do that in the Update loop by simply write a if statement to check whether this is a server: if(server) { UpdateClientsPos(); }

The workflow is the following:

The Client Alise calls your command to send his pos and rot to the server. The server calls the UpdateClientsPos()-method, which is a ClientRpc, to invoke the method on all other clients to send the synced pos and rot from the server to the other clients, f.e. client Bob.

This attributes and methods only work if you are using the unet high level api.

Ref: https://docs.unity3d.com/ScriptReference/Networking.ClientRpcAttribute.html

You can also use SyncVars, but then there is no need for Commands and ClientRPCs.

Take a look: https://docs.unity3d.com/Manual/UNetUsingHLAPI.html and it's subtitles.

\$\endgroup\$
  • \$\begingroup\$ really? dont you have a little self-initiative? Why should I simply copy and paste unity's doc code snippets. cmon.. \$\endgroup\$ – Ismoh Jul 20 '17 at 11:23
  • 3
    \$\begingroup\$ If our site just links to the docs with no additional insight or expertise offered, then we're not providing a service above and beyond a search engine. Users come here to benefit from your experience, so even a few words about what you suggest they take from the docs and why can be a significant help to them. \$\endgroup\$ – DMGregory Jul 20 '17 at 11:44
  • 1
    \$\begingroup\$ If those docs move or the docs site dies, this answer provides no value at all. \$\endgroup\$ – Tyyppi_77 Jul 20 '17 at 12:41
  • \$\begingroup\$ well, that's a good argument. ill fix that. \$\endgroup\$ – Ismoh Jul 20 '17 at 13:03
  • \$\begingroup\$ [Server] function 'System.Void SyncPosition::UpdateClientsPos()' called on client Trying to send command for object without authority. UnityEngine.Networking.NetworkBehaviour:SendCommandInternal(NetworkWriter, Int32, String) [Server] function 'System.Void SyncPosition::UpdateClientsPos()' called on client \$\endgroup\$ – Aaron Franke Jul 24 '17 at 0:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.