TutorialModalController extends DialogueController to create step-by-step tutorial modals with title, subtitle, image, and previous/next navigation.
UIViewComponent → DialogueController → TutorialModalController
using System.Collections.Generic;
using UnityEngine;
using CupkekGames.Luna;
public class GameTutorial : MonoBehaviour
{
[SerializeField] private TutorialModalController _tutorial;
[SerializeField] private List<TutorialStep> _steps;
private int _currentStep = 0;
private void Start()
{
_tutorial.OnNext += NextStep;
_tutorial.OnPrevious += PreviousStep;
ShowStep(0);
}
private void ShowStep(int index)
{
_currentStep = index;
var step = _steps[index];
_tutorial.Continue(
step.Description,
step.Title,
step.Subtitle,
step.Image,
skipCurrent: false
);
// Disable previous on first step
_tutorial.SetEnabledPrevious(index > 0);
// Disable next on last step (or change to "Finish")
_tutorial.SetEnabledNext(index < _steps.Count - 1);
}
private void NextStep()
{
if (_currentStep < _steps.Count - 1)
{
ShowStep(_currentStep + 1);
}
}
private void PreviousStep()
{
if (_currentStep > 0)
{
ShowStep(_currentStep - 1);
}
}
}
[System.Serializable]
public class TutorialStep
{
public string Title;
public string Subtitle;
public string Description;
public Sprite Image;
}Extended version with title, subtitle, and image support.
public bool Continue(
string text,
string title,
string subtitle,
Sprite image,
bool skipCurrent
)| Parameter | Description |
|---|---|
| text | The tutorial text (supports text effects) |
| title | Title displayed at the top |
| subtitle | Subtitle displayed below title |
| image | Image to display (null to hide) |
| skipCurrent | Whether to skip currently playing text |
Set the title and subtitle text directly.
public void SetTitle(string name)
public void SetSubtitle(string name)// Show image with sprite
public void ShowImage(Sprite sprite)
// Hide the image
public void HideImage()// Enable/disable navigation buttons
public void SetEnabledNext(bool enabled)
public void SetEnabledPrevious(bool enabled)Called when return button is clicked or escape is pressed. Disables return buttons and fades out the view.
public void ReturnClicked()| Event | Inherited | Description |
|---|---|---|
| OnTextStart | Yes | Fired when text begins playing |
| OnTextComplete | Yes | Fired when text finishes |
| OnNext | No | Fired when next button is pressed |
| OnPrevious | No | Fired when previous button is pressed |
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:luna="CupkekGames.Luna">
<ui:VisualElement class="tutorial-modal">
<ui:Label name="Title" />
<ui:Label name="Subtitle" />
<ui:VisualElement name="Image" />
<ui:Label name="Speech" />
<ui:VisualElement class="navigation">
<luna:InputPrompt name="PreviousButton" />
<luna:InputPrompt name="ContinueButton" />
</ui:VisualElement>
<ui:Button name="ReturnButton" text="Close" />
</ui:VisualElement>
</ui:UXML>using System.Collections.Generic;
using UnityEngine;
using CupkekGames.Luna;
public class ControlsTutorial : MonoBehaviour
{
[SerializeField] private TutorialModalController _tutorial;
private List<TutorialPage> _pages = new()
{
new TutorialPage {
Title = "Movement",
Subtitle = "Step 1 of 4",
Text = "Use <rainb>WASD</rainb> or the left stick to move your character.",
Image = null // Load from resources
},
new TutorialPage {
Title = "Combat",
Subtitle = "Step 2 of 4",
Text = "Press <shake>Left Click</shake> or RT to attack enemies.",
Image = null
},
new TutorialPage {
Title = "Inventory",
Subtitle = "Step 3 of 4",
Text = "Press <bounce>I</bounce> or Start to open your inventory.",
Image = null
},
new TutorialPage {
Title = "Ready!",
Subtitle = "Step 4 of 4",
Text = "You're all set! Good luck on your adventure!",
Image = null
}
};
private int _currentPage = 0;
private void OnEnable()
{
_tutorial.OnNext += OnNext;
_tutorial.OnPrevious += OnPrevious;
ShowPage(0);
}
private void OnDisable()
{
_tutorial.OnNext -= OnNext;
_tutorial.OnPrevious -= OnPrevious;
}
private void ShowPage(int index)
{
_currentPage = index;
var page = _pages[index];
_tutorial.Continue(page.Text, page.Title, page.Subtitle, page.Image, false);
// Update navigation state
_tutorial.SetEnabledPrevious(index > 0);
_tutorial.SetEnabledNext(index < _pages.Count - 1);
}
private void OnNext()
{
if (_currentPage < _pages.Count - 1)
{
ShowPage(_currentPage + 1);
}
else
{
// Tutorial complete
_tutorial.ReturnClicked();
}
}
private void OnPrevious()
{
if (_currentPage > 0)
{
ShowPage(_currentPage - 1);
}
}
private struct TutorialPage
{
public string Title;
public string Subtitle;
public string Text;
public Sprite Image;
}
}The tutorial uses InputPrompt for navigation buttons, which automatically display the correct input icons based on the current device.
Configure the input action names in the InputPrompt components:
ContinueButton - Next step actionPreviousButton - Previous step actionUIViewActionEscape to close the tutorialFadeOutThenDestroy() for smooth closing animationSettings
Theme
Light
Contrast
Material
Dark
Dim
Material Dark
System
Sidebar(Light & Contrast only)
Font Family
DM Sans
Wix
Inclusive Sans
AR One Sans
Direction