unity-game-enginesynchronizationunity3d-unet

Unet Syncing animator layer weight Unity 3D


I'm currently working on a multiplayer fps to learn UNET and Unity. in this project I'm using Network Animator Component to sync my animations.
I have one layer that runs the normal states like walking and jumping and another that just controls the upper body for scoping. This layer gets activated when scoping.

The parameters a syncing fine but not the layer weight. The script that controls the layer weight gets disabled on the remote player as it is part of the same script that takes care of shooting and we don't want to shoot from the remote player. So I placed that code on a different script and made Layerweight a Syncvar, still no results.

this is the snippet that sets the weight and fades it back out.

    if (isScoped)
    {
        animator.SetLayerWeight(1, 1);
        layerWeight = 1;
    }
    else
    {
        layerWeight = Mathf.Lerp(layerWeight, 0, 0.1f);
        animator.SetLayerWeight(1, layerWeight);
    }

    animator.SetBool("Scoped", isScoped);

how can I sync a variable that is updated on a disabled script? And what are the different custom attributes like [Command] and [ClientRpc] I should use to get this working?
I have read the documentation and manuals of UNET, but struggle at this relatively easy task as I'm having a hard time understanding the difference of these calls.


Solution

  • In general the main problem with [SyncVar] is that they are

    synchronized from the server to clients

    so not the other way round!


    Then

    [Command] is a method that is called by any client but only executed on the server

    [ClientRpc] is kind of the opposite, it is called by the server and then executed on all clients

    so what I would do is have a setter method for the layerweight like

    private void SetLayerWeight(float value)
    {
        // only player with authority may do so
        if(!isLocalPlayer) return;
    
        // Tell the server to update the value
        CmdSetLayerWeight(value);
    }
    
    [Command]
    private void CmdSetLayerWeight(float value)
    {
        // Server may now tell it to all clients
        RpcSetLayerWeight(value);
    }
    
    [ClientRpc]
    private void RpcSetLayerWeight(float value)
    {
        // called on all clients including server
        layerweight = value;
        animator.SetLayerWeight(1, layerweight);
    }
    

    and call it like

    if (isScoped)
    {
        SetLayerWeight(1);
    }
    else
    {
        SetLayerWeight(Mathf.Lerp(layerWeight, 0, 0.1f));
    }
    

    Note though that passing this forth and back between server and client will cause some uncanny lag!

    You would probably be better syncing the isScoped bool instead and assign the value directly on the calling client:

    private void SetScoped(bool value)
    {
        // only player with authority may do so
        if(!isLocalPlayer) return;
    
        // for yourself already apply the value directly so you don't have
        // to wait until it comes back from the server
        scoped = value;
    
        // Tell the server to update the value and who you are
        CmdSetLayerWeight(value);
    }
    
    // Executed only on the server
    [Command]
    private void CmdSetLayerWeight(bool value)
    {
        // Server may now tell it to all clients
        RpcSetLayerWeight(value);
    }
    
    // Executed on all clients
    [ClientRpc]
    private void RpcSetLayerWeight(bool value)
    {
        // called on all clients including server
        // since setting a bool is not expensive it doesn't matter
        // if this is done again also on the client who originally initialized this
        scoped = value;
    }