Unet good way to do animation syncing?

Hey guys,

I’m trying to get animations to sync properly in my Unet multiplayer game. Right now, they work in most cases, but not in all cases. And I’m trying to get them to where they are reliable, and I don’t see failures in playing certain animations at certain times – which I currently do.

First attempt:

I have tried using the “Network Animator” component that comes by default. What I noticed was that my mecanim variables sync properly IF my character/agent does NOT have “LocalPlayerAuthority” checked on their Network Identity. If it is checked, then the Network Animator variables do nothing at all, and don’t seem to be sent properly.

This is problematic because I’m mainly using this function to spawn objects:

NetworkServer.SpawnWithClientAuthority(go, connectionToClient);

The only way to get animations to sync automatically is to NOT have “LocalPlayerAuthority” checked. As per the documentation though: “Non-player objects that are to have client authority must have LocalPlayerAuthority checked in their NetworkIdentity.” But that is also what causes the Network Animator to fail – so that is a dilemma, and seems to be at odds with each other.

Second attempt:

Since the Network Animator doesn’t seem to work in my case, I have tried doing it manually. And this is what kind of (sort of), in some cases, seems to work – a little bit lol. But I think I need help with it. I’m currently using this code (this is one example but I’m using it for all of my animation states currently):

//If the enemy target dies, do the following
            if (enemyTarget.GetComponent<HealthAndDeath>().currentHealth <= 0)
            {
                isInAttackState = false;

                //Stop the unit from continuing to the enemy target's location after the enemy target dies.
                //I'm using "currentWaypoint" instead of "path = null", because the code to stop the unit's
                //run animation is only in the currentWaypoint condition check in the Move script.
                moveScript.currentWaypoint = 15000;

                //Stop playing the attack animation on this unit (and command the server and other
                //clients to do so as well in multiplayer)
                if (thisAnimator.GetBool("playAttackAnimation1") == true)
                {
                    thisAnimator.SetBool("playAttackAnimation1", false);

                    //Tell the server instance of this unit to stop the attack animation
                    CmdStopAttackAnimation1();

                    //Debug.Log("I'm sending a command to the server");

                    if (isServer)
                    {
                        //Tell other clients to stop the attack animation on this unit
                        RpcStopAttackAnimation1();
                    }

                    //Debug.Log("I'm sending commands to server to stop the attack animation");
                }


                enemyTarget = null;
            }

[Command]
    public void CmdStopAttackAnimation1()
    {
        NetworkServer.FindLocalObject(thisNetworkIdentity.netId).GetComponent<Animator>().SetBool("playAttackAnimation1", false);
    }


[ClientRpc]
    public void RpcStopAttackAnimation1()
    {
        thisAnimator.SetBool("playAttackAnimation1", false);
    }

I’d like to know if I might be doing something majorly wrong here or not? Because if my code actually looks fairly good, then perhaps it’s some of my other behavioral states or conditions that I am failing to sync properly.

Thanks for any help or ideas.

You’re missing the “isLocalPlayer” concept. Yes, Local Player Authority has to be checked in order for the Network Animator to synchronize the Animator’s state too all connected clients. However, if Local Player Authority IS checked, then the person controlling said GameObject must also be it’s “isLocalPlayer”. NetworkServer.SpawnWithClientAuthority() doesn’t automatically set the player to be the “isLocalPlayer” of that GameObject, if they already have one in scene. You’ll have to also use the NetworkServer.ReplacePlayerForConnection() in order to change a player to represent a different GameObject’s “isLocalPlayer”.

This caused a bit of confusion for me myself. I couldn’t wrap my brain around it, because each player (host and clients) can only be the “isLocalPlayer” of ONE GameObject at a time. So I was baffled at how to accomplish NPCs, if the server couldn’t be the “isLocalPlayer” for all of them simultaneously. The solution is below…

If you’re trying to synchronize animations for NPCs, then don’t check Local Player Authority. If you do this, only the server will be able to dictate which state the Animator should be in at all times.

Hey Rosen, thanks. So I guess my follow-up question from your reply would be, then how do you assign client authority on an object that doesn’t have “Local Player Authority” checked on their Identity? Perhaps that’s what you were explaining with the “NetworkServer.ReplacePlayerForConnection()” command? I tried using it and all it did was give that object the “isLocalPlayer” setting, but clients still did not have proper authority on those objects – and thus, can’t control them.

I should mention that my game is RTS in nature. So, I am giving certain players authority over a whole bunch of units. I guess you could say RTS units are NPC’s to some extent, but they need to be controlled by my players – and can’t really be neutral objects.

I guess if you could explain further what you mean by having to use the “NetworkServer.ReplacePlayerForConnection()” that would be cool. Admittedly, I don’t understand what you mean by that yet.

Thanks for your help on this.

Well I’ll explain it based on my own implementation…

I have a prefab GameObject that is used for both enemy NPCs and players. Because the enemy NPCs are spawned by the server, by default I leave “Local Player Authority” unchecked on the prefabs Network Identity. When a player summons one of their soldiers to the battlefield I use NetworkServer.SpawnWithClientAuthority(). However, until the player actually takes control of that soldier, I use A.I. to have that soldier wander about. Because of this “Local Player Authority” has be to unchecked. When it’s time for the player to take control of the soldier, I use NetworkServer.ReplacePlayerForConnection(). This changes the soldier’s “isLocalPlayer” to true. If it’s not true then “Local Player Authority” doesn’t matter. However, at the same time, if “isLocalPlayer” IS true, but “Local Player Authority” isn’t checked, then the animations (Network Animator) and transform (Network Transform) won’t be synchronized to other clients. So as soon as I call NetworkServer.ReplacePlayerForConnection(), I also set that GameObjects “Local Player Authority” to be true. I do this in a RPC.

Ah ok. I think I was just forgetting to reset the GameObject Identity scripts’ local player authority manually then, after setting the “isLocalPlayer” variable. I’m not sure why I didn’t think of that when I was testing it, but it was also very late, and I probably wasn’t thinking the best. When I have time I’ll test that out and I’ll let you know if I have any problems with it – it sounds like it should work from what you’re saying :slight_smile:

So here is what I’m going to try from everything that you’ve said:

I spawn an object prefab without the Identity script’s “Local Player Authority” checked, via the basic NetworkServer.Spawn() function (not the SpawnWithClientAuthority() function since you cannot do that as far as I know). Then (once the object is spawned) call “NetworkServer.ReplacePlayerForConnection()” on the object and then the function that manually assigns it local player authority – and then the Network Animator can properly sync again? I’m just throwing this out there, because I’m going to be working on this more in depth on the weekend most likely, and havn’t had time to test or contemplate what you said in-depth yet.

Ok, I said screw it, and just tested it out anyways. What I found was, I spawned my first unit – set it to be the local player and with local player authority. Yes, the Network Animator now works for that gameobject. However, setting that gameobject to be the local player caused my default auto-spawned “Player” gameobject to NOT be the local player object anymore and it lost authority as well. This object was basically my game manager object for the player. So my spawning script stopped working, and I am unable to “build” more units from it because it loses authority when you switch local player objects. Maybe I can quickly switch authority back to my default spawned Player object once a unit has been spawned and its authority has been set?

Perhaps I need to completely restructure my networking system somehow?

I’m not sure how to implement it for you, but for me I swap back and forth between soldiers and commander using NetworkServer.ReplacePlayerForConnection().

I have had the same problems, I do not believe UNet allows for split authority (where the client say has control over player motion using NetworkTransform, and the server has control over actions using NetworkAnimator).

My understanding is you basically have 2 options.
1: Have the player actually be 2 different objects. One with local player authority, and one without. (Object1 for Client → Server control, and object2 for Server → Client control).
2: Code part if it yourself. Either code the Client → Server NetworkTransform yourself or the Server → Client NetworkAnimator yourself. In my case what I did is set the player to not have local player authority. This allows Server → Client control over NetworkAnimator since all actions are performed on authoritative server. Player motion is handled from Client → Server using Commands instead of NetworkTransform (since I can’t use NetworkTransform because I don’t have localPlayerAuthority).

There’s some other suggestions people had in this thread also.

It would really be nice if UNet allowed for split authority, but it doesn’t yet.

Ok thanks DRRosen and Zullar. I almost have it working the way DRRosen is suggesting, but running into problems on the remote client with it right now. I’ll keep working on it, and hopefully be able to figure something out. I still have a lot of trial and error I can do.

You know @Velo222 I might have to take back my understanding of this whole subject.

The reason I was thinking NetworkServer.ReplacePlayerForConnection() was necessary was because I used to do it this way:

  1. Player prefab was my commander. Local Player Authority checked. This had everything working fine. The commander’s rigidbody properties were sync’ing just fine via the Network Transform and the animations sync just fine via the Network Animator.
  2. In a Command on the commander I would have a function that would NetworkServer.SpawnWithClientAuthority() a soldier. Local Player Authority checked.
  3. I would then use a key press to switch my Input on my commander to “OFF” and my Input on my soldier to “ON”. (The camera switches to view the solider in via the OnEnable method.)
  4. This is where the trouble starts. The soldier’s rigidbody properties sync just fine via the Network Transform…HOWEVER, the animations DO NOT sync via the Network Animator.

I simplified my workload and tried this very same concept with primitives and a simple Input script. SPACE BAR spawned a duplicate primitive, and using TAB swapped to the “clone”. Pressing LeftShift or RightShift adjusted a Float value in the primitives Animator that switched it from one state to another. Each state was a simple looping animation that rotated the primitive on a different access. This worked just fine. Everything (including transform) was synchronized.

So I really don’t understand where we’re going wrong.

Hey Rosen,

Just a quick update. So, I got the way you suggested initially working now (which is great and I’m pretty happy with :slight_smile: ). I was over-complicating it a bit yesterday. Basically, the “NetworkServer.ReplacePlayerForConnection()” is like a hammer that I have to hit each object with in order to get their Network Animator scripts to start up. Then immediately switch it back to the “real default Player object” immediately after I spawn the unit. I have a feeling this is not how Unity intended it to be used at all, since that function sounds like it’s simply supposed to swap a single player in the game, but who knows?

Since it works (at least for now), I’m now wondering about performance. Since Unet seems to be geared for games where the player only operates on a single unit, I wonder how often the Network Animator sends state updates. Is it every Update(), or only when it sees that a value has changed perhaps? Obviously, not sending information if nothing is happening would be the best. But I’m off on a tangent now.

Are you saying that your system as you described it to me in earlier posts works? Or is this a slightly different method that you’re using?

The system I was using, as I described it to you in earlier posts, isn’t working anymore.

Ah ok. I guess if it’s a bug, we’ll just have to wait for them to fix it.

My initial testing today though, I tested over a local area network, and the animations seemed to be synced really well (on two separate computers). Although, for transform movement, I’m not using the Network Transform script that came by default, I’m using a custom one I learned in a tutorial. But I am using the Network Animator script.

Also one slight difference between your project and mine (it sounds like), is that I’m not transitioning input from one object to another. I’m leaving the input on the default spawned Player object – so perhaps I simply am not running into the problem you are having.

From your linked post though, it looks like a bug that simply needs to be fixed. I hope that’s the case, and you get yours working. I’m sure I’ll probably run into something similar along the way.

Curious. Which tutorial did you watch to build your custom Network Transform? As far as my post, it’s most definitely a bug. After seeing how @seanr posted it in the Issue Tracker, I can see how it’s a bug. Like I said before, there is only ever one Player Object for each player at a time. No matter how many SpawnWithClientAuthority() you call…those are all non-player objects, just with player authority. So the issue is that non-player objects aren’t synchronizing their Animator states.

While I wait for this bug to resolved I’m just going to work on the best way to implement NPC enemies. I’ve been re-re-re-reading the documentation, and I can’t decipher if it’s best to have them be scene objects, or spawn them after the server is up. Obviously the latter method won’t work until the bug is sorted out. lol

Here is a link to the YouTube tutorial video I used:

.

This guy also has other ones as well. I was kind of forced to use a custom one, because for some reason the default Network Transform script was not working that great (i.e. bad or no interpolation I think).

I’m curious though, if non-player objects are not synchronizing their Animator states, how was my testing today actually working? As I said previously, you’re right that the Network Animator doesn’t work on non-player objects spawned with SpawnWithClientAuthority() right out of the box. However, when I used your suggested method of making them the Local Player for a second, it does seem to kick-start the Network Animator into syncing things correctly.

Is the bug what we were experiencing in the first place, that the Network Animator simply doesn’t auto-sync on non-player objects even if they have authority? If that isn’t the bug, then I need to study my own game to see how it’s even working right now lol.

Note that some of these videos are made during UNET’s infancy. There was not a lot of information available at the time, so the videos may not be accurate. All I know is that Network Transform is never going to be used for animation, and is only used for getting the latest Transforms that the server knows about to the clients.

That’s correct @asperatology , however, out issue/bug stems from the Network Animator…not the Network Transform.