SimpleAnimate | An easy-to-use & flexible module replacement for the default Animate script

[ \o7 ] SimpleAnimate

AnimationController.rbxm (21.3 KB) | Marketplace | Playground.rbxl | Donate 80 Robux

Have you tried working with the default Animate script for your game with super cool animations, and got really frustrated because the code was messy and long?
seriously, what even is this??

Well, do I have the solution for you!
Presenting…

SimpleAnimate!

SimpleAnimate is a module that you can require via a LocalScript and it’ll play your animations like the default Animate script would, but it’s more flexible & interactable!

Documentation:

Types

SimpleAnimate uses a few types which are used often, so you’ll need to know them well.

Type Name Structure Description
PoseType "Run" | "Walk" | "Idle" | ... This type represents a string which can either be “Run”, “Walk”, “Idle”, “Swimming”, “Freefall”, etc. and represents all default SimpleAnimate poses which animations can be assigned to.
AnimInfo {id: string, weight: number, anim: AnimationTrack, fadeTime: number?, stopFadeTime: number?, speed: number?} Represents a single Animation inside an array of AnimInfos assigned to a PoseType. This is because one PoseType can be assigned multiple animations to randomly play based on a weight.
AnimationsList { [PoseType]: {AnimInfo} } Represents an entire list of poses, each assigned to animations. This is the type of things such as the coreAnimations and emoteAnimations dictionaries.

AnimationController

This is the main animation controller object which will be assigned to rigs.

Below are a list of members and method functions you can use directly:

Member Name Type Description
AnimationController.Core Core The object which handles core animations such as walking, idling, running, swimming, etc.
AnimationController.Action Actions The object which handles user-defined action animations, such as slashing, holding, pointing, or other such animations.
Method Name, Parameters & Return Type Description
AnimationController:Destroy() -> () Clears up all memory occupied by this AnimationController, disconnects all connections and stops playing animations.

AnimationController.Core

Core is the object which handles user-defined action animations, such as slashing, holding, pointing, or other such animations.

Below are a list of members and method functions you can use directly:

Member Name Type Description
Core.PoseController PoseController Controls the playing of animations & tracking of poses, this is the object you use to do things like stop all animations, change animations, change poses, change animation lists, and more.
Core.Connections Connections Connects to events of the state machine (humanoid by default) and tells the PoseController what pose to change to when the states change.
Method Name, Parameters & Return Type Description
Core:Destroy() -> () Clears up all memory occupied by this Core object, disconnects all connections and stops playing animations.
AnimationController.Core.PoseController

PoseController is the object which controls the playing of animations & tracking of poses, this is the object you use to do things like stop all animations, change animations, change poses, change animation lists, and more.

Below are a list of members and method functions you can use directly:

Member Name Type Description
PoseController.PoseChanged RBXScriptSignal<PoseType, PoseType, AnimationTrack> An event made with SimpleSignal which is fired everytime the pose is changed. Passes the old pose, the new pose and the current running animation track.
Method Name, Parameters & Return Type Description
PoseController:Destroy() -> () Clears up all memory occupied by this PoseController object, disconnects all connections and stops playing animations.
PoseController:SetCoreActive(active: boolean) -> () Sets wether or not the Connections object can automatically change PoseController poses or not.
PoseController:GetCoreActive() -> boolean Returns wether or not the Connections object can automatically change PoseController poses or not.
PoseController:GetCurrentTrack() -> AnimationTrack Gets the current playing core AnimationTrack.
PoseController:GetPose() -> PoseType? Gets the current pose as a string, if any.
PoseController:SetPoseEnabled(pose: PoseType, enabled: boolean) -> () Sets wether or not the PoseController’s pose can be changed into pose.
PoseController:GetCoreAnimInfos(pose: PoseType) -> {AnimInfo} Returns a list of AnimInfos which are assigned to pose.
PoseController:ChangeCoreAnim(pose: PoseType, index: number, new: Animation | string | AnimationTrack | Types.AnimInfo) -> AnimationTrack Changes a specific index of a specific pose in the PoseController’s animations list to be new, which can either be an Animation, a content id string containing an animation, an AnimationTrack or an AnimInfo.
PoseController:GetRandomCoreAnim(pose: PoseType) -> AnimationTrack Returns a random core animation from the list of AnimInfos assigned to pose in the animations list.
PoseController:PlayAnimation(pose: PoseType, speed: number?, fadeTime: number?) Plays a random core animatino from the list of AnimInfos assigned to pose in the animations list, using the specified speed and/or fadeTime if provided. Is not affected by poses.
PoseController:ChangePose(pose: PoseType, speed: number?) -> () Changes the PoseControllers pose into pose. If the current pose isn’t already pose, then all core animations will be stopped and a random one from the ones assigned to pose in the animations list will be played with speed (or 1 if not provided).
AnimationController.Core.Connections

Connections is the object which connects to events of the state machine (humanoid by default) and tells the PoseController what pose to change to when the states change.

Below are a list of members and method functions you can use directly:

Member Name Type Description
Connections.JumpDuration number Determines how long the Jumping pose will play before the Freefall pose plays.
Connections.SwimIdleAnimationSpeed number Determines the speed at which the SwimIdle pose should play at, should be a number close to 1 as this value isn’t normalized by any threshold.
Connections.IdleAnimationSpeed number Determines the speed at which the Idle pose should play at, should be a number close to 1 as this value isn’t normalized by any threshold.
Connections.RunThreshold number Determines the walking speed at which the Connections object should stop telling the PoseController to change to the Walk pose, but into the Run pose instead.
Connections.SwimIdleThreshold number Determines the swimming speed at which the Connections object should stop telling the PoseController to change to the Swimming pose, but into the SwimIdle pose instead.
Connections.MoveAnimationSpeedMultiplier number A number which is a multiplier of the walk speed to determine the animation speed of the current locomotion pose. For example, if it’s less than 1 then the animation will be slower.
Method Name, Parameters & Return Type Description
Connections:Destroy() -> () Clears up all memory occupied by this Connections object, disconnects all connections and stops playing animations.
AnimationController.Action

Action is the object which handles user-defined action animations, such as slashing, holding, pointing, or other such animations.

The Action module works similarly to an AnimationsList where you can have the keys as literally anything, not just strings, and also you are able to create, remove, get and play animations at will.

Below are a list of method functions you can use directly:

Method Name, Parameters & Return Type Description
Action:Destroy() -> () Clears up all memory occupied by this Action object, disconnects all connections and stops playing animations.
Action:Create(key: any, template: {AnimInfo}, doPreload: boolean?, priority: Enum.AnimationPriority?) -> {AnimInfo} Creates a new entry in the Action object accessible via the key, and copies template and uses it as the assigned animations for key (and also preloads it if doPreload is true).
Action:BulkCreate(template: AnimationsList, doPreload: boolean?, priority: Enum.AnimationPriority?) -> AnimationsList Similar to :Create(), except it loads all entries in template at once and returns a copy (with preloaded animations if doPreload is true).
Action:Remove(key: any) -> () Removes a the key Action entry and stops and destroys all animations assigned to it.
Action:StopAll(key: any) -> () Stops all animations assigned to the key Action entry without destroying them or removing it from the list.
Action:GetAction(key: any) -> {Types.AnimInfo}? Returns all animations assigned to the key Action entry, if any.
Action:GetRandomActionAnim(key: any) -> AnimationTrack Returns a randomly selected based on weight AnimationTrack from the list of animations assigned to the key Action entry.
Action:SetEmoteBindable(e: BindableFunction) -> () Sets the emote bindable hook to be used for playing emote animations. e must be named PlayEmote and parented to a LocalScript named Animate in Players.LocalPlayer.Character.

Static Functions

The main SimpleAnimate module has quite a few static utility functions (functions accessed with . and not :) and I’ll go through all of them here.

Method Name, Parameters & Return Type Description
SimpleAnimate.new(rig: Model, doPreload: boolean?, coreAnimations: AnimationsList?, emoteAnimations: AnimationsList?, stateMachine: StateMachine?) -> AnimationController Creates a new AnimationController for a rig, where you can choose to pass the coreAnimations (e.g walking, running, swimming, etc.) to use, the emoteAnimations to use (e.g waving, dance1, dance2, dance3, etc. Note that emote list keys don’t have to be pose types) to use, wether or not to preload both (doPreload), and an optional stateMachine to use instead of the default Humanoid. (Module will not error if it can’t find a humanoid, if you provide a state machine.)
SimpleAnimate.fromExisting(rig: Model) -> AnimationController? Returns an AnimationController for a rig if it exists.
SimpleAnimate.waitForController(rig: Model, timeOut: number?) -> AnimationController? Yields the current thread until a controller exists for a rig.
`SimpleAnimate.getCopyOfAnimsList(name: “R6” “R15”, specifier “Animations”
`SimpleAnimate.getCopyOfAnims(name: “R6” “R15”) → (AnimationsList, AnimationsList)`
SimpleAnimate.getAnimPackageAsync(player: Player?, defaultPoseSourceIfUnavailable: AnimationsList?) -> AnimationsList? Returns a players currently owned animation package, as an animations list. Note that if called on the server, player must’nt be nil, else if called on the client, then regardless of the player value you will always get the animation package of the local player.
SimpleAnimate.getEmotePackageAsync(player: Player?, defaultPoseSourceIfUnavailable: AnimationsList?) -> AnimationsList? Returns a players currently owned emote package, as an animations list. Note that if called on the server, player must’nt be nil, else if called on the client, then regardless of the player value you will always get the emote package of the local player

NOTE: Both async functions will only work for R15 animations, as Roblox only supports animation & emote packages for R15

Example Usage & Best Practices

The script which requires SimpleAnimate should always be a LocalScript named Animate placed in StarterCharacterScripts (or you can just override it manually lolz) to ensure the default Animate script doesn’t interfere with this module.

Example of what is essentially the default animate script:

local AnimationController = require(game.ReplicatedStorage.AnimationController) -- Replace this with your own path to the module

local anims, emotes = AnimationController.getCopyOfAnims("R15")
anims = AnimationController.getAnimPackageAsync(nil, anims)

local animController = AnimationController.new(
	script.Parent, true, anims, emotes
)

local Core = animController.Core
local Action = animController.Action

local PoseController = Core.PoseController
local Connections = Core.Connections

Connections.MoveAnimationSpeedMultiplier= 0.8 -- Dont want the character to move too fast
-- Setup emote bindable hook
Action:SetEmoteBindable(script.PlayEmote)

(Make sure to parent the script which requires the module to StarterCharacterScripts, otherwise replication won’t work!)

And if you haven’t already, please check out my other modules SimpleZone and SimpleComplete!

Please report any bugs/issues, and may your code always be blessed! :heart:

41 Likes

If you have any questions on, for example, how to modify certain behaviour of this module, feel free to ask me!

3 Likes

I do in fact, how could I modify your module to use already, pre-existing AnimationTracks loaded on the character?

2 Likes

Are you trying to use them as core or action animations? Be more specific for what you’re trying to do and I’ll help you out :slightly_smiling_face:

1 Like

I already have a module under the Player which holds a big table of all animations loaded on the Character, and I want to switch your module’s behavior of creating them at runtime to loading each of those animations with the prefix of “Animate_”
e.g.

Walk = {
	{id = "",weight = 10}
}

to

Walk = nil
-- --> end of Animations table
for Name in Animations do
	Animations[Name] = AnimationsModule:GetAnim("Animate_"..Name)
end
3 Likes

Ah, you can simply do this!

for pose, animInfo in Animations do
	for _, idInfo in animInfo do
		idInfo.anim = AnimationsModule:GetAnim("Animate_"..pose)
	end
end

And then just replacing the part that preloads the animation with it
image

1 Like

Thank you, that’s useful. I was meaning to get rid of the default “id” and “weight” keys in the default Animations table… Can I safely remove those or are there needed params?

1 Like

Nope, if you remove the default animation preloading theres pretty much nowhere else that uses the id and weight.

3 Likes

If you want to edit it more, I suggest looking under the PoseController module under Core. It controls all the Core animations, and it’s pretty short too!

1 Like

Update:

  • The weight member of the Animations table is actually used now, everytime a looped animation loops the module will now search a new random animation utilizing the weights and play that (similarly to how the default Animate script does)

  • Added a new Emotes module which allows you to set a PlayEmote BindableFunction to play your emote animations! (more detail below)

  • Made changePose work even if setCoreActive(false) was called (this is done with a is_Core optional argument which the module itself always passes as true, however you don’t need to!)

  • Initialized pose to “Idle” on join (you used to not play any animations upon joining)

  • The locations of the modules storing and preloading AnimationTracks have been moved to a new module called Animations which stores both CoreAnims and EmoteAnims!


    If you wish to add your own custom emotes, simply just add it as a member in the EmoteAnims module.

Example Animate script utilizing the new changes

--[[
	EXAMPLE ANIMATE SCRIPT
	
	Location: StarterCharacterScripts
	Name: Animate
]]

local AnimationController = require(script.Parent.AnimationController)
local Emotes = AnimationController.Emotes
local Core = AnimationController.Core

-- Set the emote BindableFunction to the one inside the script (HAS TO BE CALLED "PlayEmote"!!!)
-- so that the module can use it to play emotes
Emotes.setEmoteBindable(script.PlayEmote)
-- Set the running threshold to 16
-- If the player is moving below this speed, they are walking
-- Else, they are running
Core.setRunThreshold(16)

Core.PoseChanged:Connect(function(old, new)
   if new == "Freefall" then
     print("Player is falling!")
   end
end)
2 Likes

Patch:

  • Fixed emotes not replaying Idle animation upon ending
  • Fixed missing dependencies
  • Added playAnimation as a public static function for Core.
    This function plays an animation for a specific pose no matter if the last pose is the same or not, unlike changePose which will only play the animation if the last pose isn’t the same as the new one.
2 Likes

Does this work for both R6 and R15, and NPC rigs?
I’ve tried to modify the Roblox’s default Animate script for an NPC and I constantly got this EXTREMELY annoying warning:

AnimationTrack limit of 256 tracks for one Animator exceeded, new animations will not be played.

Dispite numberous attempt of trying to fix and because the warning doesn’t point at the problem itself.
I’m running out of hope and I probably have to rewrite my own code again or make a post about it.

Regarding the flaws of Roblox’s Animate script itself, thank you for making this module.

2 Likes

I mean hopefully it shouldn’t do that? xd, it preloads the animation tracks so it shouldn’t exceed the animation track limit unless you have a ton for some reason

2 Likes

Also yeah it works for both R6 and R15, for NPCs however you are going to have to remove the Emotes module (duh)

4 Likes

I just found out that the module must be inside the character for the animations to work.
Wouldn’t it be better that the module can be put somewhere else like in ReplicatedStorage and required using a script instead?

2 Likes

You’re suggesting I turn the module from a controller into an OOP object? Hmm that’s a pretty good idea actually

I’ll try it and see if I like how it works

1 Like

Judging by how you structured the module itself. It should have been an OOP object.
I don’t see why the module itself needs to be parented to a character every time instead of being in one place.

1 Like

Yeah I based it off an old version I developed, this was kinda meant to be just a remaster xd

I’ll let you know if the OOP implementation works

2 Likes

I would suggest adding the Character parameter when requiring the module by using this common method:

AnimationController.new(Character)

Maybe adding a Humanoid one as well but this is my personal suggestion since I like to rename my NPC’s Humanoid.

The current method works but it seems less feasible when there’s a lot of players in the game and there’s AnimationController inside every character along side the Roblox’s Animate script.

1 Like

Yes the rig will be passed as the only required parameter to .new() (i also have animation module parameters)

2 Likes