Tutorial System
Build in-game tutorials as ScriptableObject-based steps. Designers can change the flow without hardcoding, and the runtime supports skipping, replaying, saving, and loading completion state.
Structure
TutorialSystem - Pure C# instance. Saves and restores completed tutorials.
└── TutorialRunner - MonoBehaviour that runs tutorial steps as coroutines.
TutorialSequence - ScriptableObject. Step list and skip settings.
└── TutorialStep[] - ScriptableObject base class for concrete tutorial steps.
Built-In Steps
| Step | Description |
|---|---|
HighlightButtonStep | Highlights a UI object and optionally waits for an action key |
WaitForActionStep | Waits until a named player action is triggered |
DialogueStep | Shows a message panel, then auto-advances or waits for click input |
DelayStep | Waits for a fixed number of seconds |
Quick Start
1. Add TutorialRunner to the Scene
// Only TutorialRunner needs to live on a GameObject because it runs coroutines.
// TutorialSystem is created from code.
2. Create a TutorialSequence Asset
Project window -> Create -> AchUtils/Tutorial/Tutorial Sequence
Configure the steps in the inspector:
SequenceId : "FirstGacha"
CanSkip : true
Steps:
[0] HighlightButtonStep - Highlight GachaButton, wait for "GachaClick"
[1] DialogueStep - "You obtained a character!"
[2] HighlightButtonStep - Highlight EquipButton
3. Start the Tutorial
[SerializeField] TutorialRunner tutorialRunner;
[SerializeField] TutorialSequence gachaSequence;
private TutorialSystem tutorialSystem;
void Awake()
{
tutorialSystem = new TutorialSystem(tutorialRunner);
}
void OnDestroy()
{
tutorialSystem.Dispose();
}
// Completed tutorials are skipped automatically.
tutorialSystem.StartTutorial(gachaSequence);
// Force replay, ignoring completion state.
tutorialSystem.StartTutorialForce(gachaSequence);
4. Trigger Player Actions
Call TriggerAction from button handlers or gameplay events.
public void OnGachaButtonClicked()
{
tutorialSystem.TriggerAction("GachaClick");
// Continue normal game logic...
}
API
TutorialSystem
TutorialSystem(TutorialRunner runner, bool loadOnCreate = true)
void StartTutorial(TutorialSequence sequence)
void StartTutorialForce(TutorialSequence sequence)
void Skip()
void TriggerAction(string key)
bool IsCompleted(string sequenceId)
void Save()
void Load()
void ResetAll()
void Dispose()
TutorialRunner
bool IsRunning
int CurrentStepIndex
event Action OnCompleted
event Action OnSkipped
event Action<int> OnStepChanged
Custom Steps
Inherit from TutorialStep and implement Execute.
[CreateAssetMenu(menuName = "AchUtils/Tutorial/Steps/My Step")]
public class MyCustomStep : TutorialStep
{
public GameObject TargetPanel;
public override IEnumerator Execute(TutorialRunner runner)
{
TargetPanel.SetActive(true);
while (!runner.HasTriggeredAction("PanelClosed"))
yield return null;
runner.ConsumeAction("PanelClosed");
TargetPanel.SetActive(false);
}
}
Save Format
Completed SequenceId values are saved to PlayerPrefs as a comma-separated string.
AchUtils_Tutorial_Completed = "FirstGacha,FirstEquip,FirstDungeon"
::: tip
If SequenceId is empty, completion is not saved. You can also disable saving with SaveProgress = false.
:::
Flow Example
Start
↓
HighlightButtonStep "Click the gacha button"
↓
WaitForActionStep "GachaClick"
↓
DialogueStep "You obtained a character!"
↓
HighlightButtonStep "Open the equipment screen"
↓
End -> MarkCompleted("FirstGacha")