Skip to content
StwGaroCreator hub
← All Roblox Studio tools

Documentation for Pace 0.2.4. Your own movement system and published, accessible animations are required. The controller does not move the rig.

Pace 0.2.4 — buyer guide

Pace estimates a looping clip's forward ground speed from sampled foot contacts, shows the speeds your clips cover, and installs a readable playback controller. It selects animations and adjusts their playback rate; it does not move the creature. Your movement system must do that.

1. Prepare the plugin, rig and clips

Install and enable Pace as a Roblox Studio plugin, then open its Pace toolbar button. Do not insert the complete plugin into a game as its runtime. Work in Edit mode and save a copy of your place before changing a rig.

Select one Model containing:

Use looping, in-place, Linear clips. The first keyframe must be at time 0; each keyframe must contain the same set of animated joint poses, matched to the rig hierarchy. Joint poses must have weight 1. The sole exception is the unique direct root-container Pose: weight 0 is accepted only when it names the rig root and has an identity CFrame. This exception does not allow zero-weight joints or root motion. First and last joint poses must close the loop within Pace's small numerical tolerances.

Animations must be readable by your Studio account and loadable in the target experience. Publish your own clips through the Animation Editor and add Animation objects carrying their IDs. Pace does not publish animations or grant asset permissions. A local sequence can be measured, but cannot be shipped as a runtime animation without an accessible asset ID.

2. READ → CHECK → INSTALL

  1. READ SELECTED RIG collects and measures the current clips. Inspect each refusal, measured speed, standing classification and coverage gap. After changing joints, scale, poses or IDs, press READ again; do not rely on an earlier measurement.
  2. CHECK THE SLIP evaluates five sample speeds derived from the slowest accepted moving clip. The displayed slip is sampled horizontal contact drift in studs, not a physical traction guarantee. It can be nonzero even at the estimated ground speed. A good result covers those samples only, not every speed or every scene.
  3. INSTALL CONTROLLER adds a PaceConfig Folder and a Pace ModuleScript directly to the Model, plus ownership/version attributes. Read the installation message: local/unpublished entries are skipped and the installed plan is rebuilt from the remaining entries. At least one accepted moving clip needs a valid published ID. An omitted local clip can leave a new gap in the installed plan.
  4. Save the place, reopen it and check that both objects remain in your Model. Installation does not start playback or create the integration Script. Add the runtime connection below and test your actual moving creature.

The panel scrolls vertically. Selection changes and Undo/Redo clear cached results; press READ before checking or installing again. Once installed, the action button becomes REMOVE CONTROLLER. To rebuild through the UI after editing clips, stop the game test, remove the old managed output, READ the current rig and INSTALL again. Preserve any deliberate edits to generated configuration before replacing it.

3. Connect the runtime — required

For an NPC, this example can live in a normal Script in ServerScriptService. Replace MyCreature with your Model's name. Keep your movement code separate and coordinate any existing animation scripts: Pace does not disable them or take exclusive control of an existing Animator.

local RunService = game:GetService("RunService")
local model = workspace:WaitForChild("MyCreature")
local runtime = model:FindFirstChild("Pace")
assert(runtime and runtime:IsA("ModuleScript"), "Install Pace on MyCreature first")

local controller, err = require(runtime).attach(model)
assert(controller, err)

local connections = {}
local cleaned = false
local function cleanup()
    if cleaned then return end
    cleaned = true
    for _, connection in ipairs(connections) do
        connection:Disconnect()
    end
    table.clear(connections)
    controller:destroy()
end

connections[#connections + 1] = RunService.Heartbeat:Connect(function(dt)
    if not model.Parent then
        cleanup()
        return
    end
    local ok, clipName, rate, detail = pcall(controller.update, controller, dt)
    if not ok then
        warn("Pace update failed:", clipName)
        cleanup()
    elseif clipName == nil and detail ~= "gap" then
        warn("Pace playback stopped:", detail)
        cleanup()
    end
    -- detail == "gap": Pace stops its current track, not the creature.
    -- Add a suitable clip or handle that movement state in your own system.
end)
connections[#connections + 1] = model.Destroying:Connect(cleanup)
connections[#connections + 1] = script.Destroying:Connect(cleanup)

-- Also call cleanup() when your own NPC lifecycle retires this controller.

attach(model) returns a controller or nil, error. It reads the installed configuration and loads its animations. Use one Pace controller per Model and destroy the previous controller before attaching again. Multiple ambiguous Animators/controllers are refused. If no suitable Animator exists, Pace can create one; destroy() removes only the animation infrastructure that this attachment created, along with its own tracks. It leaves pre-existing animation infrastructure and the installed Pace/PaceConfig objects intact.

update(dt) reads the motion root's AssemblyLinearVelocity magnitude in the horizontal X/Z plane, then applies the plan. It does not infer speed from Humanoid.WalkSpeed or distance travelled by CFrame teleports. Vertical velocity is ignored; backward and sideways motion are not separate animation states.

If your movement system supplies the speed, replace the example's pcall(controller.update, ...) line with the following, using your system's current horizontal speed as horizontalSpeedInStudsPerSecond. Keep the existing error/gap handling:

local ok, clipName, rate, detail = pcall(function()
    controller:setSpeed(horizontalSpeedInStudsPerSecond)
    return controller:apply()
end)

Use a finite speed between 0 and 10,000. Do not call update() immediately after setSpeed(): it overwrites your supplied value with measured velocity. Successful playback returns the clip name, rate and a boolean rate-cap flag; the third result is not a universal "moving"/"standing" status. A coverage gap returns nil, 1, "gap"; errors return an explanatory value. stop() stops the current Pace track, but the next update() or apply() can start it again. Disconnect the caller's loop and call destroy() for final cleanup; destroy() cannot disconnect a Heartbeat connection owned by your Script.

4. Coverage, safety and limits

Quick diagnosis

Symptom Check
No clips / duplicate clip name Put Animation objects or local sequence saves inside the selected Model; give every collected clip a unique name.
Clip cannot be read or loaded Check the exact ID, asset access in Studio and permissions in the target experience. A successful local read is not an experience-wide permission guarantee.
No feet / no stable contact Check the Motor6D tree and authored foot heights; airborne clips cannot supply grounded forward contact.
All clips skipped at installation Use Animation objects with accessible published IDs, including at least one accepted moving clip.
Installed, but nothing plays Add the Script connection; inspect attach errors, actual root velocity, standing/coverage gaps and competing animation scripts. For CFrame-driven motion, provide speed with setSpeed + apply.
A change seems ignored Stop the runtime, READ after edits, rebuild the installed output if needed, and reattach to read the new configuration.
Slip remains Review the sampled report and actual playback. Add a better-matched clip or revise the animation; Pace does not promise zero skating.

Keep your accepted source clips and a saved place copy. Test the complete movement-and-animation setup in the target experience before releasing it to players.

Download this guide as Markdown

For support, use the contact form and state “Pace 0.2.4”, your rig type and the exact error message. Do not send passwords or private credentials.