TutorialModalController extends DialogueController into a step-by-step tutorial modal with title, subtitle, image, and prev/next navigation. Pairs naturally with InputPrompt for gamepad-friendly controls.
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 a return button is clicked. Disables the return buttons and pops the view off the nav back stack (LunaNavigation.PopBackStack()).
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 actionInputEscapeManager via the nav back stack — popping the view runs the same disable-buttons + fade-out path as the in-UI return buttonReturnClicked()ReturnClicked() calls LunaNavigation.PopBackStack(), which fades the view outTutorialModalExample.cs shows this view wired into a real shellSettings
Theme
Light
Contrast
Material
Dark
Dim
Material Dark
System
Sidebar(Light & Contrast only)
Font Family
DM Sans
Wix
Inclusive Sans
AR One Sans
Direction