Animation cache/management? Am I overthinking?

What’s the best way to handle animations?

I’ve been working on multiple fighting-game projects, and so far I’ve been able to get away with just loading animations per LocalScript that needs them— for example, having a “punch” script cache different punching animations locally and play them whenever needed. I’ve been handling it like so:

local characterAnimations = { -- references to Animation objects in ReplicatedStorage
	storage.Animations.PlayerAnimations.Attacks.LightPunch1,
	storage.Animations.PlayerAnimations.Attacks.LightPunch2,
	storage.Animations.PlayerAnimations.Attacks.LightPunch3
}

local function loadCharacterAnimations()
	loadedAnimations = {}
	localCharacter = Players.LocalPlayer.Character
	for i, anim in ipairs(characterAnimations) do
		loadedAnimations[i] = localCharacter.Humanoid:LoadAnimation(anim)
	end
end

loadCharacterAnimations()

-- later on, animations are called by "loadedAnimations[#]:Play()"

However, I’ve read in multiple places that having some kind of “AnimationHandler” module makes stuff like this more streamlined or something, but I’m having serious trouble wrapping my head around it and I’ve spent several hours trying to make one of my own to no avail.

Now that I think about it, I’m not really seeing the point of something like this, and I’m starting to wonder if I’m just running circles in my own head at this point trying to code something redundant. I need a reality check— is there a point to having an animation module? Is there some better way to handle animations than what I’ve been doing?

As you said you have to declare each animation for every script that needs it, plus your approach requires you to manually create an animation instance for every animation that you add.

This is a simplified example of how I do it:

local Animator = {}

local Players = game:GetService("Players")

-- Only need the animation IDs
local anims = {
	ballHold = 5431856621,
	dropKick = 1701276386,
	celebration = 592250299,
	tackle = 639693288,
	pickUpBall = 15418653548,
}

-- Takes an animation ID with an optional character argument
-- and creates a new Animation instance and loads it for
-- the given character. If char is nil, then the local character is used.
local function setAnim(id: number, char: Model?) : Animation
	char = char or Players.LocalPlayer.Character
	local animator = char.Humanoid.Animator
	local anim = Instance.new("Animation")
	anim.AnimationId = 'rbxassetid://' .. id
	anim.Parent = animator.Parent
	return animator:LoadAnimation(anim)
end

function Animator.loadAnims() -- called every time the player spawns
	for k, v in pairs(anims) do
		Animator[k] = setAnim(v)
	end
end

function Animator.play(animName: string) : AnimationTrack
	local anim = Animator[animName]
	anim:Play()
	Animator.lastPlayed = anim
	return anim
end

-- Plays animation and yields the current thread until the animation stops.
function Animator.yield(animName: string): AnimationTrack
	local anim = Animator.play(animName)
	anim.Stopped:Wait()
	return anim
end

 -- Intended for use on client-side NPCs by other modules.
function Animator.loadAnim(id: number, character: Model?) : AnimationTrack
	return setAnim(id, character)
end

return Animator

With this approach, instead of having to create a new Animation instance then updating its properties then typing a reference to it in every script that needs it, you just copy and paste the animation ID in the Animator module and you’re ready. All you have to do to in another script is to require the Animator module then use either Animator.play(animationName) or Animator.animationName:Play().

I use this in a relatively large game and it works fine for my purposes. There is certainly other functionality that could be added to my example,. but hopefully this gives you enough of an idea to make your own version to your liking.

Other

This approach also lets your game load ever-so-slightly faster when the player first joins, because otherwise you’re making the client spend extra time to load Animation instances from ReplicatedStorage. Don’t worry about this though unless you have dozens of animations. There are a million other things the client is doing when it boots up.

I’m assuming that this module would have to go into StarterPlayerScripts and then be hooked up to another LocalScript that calls Animator.loadAnims() on LocalPlayer.CharacterAdded?

I’ll give this a shot tomorrow and see if I can get it to work then share the results here, thanks for helping!

I forgot to reply, but uhh— yeah it worked for me! I also added in a .stop() function just to have it in case. Thanks for sharing this, it’s way less complicated than I was making it out to be

I personally keep all my code in modules in ReplicatedStorage and use a local script in StarterGui to initialize everything, but this kind of module can be stored and used anywhere you want where it can run.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.