Tutorial
The runtime that walks the steps.
It owns the presentation, not the truth. When a step ends the runtime destroys the step Janitor and clears the spotlight, the pointer and the camera, so every step starts on a clean screen and no step can leak visuals into the next one.
const tutorial = Tutorial.build({
Step.new(1, { FocusUI.to(shopButton, { Advance = true }) }),
Step.new(2, { PointWorld.to(fountainPart) }),
})
tutorial:Start()
If tutorial progress lives on your server, do not call Advance from the client. Let the
server decide and mirror it with GoTo, or set Replicate = true and let the kit do it.
Types
TutorialConfig
interface TutorialConfig {Replicate: boolean?--
Mirror progress with the server. Defaults to false
}The optional second argument to Tutorial.build.
Replicate = true reports every step and the completion to the server, and follows the steps
the server sends back. It requires require(TutorialKit.Server):Start() on the server, and it
makes build yield until the bridge has replicated.
Properties
Spotlight
This item is read only and cannot be modified. Read OnlyTutorial.Spotlight: SpotlightThe dim overlay shared by every step. Also on every ActionContext, so an action rarely needs to reach for this one.
Pointer
This item is read only and cannot be modified. Read OnlyTutorial.Pointer: WorldPointerThe world guidance shared by every step: beam, floating icon and outline.
Camera
This item is read only and cannot be modified. Read OnlyTutorial.Camera: CameraTourThe cinematic camera shared by every step.
StepChanged
eventsFires on entering a step, with the new order and the one left behind. The second argument is nil on the first step.
tutorial.StepChanged:Connect(function(step, previousStep)
print("now on", step, "coming from", previousStep)
end)
Completed
eventsFires once, when the tutorial walks past its last step or Tutorial:Complete is called.
Functions
build
constructorBuilds a tutorial from a list of steps.
Steps may be listed in any order, because the kit sorts them by their order number. Order numbers do not have to be contiguous, which lets you leave gaps for steps you expect to insert later:
const tutorial = Tutorial.build({
Step.new(10, { Dialog.new({ Lines = { "Welcome!" } }) }),
Step.new(20, { FocusUI.to(shopButton, { Advance = true }) }),
})
Nothing runs until you call Tutorial:Start.
CAUTION
With Replicate = true this yields until the kit's RemoteEvent has replicated, so call it from
somewhere that can yield.
Errors
| Type | Description |
|---|---|
| "TutorialKit is client only" | Called from the server |
| "TutorialKit.build needs at least one step" | The list was empty |
| "TutorialKit step order N is duplicated" | Two steps share an order number |
Start
lifecycleTutorial:Start(step: number?--
Order number to open on. Defaults to the first step
) → ()Enters the first step and runs its actions.
Passing a step is how you resume: read the saved order from your data store and open there instead of replaying the whole flow.
const saved = DataService:Get(player, "TutorialStep")
tutorial:Start(saved)
GoTo
Tutorial:GoTo(step: number--
Order number of the step to enter
) → boolean--
false when no step carries that order
Jumps to a step, forwards or backwards.
Idempotent: going to the step already open does nothing and returns true, so mirroring the same server message twice is harmless. An unknown order warns and returns false rather than erroring, because a step removed from a newer build should not break an old client.
Advance
Tutorial:Advance() → boolean--
false when the tutorial is not running
Moves to the next step, or completes the tutorial when called on the last one.
Most steps never call this directly. Advance = true on an action is the shorthand, and
ActionContext:Advance is what an action calls when it decides for itself.
Complete
lifecycleTutorial:Complete() → ()Ends the flow early and fires Tutorial.Completed.
Use it for a skip button. The tutorial stays usable afterwards, so Start can run it again.
GetStep
Tutorial:GetStep() → number?--
Order of the open step, or nil when not running
IsActive
Tutorial:IsActive() → boolean--
Whether a step is currently open
Destroy
lifecycleTutorial:Destroy() → ()Tears down everything: the open step, the overlay, the pointer, the camera and the signals.
Call it when the player leaves the tutorial for good. Every method turns into a no-op afterwards, so a late callback cannot resurrect the flow.