Character basics
- A player character is a
Modelwith aHumanoid,HumanoidRootPart(the root; move the character withcharacter:PivotTo(cframe)), R15 body parts, and anAnimateLocalScript. - Defaults (walk speed, jump, health, R6/R15, collisions, body scaling) are configured in Studio's
Avatar Settings window and
StarterPlayer(CharacterWalkSpeed,CharacterJumpHeightorCharacterUseJumpPower+CharacterJumpPower). - Custom character: put a
ModelnamedStarterCharacterinStarterPlayer. Custom per-player spawns:Players.CharacterAutoLoads = false, thenplayer: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/JumpHeightare trivially exploitable; enforce limits on the server or use server authority (roblox-security,roblox-physics).
Health and damage (server)
--!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
Healthscript viaStarterCharacterScripts. Humanoid.BreakJointsOnDeath = false+ ragdoll constraints for ragdolls.- States:
humanoid:ChangeState(Enum.HumanoidStateType.Jumping),SetStateEnabled(state, false),StateChanged.MoveDirection,FloorMaterial,RootPart,SeatPartare read-only helpers. humanoid:MoveTo(position)walks NPCs (times out after 8 s; re-issue for long paths — seeroblox-npc-ai).
Appearance
--!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
--!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
Animationonce per Animator and reuse the track; don't callLoadAnimationevery 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 withAdjustWeight, change speed withAdjustSpeed, stop withStop(fadeTime). - Events:
track.Stopped,track.Ended, markers viaGetMarkerReachedSignal(KeyframeReachedis 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
Animatescript'sStringValue/Animationchildren (animate.run.RunAnim.AnimationId) onCharacterAdded, or ship a customAnimateinStarterCharacterScripts. - Procedural:
IKControl(look-at, foot planting, reaching) instead of hand-written joint math.
Tweening
--!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
CFrameValueand callmodel:PivotTo(value)in itsChangedhandler, or tween the primary part with the rest welded to it. TweenService:GetValue(alpha, style, direction)for custom interpolation;SmoothDampfor 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.
Related skills
roblox-npc-ai (NPC movement), roblox-physics (ragdolls, knockback), roblox-input (ability inputs),
roblox-security (movement/damage validation), roblox-game-systems (combat).