{"id":"dontmineatnight/tutorial","name":"tutorial","scope":"dontmineatnight","platform":"roblox","description":"Reusable Roblox onboarding system with 2D and 3D guidance.","version":"0.4.3","latest":"0.4.3","versions":["0.1.0","0.1.1","0.1.2","0.1.3","0.1.4","0.1.5","0.2.0","0.2.1","0.3.0","0.4.1","0.4.2","0.4.3"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{"synttx/scythe":{"version":"^1.2.1","alias":"Scythe"}},"integrity":"d3a4d2268b13f0feb550dbaff38659a874ece59861474897d7dcee95ec858179","likes":0,"downloads":0,"install":"forest install dontmineatnight/tutorial","url":"https://forest.dev/p/roblox/dontmineatnight/tutorial","files":"https://api.forest.dev/ai/package/roblox/dontmineatnight/tutorial/files","readme":"# Tutorial\n\nA small, client-side onboarding package for Roblox experiences.\n\nTutorial presents guidance around UI and world targets while leaving tutorial\ncontent, input, game actions, and progression to the calling experience. It\nprovides:\n\n- **Spotlight guidance**: a screen-space focus, tap indicator, and off-screen\n  arrow for UI or world targets.\n- **Cinematic guidance**: an animated focused hole or letterbox overlay.\n- **3D guidance**: optional highlights, beams, and world markers.\n\nTutorial does not render toast content, capture input, disable movement, or\nadvance steps based on gameplay actions.\n\n## Installation\n\nAdd Tutorial to a Wally project:\n\n```toml\n[dependencies]\nTutorial = \"dontmineatnight/tutorial@0.4.1\"\n```\n\nThen require it from the installed package:\n\n```luau\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal Tutorial = require(ReplicatedStorage.Packages.Tutorial)\n```\n\n## Quick start\n\n```luau\nlocal Players = game:GetService(\"Players\")\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal Tutorial = require(ReplicatedStorage.Packages.Tutorial)\nlocal playerGui = Players.LocalPlayer:WaitForChild(\"PlayerGui\")\n\nlocal tutorial = Tutorial.new({\n\tsteps = {\n\t\t{\n\t\t\tid = \"open-inventory\",\n\t\t\ttitle = \"Open your inventory\",\n\t\t\tdescription = \"Select the inventory button to continue.\",\n\t\t\tpresentation = {\n\t\t\t\tscreenTarget = function()\n\t\t\t\t\tlocal button = playerGui:FindFirstChild(\"InventoryButton\", true)\n\t\t\t\t\tif button and button:IsA(\"GuiObject\") then\n\t\t\t\t\t\treturn button\n\t\t\t\t\tend\n\t\t\t\t\treturn nil\n\t\t\t\tend,\n\t\t\t\tscreenMode = \"focusAndTap\",\n\t\t\t\tinputHints = {\n\t\t\t\t\tkeyboard = \"Press I\",\n\t\t\t\t\tgamepad = \"Press the inventory button\",\n\t\t\t\t\ttouch = \"Tap the inventory button\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\tonStepChanged = function(step, _, _, target)\n\t\t-- Render step.title, step.description, and target in your own UI.\n\tend,\n\tonComplete = function()\n\t\t-- Remove the experience's instruction UI.\n\tend,\n})\n\ntutorial:start()\n\n-- After the experience has confirmed the action:\n-- tutorial:complete()\n```\n\nUse functions for targets that can be created, replaced, or moved while a\nstep is active. Tutorial evaluates those functions during presentation\nupdates and treats invalid or unavailable targets as absent.\n\n## Client API\n\nCreate a controller with `Tutorial.new(options)`.\n\n| Option | Description |\n| --- | --- |\n| `steps` | Required ordered list of step inputs. |\n| `presentation` | A `Tutorial.Presentation` instance, or `false` to render nothing. |\n| `presentationOptions` | Options used to create the default presentation. |\n| `startStepId` | Optional initial step ID. |\n| `onStepChanged` | Receives `(step, index, count, screenTarget)`. |\n| `onStop` | Runs when an active tutorial is stopped or destroyed. |\n| `onComplete` | Receives the last active step. |\n\nController methods:\n\n- `start(stepId?)` starts the current or named step.\n- `stop()` hides guidance without completing the tutorial.\n- `setStep(stepId)` shows a named step.\n- `advance()` moves to the next step, completing at the end.\n- `skip()` completes the tutorial locally.\n- `complete()` hides guidance and invokes `onComplete`.\n- `destroy()` disconnects the controller and destroys owned presentation.\n- `refresh()` updates dynamic targets immediately.\n- `getCurrentStep()` returns `(step, index)` while running.\n- `isRunning()` and `isCompleted()` expose controller state.\n- `getPresentation()` returns the presentation when one is enabled.\n\nA presentation provides `show`, `hide`, `refresh`, `getScreenTarget`, and\n`destroy`. The `screenTarget` passed to `onStepChanged` is the target resolved\nwhen that step was shown; call `getScreenTarget()` after `refresh()` for the\ncurrent target.\n\nA controller owns the default presentation created from `presentationOptions`.\nIf an existing presentation is supplied, the caller owns it; controller\ndestruction hides it but does not destroy it.\n\n`advance`, `skip`, and `complete` are local operations. For a\nserver-authoritative tutorial, let the experience call `setStep` or\n`complete` only after its own server confirmation.\n\n## Step presentation\n\nTutorial passes `title`, `description`, and `inputHints` to the experience but\nonly uses target fields for visual presentation.\n\n```luau\npresentation = {\n\tscreenTarget = GuiObject or function() -> GuiObject?,\n\tscreenMode = \"focus\" | \"tap\" | \"focusAndTap\" | \"letterbox\" | \"none\",\n\tworldTarget = BasePart | Model | Vector3 or function() -> target?,\n\tworldOrigin = BasePart | Model | Vector3 or function() -> origin?,\n\tworldMode = \"beam\" | \"highlight\" | \"marker\" | \"all\" | \"none\",\n\tinputHints = {\n\t\tkeyboard = \"...\",\n\t\tgamepad = \"...\",\n\t\ttouch = \"...\",\n\t},\n}\n```\n\nIf `screenTarget` is omitted and `worldTarget` is supplied, spotlight guidance\nprojects the world target into screen space. It follows the target and shows an\nedge arrow while the target is outside the viewport.\n\n`screenMode = \"letterbox\"` shows the screen presentation's top and bottom bars\nand does not require a target. The bar fraction and dim transparency are\nconfigured with `ScreenGuidanceOptions`.\n\nWorld presentation can use a `BillboardGui` or `Model` as `markerTemplate`. For\nmodel markers, `markerOrientation`, `markerDistance`, and `markerTargetOffset`\ncontrol placement and orientation. `markerTargetPadding` reserves space from\nthe target when `markerDistance` or `markerOrientation` is also supplied.\n\n## Reusing visual assets\n\nTemplates are optional and are cloned by Tutorial:\n\n```luau\nlocal presentation = Tutorial.Presentation.new({\n\tscreen = {\n\t\tfocusTemplate = ReplicatedStorage.Assets.TutorialFocus,\n\t\ttapTemplate = ReplicatedStorage.Assets.TutorialTap,\n\t\tedgeArrowTemplate = ReplicatedStorage.Assets.TutorialArrow,\n\t},\n\tworld = {\n\t\tbeamTemplate = ReplicatedStorage.Assets.TutorialBeam,\n\t\thighlightTemplate = ReplicatedStorage.Assets.TutorialHighlight,\n\t\tmarkerTemplate = ReplicatedStorage.Assets.TutorialMarker,\n\t},\n})\n```\n\nTemplates provide the visual contents and structure. Tutorial owns the\npresentation properties needed to place, animate, and control the clones. For\nexample, screen templates receive Tutorial's position, size, anchor, z-index,\nand input settings; BillboardGui markers receive Tutorial's size, offset,\n`AlwaysOnTop`, and enabled state; and Highlights receive Tutorial's adornee,\ndepth, colors, transparencies, and enabled state. The original template\ninstances are never modified.\n\n## Cinematic overlay\n\n`Tutorial.CinematicOverlay` is a separate shared overlay for explicit\ncinematic transitions:\n\n```luau\nlocal overlay = Tutorial.CinematicOverlay\n\noverlay.ShowLetterbox({\n\tbarFraction = 0.12,\n\ttransparency = 0.35,\n})\noverlay.Focus(targetGui, {\n\tscaleFactor = 1,\n\ttransparency = 0.35,\n})\noverlay.Hide()\noverlay.Destroy()\n```\n\n`Focus` follows a target while it remains in the local player's `PlayerGui` and\nits rendered ancestors remain visible. `ShowLetterbox` creates animated top\nand bottom bars. Both transitions use short Sine/InOut animations; their render\nconnections and completed tweens are released automatically. Call `Destroy`\nwhen the experience no longer needs the shared overlay.\n\n## License\n\nMIT\n","readmeTruncated":false}