Animation Packages Guide
This guide covers how to work with Roblox animation packages and player-equipped animations in SimpleAnimate.
What are Animation Packages?
Animation packages are collections of animations that players can equip from the Roblox catalog. They include:
- Movement animations (idle, walk, run, jump)
- Emote animations (wave, dance, laugh, etc.)
- Custom character animations based on player preferences
SimpleAnimate can automatically load these player-equipped animations.
Loading Player Animations
Server-Side Loading
local Players = game:GetService("Players")
local SimpleAnimate = require(game.ReplicatedStorage.SimpleAnimate)
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
-- Wait for animator
local animator = character:WaitForChild("Humanoid"):WaitForChild("Animator")
-- Load player's animation package
local coreAnims = SimpleAnimate.getAnimPackageAsync(player)
local emoteAnims = SimpleAnimate.getEmotePackageAsync(player)
-- Create controller with player's animations
local controller = SimpleAnimate.new(
character,
true,
coreAnims,
emoteAnims
)
print(player.Name, "loaded with custom animations")
end)
end)
Client-Side Loading
-- LocalScript in StarterPlayer.StarterCharacterScripts
local SimpleAnimate = require(game.ReplicatedStorage.SimpleAnimate)
local character = script.Parent
-- Get local player's animations (no player parameter needed on client)
local coreAnims = SimpleAnimate.getAnimPackageAsync()
local emoteAnims = SimpleAnimate.getEmotePackageAsync()
-- Create controller
local controller = SimpleAnimate.new(
character,
true,
coreAnims,
emoteAnims
)
Fallback Animations
When loading animation packages, provide fallbacks for missing animations:
Using Default Fallbacks
-- If player doesn't have custom animations, R15 defaults are used
local coreAnims = SimpleAnimate.getAnimPackageAsync(player)
-- Missing animations automatically filled with R15 defaults
Custom Fallbacks
-- Define your custom fallback animations
local customDefaults = {
Idle = {{id = "rbxassetid://YOUR_IDLE", weight = 10}},
Walk = {{id = "rbxassetid://YOUR_WALK", weight = 10}},
Run = {{id = "rbxassetid://YOUR_RUN", weight = 10}}
}
-- Use custom fallbacks
local coreAnims = SimpleAnimate.getAnimPackageAsync(player, customDefaults)
-- Now if player doesn't have custom animations, your defaults are used
Selective Fallbacks
-- Get player's animations
local coreAnims = SimpleAnimate.getAnimPackageAsync(player)
-- Override specific animations with your own
coreAnims.Idle = {{id = "rbxassetid://YOUR_SPECIAL_IDLE", weight = 10}}
-- Keep player's other animations, but force your idle
local controller = SimpleAnimate.new(character, true, coreAnims)
Mixing Player and Custom Animations
Player Animations with Custom Emotes
-- Use player's movement animations
local coreAnims = SimpleAnimate.getAnimPackageAsync(player)
-- But use your custom emotes
local customEmotes = {
wave = {{id = "rbxassetid://CUSTOM_WAVE", weight = 10}},
dance = {{id = "rbxassetid://CUSTOM_DANCE", weight = 10}},
cheer = {{id = "rbxassetid://CUSTOM_CHEER", weight = 10}}
}
local controller = SimpleAnimate.new(
character,
true,
coreAnims,
customEmotes
)
Custom Animations with Player Emotes
-- Use your custom movement animations
local customCore = {
Idle = {{id = "rbxassetid://YOUR_IDLE", weight = 10}},
Walk = {{id = "rbxassetid://YOUR_WALK", weight = 10}},
Run = {{id = "rbxassetid://YOUR_RUN", weight = 10}}
}
-- But keep player's emotes
local emoteAnims = SimpleAnimate.getEmotePackageAsync(player)
local controller = SimpleAnimate.new(
character,
true,
customCore,
emoteAnims
)
Handling Different Rig Types
R6 vs R15 Detection
local function getRigType(character)
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
return humanoid.RigType == Enum.HumanoidRigType.R6 and "R6" or "R15"
end
return "R15" -- Default
end
local function setupCharacter(player, character)
local rigType = getRigType(character)
-- Get appropriate defaults
local defaultCore, defaultEmotes = SimpleAnimate.getCopyOfAnims(rigType)
-- Load player's animations with appropriate fallbacks
local coreAnims = SimpleAnimate.getAnimPackageAsync(player, defaultCore)
local emoteAnims = SimpleAnimate.getEmotePackageAsync(player, defaultEmotes)
local controller = SimpleAnimate.new(character, true, coreAnims, emoteAnims)
end
Force Specific Rig Type
-- Always use R15 animations regardless of player's equipped package
local r15Core, r15Emotes = SimpleAnimate.getCopyOfAnims("R15")
local controller = SimpleAnimate.new(
character,
true,
r15Core,
r15Emotes
)
Caching Animation Packages
Cache player animation packages to avoid repeated API calls:
local Players = game:GetService("Players")
local SimpleAnimate = require(game.ReplicatedStorage.SimpleAnimate)
-- Cache player animations
local playerAnimCache = {}
local function getPlayerAnimations(player)
-- Check cache first
if playerAnimCache[player.UserId] then
return playerAnimCache[player.UserId]
end
-- Load from API
local coreAnims = SimpleAnimate.getAnimPackageAsync(player)
local emoteAnims = SimpleAnimate.getEmotePackageAsync(player)
-- Cache for future use
playerAnimCache[player.UserId] = {
core = coreAnims,
emotes = emoteAnims
}
return playerAnimCache[player.UserId]
end
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
local anims = getPlayerAnimations(player)
local controller = SimpleAnimate.new(
character,
true,
anims.core,
anims.emotes
)
end)
end)
-- Clear cache when player leaves
Players.PlayerRemoving:Connect(function(player)
playerAnimCache[player.UserId] = nil
end)
Validating Animation Packages
Check if animations loaded successfully:
local function validateAnimationPackage(anims, packageName)
local requiredPoses = {"Idle", "Walk", "Run", "Jump"}
for _, pose in requiredPoses do
if not anims[pose] or #anims[pose] == 0 then
warn(packageName, "missing", pose, "animation")
return false
end
end
return true
end
-- Usage
local coreAnims = SimpleAnimate.getAnimPackageAsync(player)
if validateAnimationPackage(coreAnims, "Core Animations") then
print("Animation package valid")
else
-- Use defaults instead
coreAnims = SimpleAnimate.getCopyOfAnimsList("R15", "Animations")
end
Overriding Specific Animations
Keep Player Package, Override One Pose
local coreAnims = SimpleAnimate.getAnimPackageAsync(player)
-- Player has custom animations, but we want a special idle
coreAnims.Idle = {
{
id = "rbxassetid://SPECIAL_IDLE",
weight = 10,
speed = 0.8,
fadeTime = 0.3
}
}
local controller = SimpleAnimate.new(character, true, coreAnims)
Blend Custom with Player Animations
local coreAnims = SimpleAnimate.getAnimPackageAsync(player)
-- Add variants to player's animations
table.insert(coreAnims.Idle, {
id = "rbxassetid://EXTRA_IDLE_VARIANT",
weight = 3, -- Lower weight = less common
fadeTime = 0.3
})
-- Now player has their animations PLUS your variant
local controller = SimpleAnimate.new(character, true, coreAnims)
Animation Package Metadata
Store and retrieve animation package information:
local AnimationPackages = {
Stylish = {
name = "Stylish",
description = "Cool and modern animations",
animations = {
Idle = {{id = "rbxassetid://001", weight = 10}},
Walk = {{id = "rbxassetid://002", weight = 10}},
Run = {{id = "rbxassetid://003", weight = 10}}
}
},
Classic = {
name = "Classic",
description = "Traditional Roblox animations",
animations = SimpleAnimate.getCopyOfAnims("R15")
}
}
local function applyPackage(player, packageName)
local package = AnimationPackages[packageName]
if not package then
warn("Package not found:", packageName)
return
end
player.CharacterAdded:Connect(function(character)
local controller = SimpleAnimate.new(
character,
true,
package.animations
)
print("Applied", package.name, "to", player.Name)
end)
end
-- Usage
applyPackage(player, "Stylish")
Dynamic Package Switching
Allow players to switch animation packages at runtime:
local currentControllers = {}
local function switchAnimationPackage(player, packageName)
local character = player.Character
if not character then return end
-- Destroy old controller
if currentControllers[player] then
currentControllers[player]:Destroy()
end
-- Get new package
local package = AnimationPackages[packageName]
-- Create new controller
local controller = SimpleAnimate.new(
character,
true,
package.animations
)
currentControllers[player] = controller
print(player.Name, "switched to", packageName)
end
-- Remote event for switching
local switchPackageRemote = game.ReplicatedStorage.SwitchAnimPackage
switchPackageRemote.OnServerEvent:Connect(function(player, packageName)
switchAnimationPackage(player, packageName)
end)
Error Handling
Robust error handling for animation package loading:
local function safeLoadAnimations(player)
local success, coreAnims = pcall(function()
return SimpleAnimate.getAnimPackageAsync(player)
end)
if not success then
warn("Failed to load player animations:", coreAnims)
-- Use defaults
coreAnims = SimpleAnimate.getCopyOfAnimsList("R15", "Animations")
end
local success2, emoteAnims = pcall(function()
return SimpleAnimate.getEmotePackageAsync(player)
end)
if not success2 then
warn("Failed to load player emotes:", emoteAnims)
emoteAnims = SimpleAnimate.getCopyOfAnimsList("R15", "Emotes")
end
return coreAnims, emoteAnims
end
-- Usage
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
local coreAnims, emoteAnims = safeLoadAnimations(player)
local controller = SimpleAnimate.new(
character,
true,
coreAnims,
emoteAnims
)
end)
end)
Package Selection UI
Create a UI for players to choose animation packages:
-- Client Script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
-- Create UI
local screenGui = Instance.new("ScreenGui")
screenGui.Parent = playerGui
local frame = Instance.new("Frame")
frame.Size = UDim2.new(0, 300, 0, 400)
frame.Position = UDim2.new(0.5, -150, 0.5, -200)
frame.Parent = screenGui
-- List of packages
local packages = {"Default", "Stylish", "Athletic", "Ninja", "Robot"}
for i, packageName in packages do
local button = Instance.new("TextButton")
button.Size = UDim2.new(1, -20, 0, 50)
button.Position = UDim2.new(0, 10, 0, (i - 1) * 60 + 10)
button.Text = packageName
button.Parent = frame
button.MouseButton1Click:Connect(function()
-- Request package change
ReplicatedStorage.SwitchAnimPackage:FireServer(packageName)
end)
end
Pre-Loading Animation Packages
Pre-load animation packages before character spawns:
local loadedPackages = {}
-- Pre-load all packages on server start
local function preLoadPackages()
for name, package in AnimationPackages do
print("Pre-loading package:", name)
loadedPackages[name] = package.animations
end
end
preLoadPackages()
-- Instant assignment when player spawns
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
local packageName = player:GetAttribute("AnimPackage") or "Default"
local anims = loadedPackages[packageName]
local controller = SimpleAnimate.new(character, true, anims)
end)
end)
Best Practices
- Always provide fallbacks: Handle cases where player animations fail to load
- Cache when possible: Avoid repeated API calls for the same player
- Validate packages: Check that required animations exist
- Handle errors gracefully: Use pcall for API calls
- Test with different rigs: Ensure compatibility with R6 and R15
- Consider performance: Pre-load packages when possible
- Respect player choice: Let players use their equipped animations when appropriate
- Provide options: Give players ability to override with custom packages
Common Issues
Animations Not Loading
-- ❌ Problem: Server call without player parameter
local anims = SimpleAnimate.getAnimPackageAsync() -- Error on server!
-- ✅ Solution: Always pass player on server
local anims = SimpleAnimate.getAnimPackageAsync(player)
Missing Animations
-- ❌ Problem: Not providing fallbacks
local anims = SimpleAnimate.getAnimPackageAsync(player)
-- Some poses might be missing
-- ✅ Solution: Provide fallbacks
local defaults = SimpleAnimate.getCopyOfAnims("R15")
local anims = SimpleAnimate.getAnimPackageAsync(player, defaults)
Repeated Loading
-- ❌ Problem: Loading on every character spawn
player.CharacterAdded:Connect(function(character)
local anims = SimpleAnimate.getAnimPackageAsync(player) -- Loads every time
end)
-- ✅ Solution: Cache the results
local playerAnims = SimpleAnimate.getAnimPackageAsync(player)
player.CharacterAdded:Connect(function(character)
local controller = SimpleAnimate.new(character, true, playerAnims)
end)