Skip to content

Tutorials · Crypt Knight · Chapter 15

Screens

75 minutes Uses: Canvas, Rect, Panel, Text, Button, Focus, Shortcut, AudioSource, pressed, change_scene, quit, pause, resume, play_sound, stop_music, keep_number, hide, show

By the end

  • The menu’s actions and Esc, in the input map
  • A title scene the game opens on, with Play and Quit, and the castle loop already playing
  • A pause menu, Resume and Title, in all three levels, that stops the game and dims it
  • A victory screen that the Levels list opens after the last level
  • A game that is played from the title to the victory screen and back
On this page

Three screens

The game now plays from the Gate to the beast, and it opens on the Gate, and ends where the beast dies. Around that it wants a title to open on, a pause menu, and somewhere to go when the beast is slain. This chapter makes the three, in a lean shell: a title with Play and Quit, a pause menu with Resume and Title, and a victory screen with Title. There are no settings and no saved progress.

It goes quickly where Ship Evader’s tutorial has already gone slowly. A screen is a canvas with Panels, Labels and Buttons on it, a button reports an action that a script hears with pressed, and the keyboard moves a focus between buttons. Ship Evader’s screens and its pause and settings explain each of them; this chapter builds the same things, with the values, and says why where it differs.

The menu’s keys

A menu answers eight actions, if a project has them: ui_up and ui_down move the focus, ui_left and ui_right move a slider, ui_next and ui_previous step the focus on and back as Tab and Shift+Tab do, ui_accept presses the lit button and ui_cancel goes back. This game binds neither ui_next nor ui_previous. The arrow keys, Tab, Enter and Space work in every menu anyway; the six it does bind add W, A, S, D and Esc. The pause menu adds a ninth action, pause, on Esc.

  1. Choose View → Input map…, and add seven actions: pause, ui_up, ui_down, ui_left, ui_right, ui_accept and ui_cancel.
  2. Select each action, and give it its keys with Choose key…, typing to find each: pause Esc; ui_up W and Up; ui_down S and Down; ui_left A and Left; ui_right D and Right; ui_accept Enter and Space; and ui_cancel Esc.
  3. Press Save, and close the window.
The Input window with eleven actions: move, jump, down, attack, pause and the six ui actions, with the actions that share keys marked in red and lines under the list such as move and ui_up share W

Record a control on a key another action already uses stops with a warning, such as “W already runs move.”, and two choices. Choose key… binds it anyway, which is what a menu needs. The window then marks the actions that share keys in red: here that is meant, and harmless. The menus run on the title, which has no knight to move, and the pause menu runs only while the game is paused, so nothing runs move when W is pressed to move a light.

The title

The title is a scene of its own, the one the game opens on. It plays the castle loop, so that the game’s music is already going when Play opens the Gate: a scene that asks for the track that is playing leaves it playing.

  1. Press Ctrl+S to save the open scene. Click the Viewport tab, and choose File → New scene. An empty scene takes the place of the one that was open, without asking to save it.
  2. Press + Add node, choose Node, and rename it Title.
  3. Add an AudioSourceComponent: Clip audio/abandoned_castle_music_-_starninjas_6.ogg, Bus Music, Volume 1, Looping and Autoplay ticked, and Positional unticked in its Spatial fold.
  4. Press Ctrl+S, and save the scene in the scenes folder as title.ron. The editor saves no scene without a node in it, which is why this comes after Title.

The title shows the night town behind it, as the Gate does, and the Gate’s own camera.

  1. Open scenes/gate.ron. In the Hierarchy, select camera, then Ctrl-click sky, mountains, forest and town, and press Ctrl+C.
  2. Open scenes/title.ron, right-click Title, and choose Paste as child. The five come over with their components.
  3. Select the title’s camera. Remove its CameraRigComponent and its Shake with the bin at the right of each header, and the group camera with the × beside it: there is no knight to follow and nothing to shake. Set its Position to 160 and 90, the middle of the view.

Then the words and the buttons. A screen is a canvas, as the HUD is, and everything on it is anchored to it.

  1. Select Title, press + Add node, choose Node, and rename it screen. Give it a CanvasComponent: Reference Size 320 and 180, Scale Mode ScaleWithHeight, Use Safe Area ticked, Space Screen, Layer 0.
  2. Under screen, press + Add node and choose Label. Rename it logo. Its Rect: Anchor Min and Anchor Max 0.5 and 0, the top centre; Offset Min -120 and 30; Offset Max 120 and 60; Pivot 0.5 and 0.5. Its Text: CRYPT KNIGHT, Scale 3, Align Center, and Color 1, 0.94, 0.8, alpha 1, a warm white.
  3. Add a Node under screen called menu, and give it a RectComponent: as it comes, it fills the canvas.
  4. Give menu an AudioSourceComponent on Sfx, Volume 1, Positional unticked, with two triggers: move, Clip audio/select_001.ogg; and press, Clip audio/confirmation_001.ogg.

The screens use the engine’s own letters, the plain face the signs use: no font is imported, so the title’s logo is the same face at three times the size.

Two buttons

  1. Select menu, press + Add node, and choose Button: a Rect, a Panel, a Text and a Button on one node. Rename it play.
  2. Its Rect: Anchor Min and Anchor Max 0.5 and 0; Offset Min -40 and 96; Offset Max 40 and 112; Pivot 0.5 and 0.5. That is 80 by 16, at the middle of the screen.
  3. In its Panel, click Color. If the button at the top left of the picker reads U8, press it, so it reads F. Make the Panel white: 1, 1, 1, alpha 1. Leave its Texture empty.
  4. Its Text: Play, Scale 1, Align Center, and Color 1, 0.94, 0.8, alpha 1.
  5. Its Button: Action play; Normal 0.012, 0.01, 0.016, alpha 1; Hovered 0.6, 0.02, 0.06, alpha 1; Pressed 0.3, 0.01, 0.03, alpha 1.
  6. Right-click play and choose Duplicate. Rename the copy quit: Offset Min -40 and 118, Offset Max 40 and 134, Text Quit, Action quit.
  7. Give play and quit each a FocusComponent, then set play’s Up to quit and quit’s Down to play: the list a Focus link opens offers only nodes that have a FocusComponent of their own, so both get theirs first.
The play button selected, its Inspector narrowed to its Button: Action play, Disabled unticked, Normal a near-black, Hovered a deep red and Pressed a darker red

When the screen opens, the first button, Play, takes the focus. Down moves it to Quit, and Up from Play goes round to Quit, through the Focus links. The title’s script plays a tick as the focus moves, and does what Play and Quit say.

  1. In Assets, press Create, choose New script, type title_menu, and press Enter.
  2. In the Code tab’s Scripts list, click title_menu.rgs, replace everything in it with the script below, and press Ctrl+S.
rgs title_menu.rgs
/// The title's menu: a sound as the focus moves; Play, which starts a new game
/// at The Gate with neither relic; and Quit.
component TitleMenu {
    fn loop() {
        // W and S, and the arrows: the input map's ui_up and ui_down, which
        // move the focus.
        if pressed("ui_up") || pressed("ui_down") {
            play_sound("move");
        }
        if pressed("play") {
            // A sound asked for on the step that changes scene plays on after
            // it.
            play_sound("press");
            // Kept values outlast a change of scene, so a new game sets its
            // own: no relic taken yet.
            keep_number("double_jump", 0.0);
            keep_number("sword", 0.0);
            change_scene("gate");
        }
        if pressed("quit") {
            // No sound: the game closes at the end of this step.
            quit();
        }
    }
}
  1. Open scenes/title.ron, click the Viewport tab, select menu, press Add component, and choose TitleMenu.
  2. Press Ctrl+S.
The title scene in the editor: the Hierarchy lists Title with its camera, the four town layers and the screen, which holds logo and menu, and menu holds play and quit; the Viewport looks through the camera at CRYPT KNIGHT over the night town with Play and Quit buttons; and Title is selected, its Audio Source set to the castle loop on the Music bus, Looping and Autoplay ticked
pressed ("play")
A Button reports its Action when it is pressed, by a click, by Enter or Space while it has the focus, and a script hears it with pressed, as if a key bound to that action had gone down.
A new game
Kept values outlast a change of scene, so a game started again from the title would still have the relics of the last. Play keeps both at 0 before it changes scene, so a new game begins with none.
change_scene and quit
change_scene("gate") opens the scene whose file is gate.ron, by its name without the extension. A sound asked for on the same step plays on after it. quit() closes an exported game at the end of the step, and stops Play in the editor, with no sound: the window is about to close.

The game opens on its title

  1. Choose File → Project settings…, open its General tab, and set Startup scene to scenes/title.ron, then close the window.
Project settings on its General tab, with Startup scene “scenes/title.ron”

The Levels list is for the levels, and it does not need the title: the title is not one of them. The editor’s Play plays the scene that is open, so from now on open scenes/title.ron before you press F5 to play the whole game. Build → Run / Stop game starts it on the startup scene, as an exported game does.

The pause menu

Esc pauses the game: everything that runs with the game stops where it is, and the pause menu, which runs only while the game is paused, takes over. It sits on the HUD, in every level. Two scripts: the first listens for Esc, and sits on the hud, which runs with the game.

  1. In Assets, press Create, choose New script, type pause_key, and press Enter. In the Code tab, replace everything in pause_key.rgs with the script below, and press Ctrl+S.
rgs pause_key.rgs
/// Esc, which the input map calls pause, pauses the game: everything that runs
/// with the game stops where it is, and the pause menu, which runs only while
/// the game is paused, takes over. It sits on the HUD, which runs with the
/// game.
component PauseKey {
    fn loop() {
        if pressed("pause") {
            pause();
        }
    }
}

pause pauses the scene as the step ends. Whether a node runs while it is paused is its Process Mode: Inherit, as its parent does, the default; Pausable; WhenPaused, only while paused; or Always. Every node in the game so far keeps the default, so all of it is pausable, and all of it stops.

The second runs the menu, and is on a node that is hidden, and runs only while paused:

  1. In Assets, press Create, choose New script, type pause_menu, and press Enter. In the Code tab, replace everything in pause_menu.rgs with the script below, and press Ctrl+S.
rgs pause_menu.rgs
/// The pause menu, over the level. It runs only while the game is paused, so
/// it shows itself on the first step of each pause, and it hides itself on
/// the step it resumes the game: once the game runs again its hooks stop, and
/// a menu left showing would sit over the level.
component PauseMenu {
    /// Whether it is showing, so it shows itself once a pause, not on every
    /// step.
    #[hidden]
    open: bool = false,

    fn loop() {
        if !open {
            show();
            open = true;
        }
        if pressed("ui_up") || pressed("ui_down") {
            play_sound("move");
        }
        if pressed("resume") {
            play_sound("press");
            hide();
            open = false;
            resume();
        }
        if pressed("title") {
            // A change of scene ends the pause too.
            play_sound("press");
            change_scene("title");
        }
    }
}
  1. Double-click scenes/gate.ron in Assets, and click the Viewport tab. Select hud, press Add component, and choose PauseKey.
  2. Select hud, press + Add node, choose Node, and rename it pause_menu. Untick its Visible. Open its Advanced fold and set Process Mode to WhenPaused.
  3. Give it a RectComponent, which fills the canvas; an AudioSourceComponent on Sfx with Positional unticked and the title menu’s two triggers, move and press; and PauseMenu.
The pause_menu node selected, its Node section: Visible unticked, and under Advanced, Process Mode set to WhenPaused

Under it, four children: a dimmer, which darkens the level behind the menu so that the level, the HUD and the signs do not compete with it, then the words, then the two buttons. They are in this order, the dimmer first, so that it is drawn behind the rest.

  1. Under pause_menu, add a Panel called dimmer. Its Rect: Anchor Min 0 and 0, Anchor Max 1 and 1, Offset Min and Offset Max 0 and 0: the whole canvas. Its Panel Color: 0, 0, 0, alpha 0.6.
  2. Add a Label called paused. Its Rect: Anchor Min and Max 0.5 and 0; Offset Min -80 and 52; Offset Max 80 and 76; Pivot 0.5 and 0.5. Its Text: PAUSED, Scale 2, Align Center, Color 1, 0.94, 0.8, alpha 1.
  3. Add a Menu Button called resume: a Button with a Shortcut. Make it as the title’s buttons: Offset Min -40 and 90, Offset Max 40 and 106, a white Panel, its Text Resume, and a Button with Action resume and the same three tints. Give it a FocusComponent, and in its Shortcut set Action to pause, leaving Key empty: Esc resumes too, and Resume draws Esc inside its right edge.
  4. Add a Button called title, made the same way: Offset Min -40 and 112, Offset Max 40 and 128, Text Title, Action title, and a FocusComponent.
  5. Set resume’s Up to title, and title’s Down to resume.
  6. Press Ctrl+S.

The Crypt and the Hall each need the same menu on their own HUD. Copy it: with pause_menu selected in gate.ron, press Ctrl+C. Then, in scenes/crypt.ron, right-click hud and choose Paste as child, select hud, press Add component and choose PauseKey, and press Ctrl+S. Do the same in scenes/hall.ron.

Hidden, and shown by its own script
The menu’s hooks run only while the game is paused. PauseMenu shows the node on the first step of each pause, and on the step it resumes the game, hides it: once the game runs again its hooks stop, and a menu left showing would sit over the level.
resume() and the title
resume() ends the pause. The title’s button changes scene, which ends the pause too: a change of scene ends a pause.
What a pause holds
Everything that moves: bodies, every hook, wait and anneal, particles, animated sprites and the game’s sounds, which carry on from where they stopped. What plays on: the music, the menu’s own sounds. What works: kept values and the keys.

The victory screen

When the beast is slain, the Hall’s complete() is the last level’s, and the Levels list opens whatever its Then open names. That is the victory screen: the title, copied and changed.

  1. In Assets, open scenes, right-click title.ron and choose Duplicate. Right-click the copy, choose Rename, type victory.ron, and press Enter. Double-click it to open it.
  2. Rename its root Victory, and remove its AudioSourceComponent: the screen plays the jingle, not the loop.
  3. Select logo, and set its Text to The beast is slain. and Scale to 2.
  4. Select quit and press Delete. Select play, and rename it title. Set its Text to Title and its Button’s Action to title, and clear Up and Down in its Focus.
  5. Select menu. Add a third trigger to its AudioSourceComponent: Name jingle, Clip audio/jingles_STEEL07.ogg. Remove TitleMenu.
  1. In Assets, press Create, choose New script, type victory, and press Enter. In the Code tab, replace everything in victory.rgs with the script below, and press Ctrl+S.
rgs victory.rgs
/// The victory screen's menu: the music stops and the jingle plays as the
/// screen opens, and its one button, Title, goes back to the title.
component Victory {
    fn on_create() {
        // The music plays on through a change of scene, on its one channel,
        // so it is stopped here, and the jingle is heard alone.
        stop_music();
        play_sound("jingle");
    }

    fn loop() {
        if pressed("title") {
            play_sound("press");
            change_scene("title");
        }
    }
}
  1. Click the Viewport tab, select menu, press Add component, and choose Victory.
  2. Press Ctrl+S.
  3. Click the Levels tab. Drag scenes/victory.ron from Assets onto the Then open slot under the list.
The Levels panel with three levels in order, The Gate, The Crypt and The Beast’s Hall, each with the castle loop as its Music, and Then open: scenes/victory.ron under the list

The music plays on through a change of scene, on its one channel, so stop_music() stops the castle loop and the boss loop, and the jingle is heard alone. After the jingle there is only the title to go back to.

Check it works

The title as the game opens: CRYPT KNIGHT in large pale letters over the night town, and two buttons under it, Play lit red and Quit near-black
The title after one tap of S: Quit lit red and Play near-black
The Gate paused: the level dimmed under PAUSED, a lit Resume, with Esc at its right edge, and a Title button over it, the knight and the first sign still, and the five hearts held at the top left
The victory screen: The beast is slain. over the night town, and one lit button, Title