AppleyAppley Docs
Docs/UI

Menus

Create main menus, pause menus, and navigation systems.

33 min read Intermediate Updated 2026-07-01 v2.0 ~119 min to complete

Introduction

Menus are the first thing players see. This guide covers main menu design, pause menus, settings navigation, and smooth transitions between menu states.

Guide 22 of 25

This is part of the Intermediate Guides collection. Category: UI. Estimated completion: 119 minutes. Difficulty: Intermediate.

Screenshot

Menus
Menus

Step-by-Step Guide

1

Design menu states

Define menu states: Main, Settings, Play, Load, Credits. Each state has a unique panel with buttons.

2

Create menu panels

Build a Canvas with panels for each menu state. Only one panel is visible at a time. Use layout groups.

3

Add button navigation

Buttons transition between states. Play -> game scene, Settings -> settings panel, Back -> previous panel.

4

Implement pause menu

On Escape key, pause gameplay (timeScale = 0), show pause panel with Resume, Settings, Quit. Resume restores timeScale = 1.

5

Add menu transitions

Animate panel transitions with fade/slide. Use 0.2-0.3s transitions for smooth but responsive navigation.

6

Handle controller navigation

Support keyboard/gamepad: arrow keys move focus, Enter activates. Highlight the focused button.

FluxScript Example

Here is a complete FluxScript implementation for this guide:

Menus — FluxScript
menuState = "main"
menuHistory = []
isPaused = false

on start { showMenu("main") }

func showMenu(state) {
  ui.hide("mainPanel")
  ui.hide("settingsPanel")
  ui.hide("playPanel")
  ui.hide("pausePanel")
  ui.show(state + "Panel")
  ui.fadeIn(state + "Panel", 0.2)
  if menuState != state menuHistory.push(menuState)
  menuState = state
  audio.play("menu_navigate")
}

func goBack() {
  if menuHistory.length > 0 {
    prev = menuHistory.pop()
    menuState = prev
    showMenu(prev)
  }
}

on uiClick("btnPlay") { showMenu("play") }
on uiClick("btnSettings") { showMenu("settings") }
on uiClick("btnBack") { goBack() }

on input("escape") {
  if gameRunning {
    isPaused = !isPaused
    if isPaused pauseGame() else resumeGame()
  }
}

func pauseGame() {
  gameRunning = false
  world.setTimeScale(0)
  ui.show("pausePanel")
  ui.fadeIn("pausePanel", 0.15)
  audio.play("pause")
  audio.setVolume("bgm", 0.3)
}

func resumeGame() {
  gameRunning = true
  world.setTimeScale(1)
  ui.hide("pausePanel")
  audio.play("resume")
  audio.setVolume("bgm", 0.7)
}

on uiClick("btnResume") { resumeGame() }
on uiClick("btnQuitToMenu") {
  saveGame()
  world.setTimeScale(1)
  scene.load("main_menu")
}

Node Graph Example

Prefer visual programming? Here is the equivalent Node Graph:

Menus — Node Graph
[On Start] --> [Show Menu: "main"]

[Function: showMenu] --> [Hide: all panels]
   --> [Show: target panel + fade in 0.2s]
   --> [Push: state to history] --> [Play: navigate sound]

[Function: goBack] --> [Pop: history] --> [Show: previous]

[On Input "escape"] --> [Branch: gameRunning]
   +-- Toggle: isPaused
   +-- True --> [pauseGame: timeScale=0, show pause]
   +-- False --> [resumeGame: timeScale=1, hide pause]

[On UIClick "btnResume"] --> [resumeGame]
[On UIClick "btnQuitToMenu"] --> [Save + Load: main_menu]

Optimization Tips

Keep your implementation fast and efficient with these optimization tips:

  • Pool menu panels. Do not create/destroy panels on navigation — show/hide pre-created panels. Prevents GC spikes.
  • Animate transitions with 0.2-0.3s fades. Instant panel swaps feel jarring; smooth fades feel polished.
  • Support keyboard and gamepad navigation. Arrow keys move focus; Enter activates. Essential for accessibility.
  • Lower music volume in pause menus. Players expect audio to dim when paused. Restore on resume.

Common Mistakes

Not pausing game time

Set world.setTimeScale(0) when pausing. Without it, enemies and physics continue while paused.

No back navigation history

Track menu history with a stack. Without it, Back always goes to main menu, even from nested sub-menus.