Narrat Scenes
Narrat has a scenes feature allowing games to switch between different layouts and add completely new custom UI elements to narrat. For example, the default built-in scenes are:
engine-splash
: The narrat splash screen on game startgame-splash
: The game's intro screenstart-menu
: The start menu with all the buttonsplaying
: The in-game default scene with the viewport, dialog panel etcchapter-title
: An optional scene to display a simple title + subtitle intro screen
The engine automatically goes through engine-splash -> game-splash -> start-menu -> playing, but games can switch to other scenes, or add new ones.
Using a scene
An example using the chapter-title
built-in scene:
test_change_scenes:
change_scene "chapter-title" next_label "after_change_scene" title "Chapter 1: The Beginning" subtitle "A new adventure begins!"
after_change_scene:
talk helper idle "Hello!"
Scene options
A scene can have any number of options, and when using the change_scene
command those options are given by name. For example, the chapter-title
scene has the following options:
next_label: string;
title: string;
subtitle?: string;
duration?: number;
TIP
Those options are defined in the Vue component's props. For example in the chapter-title
scene component:
const props = defineProps<{
options: {
next_label: string;
title: string;
subtitle?: string;
duration?: number;
};
}>();
Full code for the chapter-title scene component
<template>
<div class="chapter-title-scene">
<h1 class="title chapter-title" v-html="props.options.title"></h1>
<h2
class="subtitle chapter-subtitle"
v-if="props.options.subtitle"
v-html="props.options.subtitle"
></h2>
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import { useScenes } from '@/stores/scenes-store';
import { useVM } from '@/stores/vm-store';
const props = defineProps<{
options: {
next_label: string;
title: string;
subtitle?: string;
duration?: number;
};
}>();
const timeout = ref<any>(null);
function finishedTimeout() {
timeout.value = null;
useScenes().changeScene('playing');
useVM().jumpToLabel(props.options.next_label);
}
onMounted(() => {
timeout.value = setTimeout(finishedTimeout, props.options.duration ?? 2000);
});
onUnmounted(() => {
if (timeout.value) {
clearTimeout(timeout.value);
timeout.value = null;
}
});
</script>
Options are passed by adding new arguments to the change_scene command with the name of the option followed by its value. The full syntax for change_scene
is: change_scene <scene_name> [option name] [option value] [other option name] [other option value] ...
In the case of the chapter-title
scene, the next_label
option decides what narrat label will be played when the intro sequence is finished. So in the example above, we change to the chapter-title
scene, and because we gave after_change_scene
as an option to it, the engine will play the after_change_scene
label when the scene is finished.
Creating custom scenes
Scenes use Vue.js components to give more control to the game developer. To create a custom scene, create a new Vue component and register it with the engine. For example look at the chapter-title scene component.
<template>
<div class="my-scene">
<h1 class="title">My scene</h1>
<h2
class="subtitle"
v-if="props.options.subtitle"
v-html="props.options.subtitle"
></h2>
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import { useScenes } from 'narrat';
const props = defineProps<{
options: {
subtitle?: string; // This is an example, any options could be there, or none
};
}>();
// The timeout allows the scene to automatically change after some time
const timeout = ref<any>(null);
function finishedTimeout() {
timeout.value = null;
// This will make the game switch to the main gameplay scene
useScenes().goToGameScene();
}
onMounted(() => {
// This will call `finishedTimeout` in 2000 milliseconds
timeout.value = setTimeout(finishedTimeout, 2000);
});
onUnmounted(() => {
if (timeout.value) {
clearTimeout(timeout.value);
timeout.value = null;
}
});
</script>
Once you have a vue component you want to use as a scene, register it with the engine in index.ts:
import MySceneComponent from '@/scenes/my-scene.vue';
// ...
window.addEventListener('load', () => {
startApp({
debug,
logging: false,
scripts,
config,
});
useScenes().addNewScene({
id: 'my-scene',
component: shallowRef(GameplayScene),
props: {},
});
});
Once a scene has been registered, it can be used in narrat script via the change_scene
command:
main:
change_scene my-scene
Changing the default scenes
The start-menu
scene is the built-in scene that has the main menu with start/continue etc buttons. The playing
scene is the one when the game is playing.
In some situations, a game might want to inject their own scene to be loaded before those, for example to display a custom splash screen before the start menu.
To do so, the new scenes
option is available in common.yaml
:
scenes:
startMenuScene: my-custom-start-scene # The scene to load instead of the start menu with the "Press start to continue"
gameScene: my-custom-game-scene # The scene to load instead of the normal gameplay scene when starting a game
gameSplashScene: my-custom-splash-scene # The scene to load instead of the splash screen that shows before the start menu
When those options are present, instead of going to the normal game scene, the game will go to the custom scenes defined in the config.
::: warn It is important to go back to the relevant splash screen scenes or start menu after running your custom scene, as they contain important engine initialization logic. If you override any of those scenes, you should make your new scene go to the expected "default" scene afterwards :::
To do so, use the goToGameScene
and goToStartMenuScene
methods from useScenes()
(the sample scene above does this).
For example, in your scene component, the following code examples are good ways to go to various narrat scenes:
import { useScenes, BuiltInScene } from 'narrat';
onMounted(() => {
// To go to the game scene
useScenes().goToGameScene();
// To go to the start menu screen
useScenes().goToStartMenuScreen();
// To go to another built in scene, use the enum that narrat exports
// For example here, go back to the engine splash screen
useScenes().changeScene(BuiltInScene.EngineSplash);
});
:::