Client Animations not displaying on the host

Hey, so currently I’ve been trying to learn about Unity Multiplayer Services like Relay, Lobby, and Netcode for GameObjects. Everything has been going fine so far until recently I ran into a issue with the Network Animator and getting client animations to work on host.

Here is a video showing what I mean

So basically the system behind it is very simple, There are several keys a player can press. Depending on the key they press, an emote animation will play. This is done by

 public void PerformEmote(Emotes emoteUsed, Animator myAnimator)
    {
        if(Input.GetKeyDown(emoteUsed.inputRequired))
        {
            myAnimator.Play(emoteUsed.animationClip.name, -1, 0);
        }
    }

The stickman object has a Network Object, the player script on the object inherits from network behavior, has a client network transform and Network Animator on it.
The movement system works fine and is reflected on both the host an client but its just animations currently.

I having trouble understanding why the client’s animation is not running on the host. I’m still thinking of possible solutions

Is it because of the way I’m playing the animation? Ex.) myAnimator.Play()
Is it because of I need a ServerRPC call to perform it on host version of the client’s model?

I’m still spit balling here. Has anyone ran into this problem before and how should I go about fixing this issue.
If find the answer, I’ll add it here for anyone in future who has this problem

The NetworkAnimator is server authoritative only. So unlike the ClientNetworkTransform you need to make changes on the server. As you mentioned you can use a ServerRpc call to perform the animation on the host and that will correctly replicate it to all the clients.

1 Like

Thanks for the help. After trying new approach, I tried to perform a ServerRPC from client but I get no animation on the client and when I try it on the host, both the client and client host wave.

Here is the code setup for performing action

void Update()
    {
        if(canMove)
        {
            if (IsOwner)
            {
                Move();
                if (!IsHost)
                {
                    CheckForEmoteServerRpc();
                    
                }
                else if (IsServer)
                {
                    stickmanAnimations.CheckForEmote(myAnimator);
                }
            }
          
        }
    }

    [ServerRpc(RequireOwnership = false)]
    void CheckForEmoteServerRpc()
    {
        stickmanAnimations.CheckForEmote(myAnimator);
        //CheckForEmoteClientRpc();
        Debug.Log("Client Wave");
    }
    [ClientRpc]
    void CheckForEmoteClientRpc()
    {
        stickmanAnimations.CheckForEmote(myAnimator);
        Debug.Log("Client Wave");
    }

Is this the right way to approach?

The prevoius verison from video was like this

 void Update()
    {
        if(canMove)
        {
            if (IsOwner)
            {
                Move();
                stickmanAnimations.CheckForEmote(myAnimator);
            }
          
        }
    }

Checking for a key press should still happen on the client. Calling an animation if the right key is pressed should happen on the server.

It might however be better to remove the NetworkAnimator if the player is client-authorative and just replicate all animations of the local player on the other clients via ServerRpc+ClientRpc. That way, the player never experiences a delay with their own animations. The flow would then be (assuming PlayEmote() is the function name):

Update: check for key press → if pressed call PlayEmote(), then PlayEmoteServerRpc() → in PlayEmoteServerRpc(), call PlayEmote() if not the owner and then call PlayEmoteClientRpc() → in PlayEmoteClientRpc(), call PlayEmote() if not the owner and not the server.

1 Like

Thanks for the info, I adjust the code accordingly but it seems to make them both perform the emote when I press a key for an emote.

https://www.youtube.com/watch?v=2Mwn9deF_Sg

In the video, I tried both with and without the network animator and got the following results.

  • With Network animator host animations were playing on client but client animations not playing on host
  • Without Network animator, host animations were not showing on client and client animations weren’t playing on host

I’m sure i’m doing something wrong on my part. I’m still trying stuff and reviewing the documentation on RPC.

Edit
On that ServerRPC call, I didn’t notice a difference between this
csharp** **if(!IsOwner) { stickmanAnimations.CheckForEmote(myAnimator); } CheckForEmoteClientRpc();** **
and this
csharp** **if(!IsOwner) { stickmanAnimations.CheckForEmote(myAnimator); CheckForEmoteClientRpc(); }** **

8119799--1052312--StickmanAnimations.png 8119799--1052315--PlayerController.png

You are still checking for key presses on all clients/server, in your CheckForEmote function. Only call this function locally, in the Update loop, for IsOwner (or IsLocalPlayer). Then if an animation should be played, use a ServerRpc to play it if there is a NetworkAnimator, or a ServerRpc+ClientRpcs if there is not as I suggested above.

As to your edit: you don’t notice a difference because ClientRpcs can only be called by the server.

1 Like

Thanks for letting me know

// Update is called once per frame
    void Update()
    {
        if(canMove)
        {
            if (IsOwner)
            {
                Move();
              
            }
            if (IsLocalPlayer)
            {
                if (Input.anyKeyDown)
                {
                    stickmanAnimations.CheckForEmote(myAnimator);
                    CheckForEmoteServerRpc();
                }
            }
          
        }
    }

    [ServerRpc(RequireOwnership = false)]
    void CheckForEmoteServerRpc(ServerRpcParams serverRpcParams = default)
    {
        if(!IsOwner)
            stickmanAnimations.CheckForEmote(myAnimator);
        CheckForEmoteClientRpc();
    }
    [ClientRpc]
    void CheckForEmoteClientRpc(ClientRpcParams clientRpcParams = default)
    {
        if (!IsOwner && !IsServer)
            stickmanAnimations.CheckForEmote(myAnimator);
        Debug.Log("Hit " + IsLocalPlayer);
    }

I’ve made the changes but now I’m back at the beginning where Host animations are playing on client but client animations are not playing on host.

https://www.youtube.com/watch?v=GOtE96OKyVA

I checked out this video on Netcode Animations and they don’t seem to run into this problem.

You’re still doing what I repeatedly said was the problem: don’t have ServerRpcs or ClientRpcs to check for emotes (i.e. key presses), only have them to play the emotes.

1 Like

So just so I’m clear on this,

You are saying that since CheckForEmote Function is in another class (outside of RPC), it won’t be excuted.

So I should just move logic from that function into the Sever and Client RPC and it should work.

CheckForEmote is actually doing the perform emote. I should just put this together and place in my RPC right?

 public void PerformEmote(Emotes emoteUsed, Animator myAnimator)
    {
        if(Input.GetKeyDown(emoteUsed.inputRequired))
        {
            myAnimator.Play(emoteUsed.animationClip.name, -1, 0);
        }
    }

    public void CheckForEmote(Animator myAnimator)
    {
        if(Input.anyKeyDown)
        {
            myEmotes.ForEach(ele =>
            {
                if(Input.GetKeyDown(ele.inputRequired))
                {
                    PerformEmote(ele, myAnimator);
                }

            });
        }
    }

That was the fix, So at the end.

The issue I was having was that in the Server and Client RPC.

You have to do your actions there and not go into any other functions outside of your Network Behaviour. I was using “stickmanAnimations.CheckForEmote(myAnimator);” when I had to break it down even further and have everything that was inside of CheckForEmote and put inside of the RPC.

Thanks for all your help getting through this problem. I’ll learned a lot and couldn’t have done it with out you.

:slight_smile:

1 Like

Hey can you please share your solution as I am having the same issue and i am unable to understand the solution.

3 Likes

My solution was:

DONE!

2 Likes

That’s veryy simple,

Just make another script named as “ClientNetworkAnimator” and paste the following code in it, and after pasting and saving the code, remove the NetworkAnimator script from your player object and put this script in it, then put your player object’s animator in the “animator” of “ClientNetworkAnimator”. Save the scene, test it out, it works fine. :raised_hands:

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

public class ClientNetworkAnimator : NetworkAnimator
{
    protected override bool OnIsServerAuthoritative()
    {
        return false;
    }
    
}