Characters and animation - Humanoid properties/states, spawning (LoadCharacterAsync), health and damage, HumanoidDescription appearance, Animator/AnimationTrack playback, priorities and markers, default animation replacement, Tools, TweenService, IKControl, Character Controller Library (beta). Use when changing movement, abilities, damage, respawn, avatars, animations, tweens, or tools/weapons.
Installs into .claude/skills of the current project.
Are you the author of Roblox Characters Animation?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/el4cteo-roblox-characters-animation)
---
name: roblox-characters-animation
description: Characters and animation - Humanoid properties/states, spawning (LoadCharacterAsync), health and damage, HumanoidDescription appearance, Animator/AnimationTrack playback, priorities and markers, default animation replacement, Tools, TweenService, IKControl, Character Controller Library (beta). Use when changing movement, abilities, damage, respawn, avatars, animations, tweens, or tools/weapons.
---
# Characters and animation
## Character basics
- A player character is a `Model` with a `Humanoid`, `HumanoidRootPart` (the root; move the character
with `character:PivotTo(cframe)`), R15 body parts, and an `Animate` LocalScript.
- Defaults (walk speed, jump, health, R6/R15, collisions, body scaling) are configured in Studio's
**Avatar Settings** window and `StarterPlayer` (`CharacterWalkSpeed`, `CharacterJumpHeight` or
`CharacterUseJumpPower` + `CharacterJumpPower`).
- Custom character: put a `Model` named `StarterCharacter` in `StarterPlayer`. Custom per-player
spawns: `Players.CharacterAutoLoads = false`, then `player:LoadCharacterAsync()` when ready
(e.g. after data loads). `LoadCharacter` (non-Async) is deprecated.
- Characters are destroyed and recreated on death/respawn: bind per-character logic in `CharacterAdded`.
- Client-set `WalkSpeed`/`JumpHeight` are trivially exploitable; enforce limits on the server or use
server authority (`roblox-security`, `roblox-physics`).
## Health and damage (server)
```luau
--!strict
local Players = game:GetService("Players")
local function damage(target: Model, amount: number, source: Player?)
local humanoid = target:FindFirstChildOfClass("Humanoid")
if not humanoid or humanoid.Health <= 0 then
return
end
humanoid:SetAttribute("LastDamagedBy", if source then source.UserId else 0)
humanoid:TakeDamage(amount) -- respects ForceField; use Health -= amount to bypass
end
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
local humanoid = character:WaitForChild("Humanoid") :: Humanoid
humanoid.MaxHealth = 150
humanoid.Health = 150
humanoid.Died:Once(function()
local killerId = humanoid:GetAttribute("LastDamagedBy")
print(`{player.Name} died (killer {killerId})`)
end)
end)
end)
return damage
```
- Disable default regen by deleting/replacing the `Health` script via `StarterCharacterScripts`.
- `Humanoid.BreakJointsOnDeath = false` + ragdoll constraints for ragdolls.
- States: `humanoid:ChangeState(Enum.HumanoidStateType.Jumping)`, `SetStateEnabled(state, false)`,
`StateChanged`. `MoveDirection`, `FloorMaterial`, `RootPart`, `SeatPart` are read-only helpers.
- `humanoid:MoveTo(position)` walks NPCs (times out after 8 s; re-issue for long paths — see `roblox-npc-ai`).
## Appearance
```luau
--!strict
local Players = game:GetService("Players")
local function applyUniform(player: Player)
local character = player.Character
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if not humanoid then
return
end
local ok, description = pcall(function()
return Players:GetHumanoidDescriptionFromUserIdAsync(player.UserId)
end)
if not ok then
return
end
description.Shirt = 123456 -- asset ids
description.Pants = 654321
description.HeightScale = 1.05
pcall(function()
humanoid:ApplyDescriptionAsync(description) -- server-side; replicates
end)
end
return applyUniform
```
`Player:LoadCharacterWithHumanoidDescriptionAsync(description)` spawns directly with an outfit.
All the non-Async variants (`ApplyDescription`, `GetHumanoidDescriptionFromUserId`, ...) are deprecated.
## Animation playback
```luau
--!strict
local function playAttack(character: Model, animation: Animation): AnimationTrack?
local humanoid = character:FindFirstChildOfClass("Humanoid")
local animator = humanoid and humanoid:FindFirstChildOfClass("Animator")
if not animator then
return nil
end
local track = animator:LoadAnimation(animation) -- Humanoid:LoadAnimation is deprecated
track.Priority = Enum.AnimationPriority.Action
track.Looped = false
track:GetMarkerReachedSignal("Hit"):Connect(function(param)
print("hit frame reached", param) -- apply damage window, play sound, etc.
end)
track:Play(0.1) -- fade time
return track
end
return playAttack
```
- **Where to play**: the local player's own character → play on the client (replicates automatically via
its Animator); NPCs → play on the server (or on clients for purely cosmetic, high-count NPCs).
- Load each `Animation` once per Animator and reuse the track; don't call `LoadAnimation` every time an
action happens (there's a per-Animator track limit). Exception: server authority (query live tracks).
- Priorities: `Core < Idle < Movement < Action < Action2 < Action3 < Action4`. Blend with `AdjustWeight`,
change speed with `AdjustSpeed`, stop with `Stop(fadeTime)`.
- Events: `track.Stopped`, `track.Ended`, markers via `GetMarkerReachedSignal` (`KeyframeReached` is superseded).
- Animation assets must be owned by (or shared with, via asset permissions) the experience's owner —
otherwise they silently fail to play in live servers.
- Replace defaults by editing the `Animate` script's `StringValue`/`Animation` children
(`animate.run.RunAnim.AnimationId`) on `CharacterAdded`, or ship a custom `Animate` in
`StarterCharacterScripts`.
- Procedural: `IKControl` (look-at, foot planting, reaching) instead of hand-written joint math.
## Tweening
```luau
--!strict
local TweenService = game:GetService("TweenService")
local door = Instance.new("Part")
door.Anchored = true
door.Parent = workspace
local info = TweenInfo.new(0.6, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local open = TweenService:Create(door, info, { CFrame = door.CFrame * CFrame.new(0, 8, 0) })
open.Completed:Once(function(state)
print("door tween finished:", state)
end)
open:Play()
```
- Tween **anchored** parts or UI. For unanchored/physics objects use constraints.
- Tweening on the server replicates every frame and stutters: for visual-only motion, tween on each client
(the server sets the final state or an attribute clients react to).
- Models: tween a `CFrameValue` and call `model:PivotTo(value)` in its `Changed` handler, or tween the
primary part with the rest welded to it.
- `TweenService:GetValue(alpha, style, direction)` for custom interpolation; `SmoothDamp` for follow motion.
## Tools
`Tool` in `StarterPack`/`Backpack` with a `Handle` part (or `RequiresHandle = false`). `Equipped`,
`Unequipped`, `Activated` fire on both client and server (`Activated` comes from the client: validate
rate and state on the server). Keep weapon logic in modules and use tags rather than scripts inside
every tool clone.
## Character Controller Library (beta)
Roblox's CCL (`require("@rbx/AvatarAbilities")`) replaces the fixed Humanoid state machine with
composable **abilities** (Running, Jumping, Climbing, custom Dash...) coordinated by labels, sensors,
conditions (`StartsWhen`/`RunsWhile` with `All`/`Any`/`Not`), conflicts (`Blocks`/`Stops`/`Suspends`),
and lifecycle callbacks. Enable it via **File → Beta Features** and **Avatar Settings → Movement →
Abilities**. Only use it when the project has opted in; APIs may change while in beta.
Sketch: [references/character-controller-library.md](references/character-controller-library.md).
## Related skills
`roblox-npc-ai` (NPC movement), `roblox-physics` (ragdolls, knockback), `roblox-input` (ability inputs),
`roblox-security` (movement/damage validation), `roblox-game-systems` (combat).