Animation not syncing of client player when your current tab is not focused

Hi there,

I was connected in a multiplayer session in a room. My player A is looking at player B who is sitting on a chair whose current animation is ‘sitDown’. When I switch to some other browser tab and player B moved away from the chair and standing idle now in space. Now when I come back to my current tab and I see that player B is at new position where he was standing idle but I see player B animation as ‘SitDown’ only.

Its updates for me only when player B move again after I came back to my current screen.

I saw the logs from ‘debuganimatorcontroller’, I found that I didn’t received the log ‘transition changed to 0(idle)’ only got the log Set Trigger to ‘idle’.

Original Post on Discord

by user 389863047159873540

Hey, are you using SyncedAnimator or something different to sync the state?
Do you see the same issue on Synchronized Animator | Needle Engine ?

I’ve used ‘connection.send’ and client gets update from ‘beginListen’ callback. Please find the code snippet for logic I’ve used for my custom SyncedAnimator. Let me know if there is any issue in it. If this didnt work, I will used the sample logic of SyncedAnimator.

onEnable(): void {
        this.animator = this.gameObject.getComponent(Animator) as Animator;
        this.context.connection.beginListen("animCall", this.onAnimationUpdated);
    }

    onDisable(): void {
        this.context.connection.stopListen("animCall", this.onAnimationUpdated);
    }

    onAnimationUpdated = (response: AnimModel) => {
        if (!this.animator) return;

        if (response.id === this.syncedTransformGuid) {
            this.ResetAllTriggers();
            this.animator.setTrigger(response.transitionTo);
        }
    }

    public SetTrigger(transitionTo: string) {
        if (!this.animator) return;

        this.ResetAllTriggers();
        this.animator.setTrigger(transitionTo);
        this.animModel = new AnimModel(this.syncedTransformGuid, transitionTo);
        this.context.connection.send("animCall", this.animModel);
    }

    private ResetAllTriggers() {
        if (!this.animator) return;

        this.animator.resetTrigger("idle");
        this.animator.resetTrigger("walking");
        this.animator.resetTrigger("running");        
        this.animator.resetTrigger("dance");
        this.animator.resetTrigger("sitDown");
        this.animator.resetTrigger("sitIdle");
        this.animator.resetTrigger("standUp");
    }

by user 389863047159873540

Does your AnimModel have a guid?

If it hasn’t it will not be persisted, which means it’s a one-off event and won’t be rolled into state updates that you get later.

@marcel :cactus: please correct me if I’m wrong on that

Can you show your AnimModel ? It needs a guid to be stored in the state, yes

So I created a class like this:

class AnimModel {
    id: string;
    transitionTo: string;

    constructor(id: string, transitionTo: string) {
        this.id = id;
        this.transitionTo = transitionTo;
    }
}

Should I add one more string field as guid: string in AnimModel and send call be like this:

this.animModel = new AnimModel(this.syncedTransformGuid, transitionTo, this.guid);
this.context.connection.send("animCall", this.animModel);

I tried this but didn’t work.

by user 389863047159873540

this.syncedTransformGuid I’m getting this from another class where I’m downloading model and assigning SyncTransform at runtime. So its like that

// my Character loading class...
:
:
@serializable(SyncedTransform)
currentSyncedTransform!: SyncedTransform;
:
:
async downloadAndApply(url: string) {
:
// downloading model from url and instantiating it as a GameObject
:
:
//
let syncAnimatorInstance = GameObject.addNewComponent(go, SyncAnimator);

if (syncAnimatorInstance) {
  syncAnimatorInstance.awake();
  syncAnimatorInstance.enabled = true;

  if (this.currentSyncedTransform) {
    syncAnimatorInstance.syncedTransformGuid = this.currentSyncedTransform.guid;
  }
}
:
:

by user 389863047159873540

do not use the same guid for your animation and position.
The guid is an ID for the storage on the server, so under a single guid a single message is stored. And the usecase here is to store the last message for whoever connects to get it.

This way you could be randomly “loosing data”, since it is sharing a single ID for 2 unique information.

While it is called guid, it is rather just a unique string. So to make it deterministic i would advice to add a prefix and set that to the sync animator.

like
 anim_${syncTransform.guid} and see if that solves the issue of loosing state on “reconnect”.

Thanks Kipash. I got your point. Will try the way you suggested. :+1: :grinning:

by user 389863047159873540

BTW, if you have any feedback in regards to the networking API and usage, feel free to share :cactus: