Skip to content

Tutorials · Interfaces · Chapter 6

Settings

80 minutes Uses: Slider, Toggle, Dropdown, FocusRing, Focus, Button, Shortcut, WorldText, AudioSource, amount, pressed, save_number, saved_number, set_flag, set_number, set_point, set_music_volume, set_sfx_volume, play_sound, change_scene, show, hide, vec2

By the end

  • scenes/settings.ron: music and effects sliders, Show hints and Item names tick boxes, a Text size list, Back
  • Every choice saved with save_number and applied the next time the game opens
  • A Focus Ring round the focused control
On this page

A settings screen

The settings screen is a scene of its own, which the title’s Settings button opens. It holds five rows, each a control with its name beside it, and a Back button. Here it is as it opens on a first launch, before the player has chosen anything: the defaults you are about to set.

The settings screen on a first launch: SETTINGS in gold over a brown frame holding a MUSIC slider and an EFFECTS slider, both filled to four fifths, a SHOW HINTS box ticked, an empty ITEM NAMES box, a TEXT SIZE list reading NORMAL, and a BACK button with ESC at its right edge
A first launch: the defaults.
  1. Copy the seven files below into your project, each to where the list says: the pictures into art, and the music into audio. Then press Refresh in the Assets panel.

The screen starts as a copy of the title, which already has the camera, the palette and the name.

  1. Open scenes/title.ron and choose File → Save as…. Save it as scenes/settings.ron. The editor now holds the copy.
  2. In the Hierarchy, rename Title to Settings, title_canvas to settings_canvas and title_name to settings_name. Select title_fade and press Delete: the menu stays on the title.
  3. Select settings_name. In its Rect, set Anchor Min and Anchor Max both to 0.5, 0.5, Offset Min to -360, -256, Offset Max to 360, -176 and Pivot to 0.5, 1.
  4. In its Text, set Text to Settings, Scale to 1.5, Outline Width to 2 and Shadow Offset to 3, 3. Its Align stays Center, its outline and shadow colours as they are, and its role Accent.
  5. Select settings_canvas, click + Add node and choose Panel. Rename it settings_frame.
  6. In its Rect, set Anchor Min and Anchor Max both to 0.5, 0.5, Offset Min to -270, -160 and Offset Max to 270, 240. Pivot stays 0.5, 0.5. In its Panel, set Texture to panel_brown.png and Border to 8.
  7. Select settings_frame, click + Add node and choose Stack. Rename it settings_rows.
  8. In its Rect, set Anchor Max to 1, 0, Offset Min to 24, 24, Offset Max to -24, 312 and Pivot to 0.5, 0. Anchor Min stays 0, 0. In its Stack, set Fit to Fixed and Spacing to 12. Item stays 48, which makes five rows of 48 and four gaps of 12: 288, the Stack’s height.
  9. Select settings_rows, click + Add node and choose Node. Rename it music_row. Add four more Nodes under settings_rows, in this order: effects_row, hints_row, names_row and size_row. Each is a row 492 wide and 48 high, and each holds one control.

Two changes to the title make it open this screen, and give it its music.

  1. Open scenes/title.ron. Select settings and untick Disabled in its Button.
  2. Select Title, click Add component and choose AudioSourceComponent. Set Clip to abandoned_castle_music_-_starninjas_6.ogg and Bus to Music. Tick Looping and Autoplay, and untick Positional.

Two sliders

The first two rows are sliders. A slider is chapter 2’s health bar with Interactive ticked, so the pointer and the keys can move it, and with an Action to say so.

  1. Open scenes/settings.ron. Select music_row, click + Add node and choose Slider. Rename it music.
  2. In its Rect, set Anchor Min and Anchor Max both to 0, 0.5, Offset Min to 244, -8, Offset Max to 484, 8 and Pivot to 0, 0.5. That is a bar 240 wide and 16 high, 244 in from the row’s left edge and centred up and down.
  3. In its Slider, set Action to music, Value to 0.8 and Step to 0.1. Min stays 0, Max 1 and Interactive ticked. Under Textures, set Fill Texture to slide_horizontal_color.png, Track Texture to slide_horizontal_grey.png and Border to 8. Fill and Track come from Apply theme.
  4. Click Add component and choose FocusComponent. Its links come with the ring, below.
  5. Select music, click + Add node and choose Label. Rename it music_label.
  6. In its Rect, set Anchor Min and Anchor Max both to 0, 0.5, Offset Min to -244, -24, Offset Max to -24, 24 and Pivot to 0, 0.5. It is a child of the slider and hangs to its left, the row’s label, 220 wide. In its Text, set Text to Music and Scale to 0.75.
  7. Select music and press Ctrl+C. Right-click effects_row and choose Paste as child. Rename the copy effects, and its child effects_label. Set effects’s Action to effects and effects_label’s Text to Effects.
music selected, its Inspector narrowed with the search box, slider, to its Slider: Action music, Value 0.80, Min 0.00, Max 1.00, Step 0.10, Interactive ticked, Fill and Track white, and under Textures Fill Texture and Track Texture slide_horizontal pictures and Border 8.00; the Viewport shows the SETTINGS screen, with MUSIC and EFFECTS sliders, SHOW HINTS and ITEM NAMES boxes, a TEXT SIZE list and BACK, and the music slider outlined as the selection
Action
A Slider’s Action: the action it fires as it moves. A script hears it with pressed("music"), and reads where the slider stands with amount("music"), in the slider’s own range, 0 to 1 here.
Step
The smallest amount a Slider’s value can change by, and how far one arrow press moves it. 0.1 makes ten notches from one end to the other, and Left and Right move one. 0 allows any value, and the arrows then move it a twentieth of its range. A new Slider’s is 0.

The Slider’s Value is where it opens, 0.8, which is also the volume a player who has never chosen one gets. Its other rows are chapter 2’s: Interactive ticked is what chapter 2 left unticked for the health bar.

The music volume

  1. In Assets, click Assets in the path bar over the tree, so the script is written at the project’s top, then press Create, choose New script, type music_volume, and press Enter.
  2. In the Code tab, replace everything in music_volume.rgs with the script below, and press Ctrl+S.
rgs music_volume.rgs
/// The music slider, on the settings screen: it opens where the saved music
/// volume is, and each move the player makes sets the music's volume and
/// saves it.
component MusicVolume {
    fn on_create() {
        // Writing a slider moves it and presses nothing, so the screen opens
        // on the saved volume and saves nothing. 0.8 until the player has
        // chosen one.
        set_number("SliderComponent", "value", saved_number("music_volume", 0.8));
    }

    fn loop() {
        // A drag, a click on the bar, or Left and Right while it holds the
        // focus moves it, and presses music on that step; amount() answers
        // where it now is.
        if pressed("music") {
            let level = amount("music");
            set_music_volume(level);
            save_number("music_volume", level);
        }
    }
}
  1. Select music, click Add component and choose MusicVolume.

pressed("music") is true on every step the slider’s value changes: a drag, a click on the bar, or Left and Right, or A and D, while it holds the focus. amount("music") is where it now stands, and it keeps that value between presses.

On create, the script sets the slider to the saved music volume, 0.8 until the player has chosen one. A script that writes a slider moves it and presses nothing, so opening the screen changes no volume and saves nothing. When the player moves it, the script sets the music bus to the new level and saves it for the next launch.

The Code panel with music_volume.rgs open: a comment on the music slider, then component MusicVolume with an on_create that sets the slider’s value from saved_number music_volume with 0.8, and a loop that, when pressed music, sets the music volume from amount music and saves it; the Scripts list at the left names the project’s scripts
set_number and saved_number
set_number("SliderComponent", "value", …) writes a number onto another component of this node, here the slider’s value. saved_number("music_volume", 0.8) reads a number saved on disk, or the second number if none was ever saved.
amount, set_music_volume and save_number
amount("music") is the slider’s value. set_music_volume(level) sets the music bus, 0 to 1. save_number("music_volume", level) writes the number to disk, where it lasts from one launch to the next.

The effects volume

The effects slider does the same for the effects bus, and plays a click at the new volume, so the player hears how loud the game will be.

  1. In Assets, press Create, choose New script, type effects_volume, and press Enter.
  2. In the Code tab, replace everything in effects_volume.rgs with the script below, and press Ctrl+S.
rgs effects_volume.rgs
/// The effects slider, on the settings screen: it opens where the saved
/// effects volume is, and each move the player makes sets the effects' volume,
/// saves it, and plays a click at it, so the player hears how loud the game
/// will be. While it is dragged, the click plays no more than once every
/// sample_gap.
component EffectsVolume {
    /// The shortest time between two clicks while the slider moves.
    #[suffix("s")]
    sample_gap: f32 = 0.15,
    /// Seconds until the click may sound again.
    #[hidden]
    quiet: f32 = 0.0,

    fn on_create() {
        // Writing a slider moves it and presses nothing, so the screen opens
        // on the saved volume without a sound. 0.8 until the player has
        // chosen one.
        set_number("SliderComponent", "value", saved_number("effects_volume", 0.8));
    }

    fn loop(dt: f32) {
        // Counted here rather than waited out: a hook that waits hears
        // nothing, and the moves made meanwhile would go unset and unsaved.
        if quiet > 0.0 {
            quiet -= dt;
        }
        // A drag, a click on the bar, or Left and Right while it holds the
        // focus moves it, and presses effects on that step.
        if pressed("effects") {
            let level = amount("effects");
            set_sfx_volume(level);
            save_number("effects_volume", level);
            if quiet <= 0.0 {
                play_sound("click");
                quiet = sample_gap;
            }
        }
    }
}
  1. Select effects, click Add component and choose EffectsVolume. Its Sample Gap starts at the script’s own 0.15, and its Quiet is hidden at 0: leave both.
  2. Click Add component again and choose AudioSourceComponent. Untick Positional. In its Triggers, press the + beside it once, and set the entry’s Name to click and its Clip to click_001.ogg.

pressed("effects") is true on every step the effects slider’s value changes, as the music’s is. A drag moves it on many steps a second, so the script counts a gap of 0.15 seconds between clicks and plays none in between, or the sound would be a buzz. play_sound and set_sfx_volume are the effects’ side of set_music_volume.

Tick boxes

A tick box is a Toggle: a box that flips between two states when it is pressed, and remembers which. Its box is a Panel, and its tick is the Panel’s other picture. There are two, Show hints and Item names, and each decides something in the delve.

  1. Select hints_row, click + Add node and choose Toggle. Rename it show_hints.
  2. Remove its Text: press the bin at the right of its TextComponent header. The box has no word of its own; its name sits beside it.
  3. In its Rect, set Anchor Min and Anchor Max both to 0, 0.5, Offset Min to 244, -16, Offset Max to 276, 16 and Pivot to 0, 0.5. That is a box 32 wide and 32 high.
  4. In its Panel, set Texture to check_square_grey.png. Border stays 0.
  5. In its Toggle, set Action to show_hints, tick On, and set On Texture to check_square_grey_checkmark.png. The colours come from Apply theme.
  6. Click Add component and choose FocusComponent.
  7. Select show_hints, click + Add node and choose Label. Rename it show_hints_label. Give its Rect and its Text the values of music_label, with Text Show hints.
  8. Select show_hints and press Ctrl+C. Right-click names_row and choose Paste as child. Rename the copy item_names and its child item_names_label. Set item_names’s Action to item_names and untick its On, and set item_names_label’s Text to Item names.
show_hints selected, its Inspector narrowed with the search box, toggle, to its TickBox, Setting show_hints, and its Toggle: Action show_hints, On ticked, Disabled unticked, Normal and Hovered white, Pressed light grey, Check a dark plum, Check Inset 6.00 and On Texture check_square_grey_checkmark.png; the Viewport shows the SETTINGS screen with the SHOW HINTS box outlined
Action
A Toggle’s Action: the action it presses on the step it flips, here show_hints. A script reads amount("show_hints"), which is 1 while it is ticked and 0 while it is not, from then on.
On
Whether the box is ticked. It is saved with the scene, unlike a Button’s pressed look. show_hints opens ticked, and item_names not, as a player who has chosen nothing finds them.
Normal, Hovered, Pressed
A Toggle’s tints for its box, as a Button’s are: at rest, under the pointer or with the focus, and while held down. Apply theme writes them from the palette’s button colours: 255, 255, 255 at rest and lit, and 211, 211, 211 held.
Check
The colour of the mark drawn inside the box when it is on. Apply theme writes the palette’s Button Text, the plum 63, 38, 49. A Toggle that has an On Texture draws that and no mark, so here Check is set and not seen.
On Texture
The picture drawn instead of the Panel’s, and instead of the mark, while the box is on: the same box with a tick in it, check_square_grey_checkmark.png. Empty, the box draws the mark in Check, inset by Check Inset, which stays 6.

A tick box that keeps its setting

  1. In Assets, press Create, choose New script, type tick_box, and press Enter.
  2. In the Code tab, replace everything in tick_box.rgs with the script below, and press Ctrl+S.
rgs tick_box.rgs
/// A tick box on the settings screen that keeps a setting: it opens on the
/// saved setting, and saves it, 1 ticked and 0 not, each time the player ticks
/// or unticks it.
component TickBox {
    /// The setting it keeps: the name it is saved under, which is also the
    /// action its Toggle presses.
    setting: String = "show_hints",
    /// The setting before the player has ever chosen: 1 ticked, 0 not.
    unsaved: f32 = 1.0,

    fn on_create() {
        // Writing a tick box ticks or unticks it and presses nothing, so the
        // screen opens on the saved setting and saves nothing.
        set_flag("ToggleComponent", "on", saved_number(setting, unsaved) == 1.0);
    }

    fn loop() {
        // A click, or Space or Enter while it holds the focus, flips it and
        // presses its action on that step; amount() answers 1 ticked, 0 not.
        if pressed(setting) {
            save_number(setting, amount(setting));
        }
    }
}
  1. Select show_hints, click Add component and choose TickBox. Its Setting is show_hints and its Unsaved 1, as the script starts: leave both.
  2. Select item_names, click Add component and choose TickBox. Set its Setting to item_names and its Unsaved to 0.

pressed(setting) is true on the step the box flips: a click let go on it, or Enter or Space while it holds the focus. amount(setting) is 1 from then on while it is ticked, and 0 while it is not, and the script saves that number under the setting’s name. On create it ticks or unticks the box from the saved number, which, like a slider, presses nothing.

The Code panel with tick_box.rgs open: a comment on a tick box that keeps a setting, then component TickBox with two fields, setting, a string, show_hints, and unsaved, a number, 1.0, an on_create that sets the Toggle’s on flag from the saved number, and a loop that, when the setting is pressed, saves its amount; the Scripts list at the left names the project’s scripts
set_flag
Writes a true or false onto another component of this node: here the Toggle’s On, ticked if the saved number is 1. Unsaved is the number a player who has never chosen is given.

What the boxes decide

Show hints decides whether the delve draws its line of key hints at the foot of the screen, and Item names whether each pickup wears its name. Both are in the delve’s scene. One script serves both: it shows its node, or hides it, by a saved setting.

  1. In Assets, press Create, choose New script, type shown_by_setting, and press Enter.
  2. In the Code tab, replace everything in shown_by_setting.rgs with the script below, and press Ctrl+S.
rgs shown_by_setting.rgs
/// Shows this node only while a setting the player chose in Settings is on:
/// the key hints in the HUD, a pickup's name in the room.
component ShownBySetting {
    /// The setting that shows it: show_hints or item_names.
    setting: String = "show_hints",
    /// The setting before the player has ever chosen: 1 on, 0 off.
    unsaved: f32 = 1.0,

    fn on_create() {
        // Settings are saved as 1 for on and 0 for off.
        if saved_number(setting, unsaved) == 1.0 {
            show();
        } else {
            hide();
        }
    }
}
  1. Open scenes/delve.ron. Select hud, click + Add node and choose Label. Rename it key_hints.
  2. In its Rect, set Anchor Min and Anchor Max both to 0.5, 1, Offset Min to -320, -44, Offset Max to 320, -12 and Pivot to 0.5, 1. That is a line 640 wide at the foot of the screen.
  3. In its Text, set Text to WASD or arrows walk, Scale to 0.625 and Align to Center. Set Outline to 63, 38, 49, alpha 255 and Outline Width to 2.
  4. Click Add component and choose ShownBySetting. Its Setting is show_hints and its Unsaved 1, as the script starts: leave both.

On create, shown_by_setting.rgs reads the saved number for its Setting, 1 for on and 0 for off, or Unsaved if the player has never chosen, and shows the node or hides it. show and hide make a node visible or invisible.

The pickups’ names are World Text, which draws words in the room, over a pickup, and not on the screen. There are twelve, one for each pickup. You make one and copy it.

  1. Select potion_1, click + Add node and choose Node. Rename it potion_1_name, and untick its Visible: the name starts hidden.
  2. Click Add component and choose WorldTextComponent. Set Text to Potion, Scale to 0.1, Color to 236, 227, 206, alpha 255, Offset to 0, -10 and Sort Layer to 4. Set Outline to 63, 38, 49, alpha 255 and Outline Width to 0.5.
  3. Click Add component and choose ShownBySetting. Set its Setting to item_names and its Unsaved to 0.
  4. Press Ctrl+C. For each of the other eleven pickups, right-click it and choose Paste as child: potion_2, elixir, key, gem_green, gem_red, gem_blue and coin_1 to coin_5.
  5. Rename each copy after its pickup, potion_2_name, elixir_name, key_name, gem_green_name, gem_red_name, gem_blue_name and coin_1_name to coin_5_name, and set its Text: Elixir for the elixir, Key for the key, Gem for each of the three gems, and Coin for each of the five coins.

A World Text’s Scale is a share of the face’s size, as a Text’s is: 0.1 is small enough for a name over a sprite 16 units across. Offset is how far from its node it sits, in the room’s own units, here up 10; Sort Layer sorts it over the pickup’s sprite; Outline Width is in room units too, 0.5. The name is hidden on create unless the saved Item names is 1.

The settings screen after Show hints was clicked: the same screen as on a first launch, but the SHOW HINTS box is empty
Show hints clicked: off.

A list

The last row is a Dropdown: a closed list that opens when it is pressed and shows its chosen option. This one picks the size of everything on the screen.

  1. Open scenes/settings.ron. Select size_row, click + Add node and choose Dropdown. Rename it text_size.
  2. In its Rect, set Anchor Min and Anchor Max both to 0, 0.5, Offset Min to 244, -20, Offset Max to 484, 20 and Pivot to 0, 0.5. That is a face 240 wide and 40 high.
  3. In its Panel, set Texture to input_rectangle.png and Border to 8. In its Text, set Text to Text size, Scale to 0.75 and Align to Center.
  4. In its Dropdown, set Action to text_size. Under Options, press the + until there are three, and set them to Small, Normal and Large. Set Selected to 1 and Item Height to 40.
  5. Click Add component and choose FocusComponent.
  6. Select text_size, click + Add node and choose Panel. Rename it text_size_arrow. In its Rect, set Anchor Min and Anchor Max both to 1, 0.5, Offset Min to -46, -11, Offset Max to -12, 11 and Pivot to 1, 0.5. In its Panel, set Texture to icon_arrow_down_dark.png.
  7. Select text_size, click + Add node and choose Label. Rename it text_size_label. Give its Rect the values of music_label, and set its Text to Text size and its Scale to 0.75.
text_size selected, its Inspector narrowed with the search box, dropdown, to its Dropdown: Action text_size, Options with three items, Small, Normal and Large, Selected 1, Disabled unticked, Item Height 40.00, Normal white, Hovered yellow, Pressed light grey and Popup white; the Viewport shows the SETTINGS screen with the TEXT SIZE list outlined
Action
A Dropdown’s Action: the action it fires when the chosen row really changes. Choosing the row that is already chosen presses nothing. A script reads amount("text_size"), the chosen row’s number counted from 0.
Options
The rows of the open list, in order: Small, Normal, Large.
Selected
Which row is chosen, counted from 0, so 1 is Normal. A number past the last row reads as nothing chosen. The closed face shows the chosen option’s words, over its own Text.
Item Height
The height of one row of the open list, in UI units: 40, so the three rows are 120 high.
Normal, Hovered, Pressed
A Dropdown’s tints for its closed face, as a Button’s are: at rest, under the pointer or with the focus, and held down or while the list is open. Apply theme writes them: 255, 255, 255 at rest, the palette’s accent yellow 254, 231, 97 when lit, which is also the colour of the row the keys or the pointer are on, and 211, 211, 211 held.
Popup
The background of the open list, white here, 255, 255, 255. It is drawn opaque, since the list opens over the rows below it.

A list that keeps its choice

  1. In Assets, press Create, choose New script, type text_size_list, and press Enter.
  2. In the Code tab, replace everything in text_size_list.rgs with the script below, and press Ctrl+S.
rgs text_size_list.rgs
/// The Text size list on the settings screen: it opens on the saved size, and
/// saves the size each time the player picks one. 0 is Small, 1 Normal and 2
/// Large, the order of the list's options.
component TextSizeList {
    fn on_create() {
        // Writing the list's choice changes what it shows and presses
        // nothing. Normal until the player has chosen.
        set_number("DropdownComponent", "selected", saved_number("text_size", 1.0));
    }

    fn loop() {
        // A click on a row, or Enter on the row the arrows lit, chooses it and
        // presses text_size on that step; amount() answers the row, from 0.
        if pressed("text_size") {
            save_number("text_size", amount("text_size"));
        }
    }
}
  1. Select text_size, click Add component and choose TextSizeList.

pressed("text_size") is true on the step a real choice is made: a click on a row, or Enter on the row the arrows have lit. amount("text_size") is that row’s number from 0: 0 is Small, 1 Normal and 2 Large.

A click opens the list, and a click on a row chooses it; a click anywhere else closes it. With the keys, Enter or Space opens it from the focus, Down and Up light a row, Enter chooses the lit row, and Esc closes the list without choosing.

The Code panel with text_size_list.rgs open: a comment on the Text size list, then component TextSizeList with an on_create that sets the Dropdown’s selected from saved_number text_size with 1.0, and a loop that, when pressed text_size, saves its amount; the Scripts list at the left names the project’s scripts
The TEXT SIZE list open under its face, which reads NORMAL in a darker tint: three rows, SMALL, NORMAL lit in yellow, and LARGE, covering the top of the BACK button
The list open: Normal lit.

Sizing every screen

A choice of size has to reach every screen, so one script sizes a canvas from the saved choice, and goes on every canvas: the delve’s HUD, the title and the settings.

  1. In Assets, press Create, choose New script, type text_size, and press Enter.
  2. In the Code tab, replace everything in text_size.rgs with the script below, and press Ctrl+S.
rgs text_size.rgs
/// Sizes everything on this canvas by the text size the player chose in
/// Settings. A canvas fits its reference into the window, so the smaller the
/// reference, the bigger everything on it is drawn.
component TextSize {
    /// The reference at Small.
    small: Vec2 = vec2(1536.0, 864.0),
    /// The reference at Normal: the window's own size, the art at 1x.
    normal: Vec2 = vec2(1280.0, 720.0),
    /// The reference at Large.
    large: Vec2 = vec2(1066.0, 600.0),

    fn loop() {
        // Read on every step, so the settings screen changes size as soon as
        // its list saves a new choice. Normal, 1, until the player has chosen.
        let size = saved_number("text_size", 1.0);
        if size == 0.0 {
            set_point("CanvasComponent", "reference_size", small);
        } else if size == 2.0 {
            set_point("CanvasComponent", "reference_size", large);
        } else {
            set_point("CanvasComponent", "reference_size", normal);
        }
    }
}
  1. Select settings_canvas, click Add component and choose TextSize. Its Small, Normal and Large start at the script’s own 1536 by 864, 1280 by 720 and 1066 by 600: leave them.
  2. Do the same for title_canvas in scenes/title.ron, and for hud in scenes/delve.ron.

A canvas fits its Reference Size into the window, so a smaller reference draws everything on it bigger. Large is a reference of 1066 by 600, one fifth bigger than 1280 by 720, and Small is 1536 by 864, a sixth smaller. set_point writes a pair of numbers, and vec2 builds one; the script writes the canvas’s reference_size on every step, from the saved size, 1 (Normal) until the player has chosen.

A ring for the keys

A player with the keys needs to see where they are. The lit tint of a button says it, and a slider brightens, but a tick box and a list need more. A Focus Ring draws a ring round whatever the keys have moved to. It is on the canvas, so it serves every control under it.

  1. Select settings_canvas, click Add component and choose FocusRingComponent.
  2. Open scenes/title.ron, select title_canvas, and add a FocusRingComponent the same way. The title’s menu then shows it too.
settings_canvas selected, its Inspector narrowed with the search box, ring, to its Focus Ring: Color orange, Width 2.0 and Outset 2.0; the Viewport shows the SETTINGS screen with a thin blue outline round the whole canvas
Color
A Focus Ring’s Color: the colour of the ring. Apply theme sets it to the palette’s accent words, 254, 174, 52, a gold that reads over pictures and plates alike. A new ring’s is white.

The ring’s Width and Outset stay as they come, 2 and 2, and chapter 16 defines them.

The ring is shown by a key, and hidden by the pointer. An arrow, Tab or Shift+Tab shows it round the control the focus has moved to; any press of the pointer hides it until the next key. So a player who uses the mouse does not see it, and one who uses the keys does.

Links, and a way back

Every control gets its focus links, as the title’s did, so that the arrows go round the screen in the order it reads. A control’s Up and Previous name the one above it, and its Down and Next the one below, round the loop: music, effects, show_hints, item_names, text_size and back.

  1. Open scenes/title.ron, select quit and press Ctrl+C. Open scenes/settings.ron, right-click settings_frame and choose Paste as child. The copy brings the button’s pictures, its Shortcut and its Focus. Rename it back.
  2. In its Rect, set Anchor Min and Anchor Max both to 0.5, 1, Offset Min to -110, -73, Offset Max to 110, -24 and Pivot to 0.5, 1. In its Text, set Text to Back. In its Button, set Action to back. Its Shortcut’s Action stays ui_cancel.
  3. In each control’s Focus, set the four links from the table below.
NodeUpDownNextPrevious
musicbackeffectseffectsback
effectsmusicshow_hintsshow_hintsmusic
show_hintseffectsitem_namesitem_nameseffects
item_namesshow_hintstext_sizetext_sizeshow_hints
text_sizeitem_namesbackbackitem_names
backtext_sizemusicmusictext_size

Back goes on the way out: it plays the back sound and returns to the title. Its script is on the frame, with the sound.

  1. In Assets, press Create, choose New script, type settings_menu, and press Enter.
  2. In the Code tab, replace everything in settings_menu.rgs with the script below, and press Ctrl+S.
rgs settings_menu.rgs
/// The settings screen's way back: Back goes to the title. Each control keeps
/// its own setting.
component SettingsMenu {
    fn loop() {
        // A click on Back, Enter while it holds the focus, or Escape through
        // its Shortcut presses back on the game's next step. Escape closes an
        // open list first, and goes back on the next press.
        if pressed("back") {
            // A sound asked for on the step that changes scene plays on after
            // it.
            play_sound("back");
            change_scene("title");
        }
    }
}
  1. Select settings_frame, click Add component and choose SettingsMenu.
  2. Click Add component again and choose AudioSourceComponent. Untick Positional. In its Triggers, press the + beside it once, and set the entry’s Name to back and its Clip to back_001.ogg.
  3. Select settings_canvas and press Apply theme. It writes the colours of everything on the screen from the palette: the labels’ cream, the sliders’ and the list’s tints, the boxes’ marks, and the ring’s colour, the accent’s words, 254, 174, 52, alpha 255.
  4. Do the same on title_canvas in scenes/title.ron, where it gives the ring its colour, and on hud in scenes/delve.ron, where it gives the key line its cream.

pressed("back") is true on the step after Back is pressed: a click let go on it, Enter while it holds the focus, or Esc through its Shortcut. An Esc that closes an open list does that first and goes back on the next press, since the list takes it.

The settings screen after one press of the down arrow: an orange ring round the EFFECTS slider, and the rest as on a first launch
The arrow key: the ring on the effects slider.

Saved and applied

Each control’s script reads its saved number on create and saves when it is pressed (save_number and saved_number). That is half of it. The other half is the title, which has to set both volumes as it opens, before the game’s first sound, and a Settings button that opens the screen. Both are small changes to the title’s two scripts.

  1. In the Code tab’s Scripts list, click title.rgs, replace everything in it with the script below, and press Ctrl+S.
rgs title.rgs
/// The title, as it opens: the volumes the player chose in Settings, set before
/// the game's first sound, and the menu faded in. The Canvas Group on this
/// node starts at alpha 0, where the menu is not drawn and takes no key; this
/// brings it to 1 over half a second.
component Title {
    fn on_create() {
        // 0.8 until the player has chosen a volume.
        set_music_volume(saved_number("music_volume", 0.8));
        set_sfx_volume(saved_number("effects_volume", 0.8));
        anneal_number("CanvasGroupComponent", "alpha", 1.0, 0.5, .OutQuad);
    }
}
  1. Click title_menu.rgs in the list, replace everything in it with the script below, and press Ctrl+S.
rgs title_menu.rgs
/// The title's menu: New game starts a fresh run in the delve, Settings opens
/// the settings screen, and Quit closes the game.
component TitleMenu {
    /// The health a new game starts with.
    start_health: f32 = 10.0,

    fn loop() {
        // A button pressed by a click, by Enter while it holds the focus, or
        // by its Shortcut presses its action on the game's next step.
        if pressed("new_game") {
            // What a run keeps outlasts a change of scene, so a new game puts
            // it back as a run starts: full health, and nothing picked up.
            keep_number("health", start_health);
            keep_number("potions", 0.0);
            keep_number("elixirs", 0.0);
            keep_number("keys", 0.0);
            keep_number("gems", 0.0);
            keep_number("coins", 0.0);
            // A sound asked for on the step that changes scene plays on after
            // it.
            play_sound("click");
            change_scene("delve");
        }
        if pressed("settings") {
            play_sound("click");
            change_scene("settings");
        }
        if pressed("quit") {
            play_sound("back");
            // The game closes at the end of the step that asks, which would
            // cut the sound off, so it waits a tenth of a second first.
            await wait(0.1);
            quit();
        }
    }
}

The first script now sets the music and effects buses from the saved volumes, 0.8 until a player has chosen. The second adds Settings, which plays the click and opens settings. pressed("settings") is true for one step after Settings is pressed, by a click let go on it or by Enter while it holds the focus, now that it is not disabled.

In the editor’s Play, the saved values are kept in a file of their own, play-saved.ron in your project folder, so trying the game never touches a real player’s. They are still there the next time you Play. With Play stopped, Scene → Clear saved values empties it and every setting goes back to its default.

The delve’s key line: the room with the HUD’s plate at its top left and the bag at the right, and along the foot of the screen the words WASD OR ARROWS WALK
The delve’s key line, Show hints on.

The two pictures here and below are of the same delve. In the first, Show hints is on, as it is for a player who has never opened Settings, so the line shows. In the second the player has first ticked Item names and chosen Large, then started a new game: the names show over the pickups and the HUD is drawn bigger.

The delve after Item names was ticked and Large chosen: the HUD’s plate and the bag drawn one fifth bigger, the key line along the foot bigger too, and names over the pickups: GEM over the red and the green gems, KEY over the key, POTION over each red flask, and COIN over each of the five coins
Item names ticked and Large chosen, then New game: names over the pickups, the HUD bigger.

The settings screen, its controls and the changes to the title and the delve

The settings screen

NodeComponentFieldValue
cameraCameraScalingLetterbox
Extent180
Width320
Background118, 59, 54, alpha 255
settings_canvasCanvasScale ModeShrinkToFit
ThemePanel255, 255, 255, alpha 255
Panel Accent254, 231, 97, alpha 255
Panel Danger232, 69, 55, alpha 255
Text236, 227, 206, alpha 255
Text Accent254, 174, 52, alpha 255
Text Danger255, 228, 220, alpha 255
Button255, 255, 255, alpha 255
Button Text63, 38, 49, alpha 255
Hover Lift0.15
Press Sink0.35
settings_nameRectAnchor Min0.5, 0.5
Anchor Max0.5, 0.5
Offset Min-360, -256
Offset Max360, -176
Pivot0.5, 1
TextTextSettings
Scale1.5
Color254, 174, 52, alpha 255, written by Apply theme
AlignCenter
Outline63, 38, 49, alpha 255
Outline Width2
Shadow0, 0, 0, alpha 153
Shadow Offset3, 3
Shadow Softness0
Theme RoleRoleAccent
settings_frameRectAnchor Min0.5, 0.5
Anchor Max0.5, 0.5
Offset Min-270, -160
Offset Max270, 240
PanelColor255, 255, 255, alpha 255, written by Apply theme
Texturepanel_brown.png
Border8
settings_rowsRectAnchor Max1, 0
Offset Min24, 24
Offset Max-24, 312
Pivot0.5, 0
StackFitFixed
Spacing12

The sliders

NodeComponentFieldValue
music, effectsRectAnchor Min0, 0.5
Anchor Max0, 0.5
Offset Min244, -8
Offset Max484, 8
Pivot0, 0.5
SliderValue0.8
Step0.1
Fill255, 255, 255, alpha 255, written by Apply theme
Track255, 255, 255, alpha 255, written by Apply theme
Fill Textureslide_horizontal_color.png
Track Textureslide_horizontal_grey.png
Border8
musicSliderActionmusic
effectsSliderActioneffects
effectsAudio SourcePositionalunticked
Triggersclick with click_001.ogg; Mode Shuffle, Pitch and Volume 1 to 1
EffectsVolumeSample Gap0.15
Quiet0
music_label, effects_labelRectAnchor Min0, 0.5
Anchor Max0, 0.5
Offset Min-244, -24
Offset Max-24, 24
Pivot0, 0.5
TextScale0.75
Color236, 227, 206, alpha 255, written by Apply theme
music_labelTextTextMusic
effects_labelTextTextEffects

The tick boxes

NodeComponentFieldValue
show_hints, item_namesRectAnchor Min0, 0.5
Anchor Max0, 0.5
Offset Min244, -16
Offset Max276, 16
Pivot0, 0.5
PanelColor255, 255, 255, alpha 255, written by Apply theme
Texturecheck_square_grey.png
ToggleNormal255, 255, 255, alpha 255, written by Apply theme
Hovered255, 255, 255, alpha 255, written by Apply theme
Pressed211, 211, 211, alpha 255, written by Apply theme
Check63, 38, 49, alpha 255, written by Apply theme
On Texturecheck_square_grey_checkmark.png
show_hintsToggleActionshow_hints
Onticked
TickBoxSettingshow_hints
Unsaved1
item_namesToggleActionitem_names
TickBoxSettingitem_names
Unsaved0
show_hints_label, item_names_labelRectAnchor Min0, 0.5
Anchor Max0, 0.5
Offset Min-244, -24
Offset Max-24, 24
Pivot0, 0.5
TextScale0.75
Color236, 227, 206, alpha 255, written by Apply theme
show_hints_labelTextTextShow hints
item_names_labelTextTextItem names

The text size list

NodeComponentFieldValue
text_sizeRectAnchor Min0, 0.5
Anchor Max0, 0.5
Offset Min244, -20
Offset Max484, 20
Pivot0, 0.5
PanelColor255, 255, 255, alpha 255, written by Apply theme
Textureinput_rectangle.png
Border8
TextTextText size
Scale0.75
Color63, 38, 49, alpha 255, written by Apply theme
AlignCenter
DropdownActiontext_size
OptionsSmall, Normal, Large
Selected1
Item Height40
Normal255, 255, 255, alpha 255, written by Apply theme
Hovered254, 231, 97, alpha 255, written by Apply theme
Pressed211, 211, 211, alpha 255, written by Apply theme
Popup255, 255, 255, alpha 255, written by Apply theme
text_size_arrowRectAnchor Min1, 0.5
Anchor Max1, 0.5
Offset Min-46, -11
Offset Max-12, 11
Pivot1, 0.5
PanelColor255, 255, 255, alpha 255, written by Apply theme
Textureicon_arrow_down_dark.png
text_size_labelRectAnchor Min0, 0.5
Anchor Max0, 0.5
Offset Min-244, -24
Offset Max-24, 24
Pivot0, 0.5
TextTextText size
Scale0.75
Color236, 227, 206, alpha 255, written by Apply theme
settings_canvasTextSizeSmall1536, 864
Normal1280, 720
Large1066, 600

The ring and the way back

NodeComponentFieldValue
settings_canvasFocus RingColor254, 174, 52, alpha 255, written by Apply theme
backRectAnchor Min0.5, 1
Anchor Max0.5, 1
Offset Min-110, -73
Offset Max110, -24
Pivot0.5, 1
PanelColor255, 255, 255, alpha 255, written by Apply theme
TexturebuttonLong_beige.png
Border8
TextTextBack
Scale0.75
Color63, 38, 49, alpha 255, written by Apply theme
AlignCenter
ButtonActionback
Normal255, 255, 255, alpha 255, written by Apply theme
Hovered255, 255, 255, alpha 255, written by Apply theme
Pressed211, 211, 211, alpha 255, written by Apply theme
Hovered TexturebuttonLong_grey.png
Pressed TexturebuttonLong_beige_pressed.png
Disabled TexturebuttonLong_blue.png
ShortcutActionui_cancel
settings_frameAudio SourcePositionalunticked
Triggersback with back_001.ogg; Mode Shuffle, Pitch and Volume 1 to 1

The title’s changes

NodeComponentFieldValue
TitleAudio SourceClipabandoned_castle_music_-_starninjas_6.ogg
BusMusic
Loopingticked
Autoplayticked
Positionalunticked
title_canvasFocus RingColor254, 174, 52, alpha 255, written by Apply theme
TextSizeSmall1536, 864
Normal1280, 720
Large1066, 600
settingsButtonDisabledunticked

The delve’s key line and its sizing

NodeComponentFieldValue
hudTextSizeSmall1536, 864
Normal1280, 720
Large1066, 600
key_hintsRectAnchor Min0.5, 1
Anchor Max0.5, 1
Offset Min-320, -44
Offset Max320, -12
Pivot0.5, 1
TextTextWASD or arrows walk
Scale0.625
Color236, 227, 206, alpha 255, written by Apply theme
AlignCenter
Outline63, 38, 49, alpha 255
Outline Width2
ShownBySettingSettingshow_hints
Unsaved1

The pickups’ names

NodeComponentFieldValue
potion_1_name, potion_2_name, elixir_name, key_name, gem_green_name, gem_red_name, gem_blue_name, coin_1_name to coin_5_nameNodeVisibleunticked
World TextScale0.1
Color236, 227, 206, alpha 255
Offset0, -10
Sort Layer4
Outline63, 38, 49, alpha 255
Outline Width0.5
ShownBySettingSettingitem_names
Unsaved0
potion_1_name, potion_2_nameWorld TextTextPotion
elixir_nameWorld TextTextElixir
key_nameWorld TextTextKey
gem_green_nameWorld TextTextGem
gem_red_nameWorld TextTextGem
gem_blue_nameWorld TextTextGem
coin_1_name to coin_5_nameWorld TextTextCoin

The settings scene’s root, Settings, and the five row nodes are empty nodes. Every other field of every node is as it comes.

Check it works