Issues with animations when streaming in players

I am trying to let players stream out when out of streaming distance.
However, from time to time, players stream in correctly, but animations that were playing while the player was loading in don’t get “replicated” correctly.
Checking the animator instance inside their humanoid and looking into GetPlayingAnimationTracks, it seems that the animations seem to be “suspended”, where the AnimationTracks are playing (IsPlaying is set to true), but the TimeLength is 0 (even if their TimePosition can be above 0), indicating it hasn’t loaded for some reason, even though it should be playing. The client is aware of these animations but hasn’t “applied” them to the humanoid as if they haven’t loaded yet.

Yes, the game has access to the animations, as it plays them just fine when the player has not streamed out yet.
I tried to make the game just reload the animation, but that means the other player is no longer in “control” of that AnimationTrack and it just plays independently, and doesn’t start/stop as it should.

Any ideas?

edit: clarification about the state of the animationtracks

1 Like

Messing around I kind of found a “hack” for that. Can’t say it’s full proof however only used it once.
You can try setting the Parent of the Animator to nil and back to the Humanoid to force a refresh of the animator subsystem.

Unfortunately doesn’t seem to fix the problem… stays broken as it was. Maybe I’m just setting the parent wrong though somehow.

1 Like

Well, the animation system doesn’t automatically re-sync or re-apply the animation timeline state.
A reload can correct that, but the sync is pretty touchy. I am a noob when it comes to this, even though I understand the problem with what’s happening. I can write you a minimal sample code for such a server/client sync system for animations, but I’m sure someone else can do it better.

I needed to strip out a few things here, for an example.
This is one I was working on but gave up and went to no streaming. :joy:

Server

in ServerScriptService

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local AnimEvent = Instance.new("RemoteEvent", ReplicatedStorage)
AnimEvent.Name = "AnimEvent"

local animations = {
    Wave = Instance.new("Animation"),
    Dance = Instance.new("Animation"),
}

animations.Wave.AnimationId = "rbxassetid://WAVE_ANIMATION_ID"
animations.Dance.AnimationId = "rbxassetid://DANCE_ANIMATION_ID"

local players = game:GetService("Players")

local playerTracks = {}

players.PlayerAdded:Connect(function(player)
    player.CharacterAdded:Connect(function(char)
        playerTracks[player] = {}
        AnimEvent:FireClient(player, "StopAll")
    end)
end)

function playAnim(player, animName)
    if not playerTracks[player] then playerTracks[player] = {} end
    if playerTracks[player][animName] then return end
    playerTracks[player][animName] = true
    AnimEvent:FireClient(player, "Play", animName, animations[animName].AnimationId)
end

function stopAnim(player, animName)
    if playerTracks[player] then
        playerTracks[player][animName] = nil
        AnimEvent:FireClient(player, "Stop", animName)
    end
end

-- Example:
players.PlayerAdded:Connect(function(player)
    wait(5)  playAnim(player, "Wave")
    wait(10)  stopAnim(player, "Wave")
end)
Client

in StarterPlayerScripts

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local AnimEvent = ReplicatedStorage:WaitForChild("AnimEvent")
local players = game:GetService("Players")
local player = players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local humanoid = char:WaitForChild("Humanoid")
local animator = humanoid:WaitForChild("Animator")

local tracks = {}

AnimEvent.OnClientEvent:Connect(function(action, animName, animId)
    if action == "Play" then
        if tracks[animName] then return end
        local anim = Instance.new("Animation")
        anim.AnimationId = animId
        local track = animator:LoadAnimation(anim)
        tracks[animName] = track
        track:Play()
    elseif action == "Stop" then
        if tracks[animName] then
            tracks[animName]:Stop()
            tracks[animName]:Destroy()
            tracks[animName] = nil
        end
    elseif action == "StopAll" then
        for _, track in pairs(tracks) do
            track:Stop()
            track:Destroy()
        end
        tracks = {}
    end
end)

player.CharacterAdded:Connect(function(c)
    char = c
    humanoid = char:WaitForChild("Humanoid")
    animator = humanoid:WaitForChild("Animator")
    for _, track in pairs(tracks) do
        track:Stop()
        track:Destroy()
    end
    tracks = {}
end)
1 Like