Skip to main content
View
Mystery Gift — Step 2 / 11— Create a UI scene
1. Prepare the assets and text before creating a UI
2. Create a UI scene
3. Create a Composition
4. Create a SpriteStack component
5. Add a data layer with PFM
6. Handle keyboard input
7. Handle mouse input
8. Add i18n text
9. Use confirmation dialogs
10. Create animations
11. Customize GenericBase

Create a UI scene

This is the first code guide in a series where we build a complete Mystery Gift UI step by step. This system lets players enter codes to claim rewards. The setup guide already put the graphics and text in place; here we create the file structure and a minimal scene that displays and closes with B.

Principle​

A UI in PSDK is organized in distinct layers, each with a specific role:

  • PFM (optional): the data model. Only used if the scene requires persistence. In our Mystery Gift UI, this layer will store the codes already used by the player.
  • UI: the visual components. Contains constants, graphical sub-components (based on SpriteStack), and the Composition that orchestrates everything.
  • GamePlay: the scene logic. Manages the lifecycle, keyboard and mouse inputs, and transitions.

The name must be identical across all three layers: PFM::MysteryGift, UI::MysteryGift, GamePlay::MysteryGift. This convention is mandatory in PSDK.

File structure​

Here is the complete file structure of the Mystery Gift UI that we will build across the following guides:

scripts/20 MysteryGift/
000 Entry.rb -> GamePlay accessor
001 Constants.rb -> UI::MysteryGift module constants
001 PFM/
000 MysteryGift.rb -> PFM::MysteryGift (persistence)
002 UI/
000 GiftRow.rb -> Gift row component
001 ReceivedBanner.rb -> Gift received animation banner
999 Composition.rb -> Visual orchestrator
003 GamePlay/
000 Base.rb -> GenericBase subclass
001 Main.rb -> Scene class
002 Logic.rb -> Business logic
003 Input.rb -> Keyboard input
004 Mouse.rb -> Mouse input
  • Files and folders are prefixed with numbers that define the loading order.
  • A file with the same prefix as a folder is loaded before the folder contents. For example, 001 Constants.rb is loaded before anything inside the 001 PFM/ folder.
  • The 001 PFM/ folder contains the persistence layer (optional), the 002 UI/ folder contains the visual components, the 003 GamePlay/ folder contains the scene logic.
  • In this first guide, we only create three files: 000 Entry.rb, 003 GamePlay/000 Base.rb, and 003 GamePlay/001 Main.rb. The other files are added in the following guides.

Entry point​

The entry point exposes an accessor and an opening method on the GamePlay module. This is how the Mystery Gift scene is launched from anywhere in the game.

Create scripts/20 MysteryGift/000 Entry.rb:

module GamePlay
class << self
# @return [Class] the Mystery Gift scene class
attr_accessor :mystery_gift_class

# Open the Mystery Gift scene
def open_mystery_gift
return current_scene.call_scene(mystery_gift_class)
end
end
end
  • attr_accessor :mystery_gift_class creates a class attribute that stores the reference to the scene class. This allows other scripts to replace the scene with a custom version via monkey-patching.
  • open_mystery_gift is the public method that the game calls to open the scene. It uses current_scene.call_scene which pushes the new scene on top of the current one.
  • The scene class is registered in this accessor at the bottom of 001 Main.rb, which we will see later in this guide.

Custom GenericBase​

The GenericBase class provides the common base visual elements shared by all scenes: background image, button bar, ctrl buttons (A/X/Y/B), and the win_text (reusable text object). You should never rebuild these elements manually: inherit from GenericBase and override private methods to customize the rendering.

Create scripts/20 MysteryGift/003 GamePlay/000 Base.rb:

module UI
# Custom base UI for the Mystery Gift scene
class MysteryGiftBase < GenericBase
private

# Return the background filename
# @return [String]
def background_filename
return 'mystery_gift/background'
end

# Disable the background scroll animation
def create_background_animation; end
end
end
  • MysteryGiftBase < GenericBase: inherit from GenericBase to get the entire visual foundation without rewriting anything.
  • background_filename: override of the private method that returns the background image path (graphics/interface/mystery_gift/background.png, installed in the setup guide). The framework loads this image automatically.
  • create_background_animation: by defining an empty method, you disable the background scroll animation. If you want the animation, simply do not redefine this method.
  • This is a minimal version. We customize the button bar and ctrl buttons in the GenericBase guide.

Minimal scene​

The scene class itself inherits from BaseCleanUpdate::FrameBalanced, which provides the standard lifecycle and frame balancing. For this first guide, we create a simplified version without composition or list navigation -- those are added in the following guides. It already handles the B button so you can close the scene.

Create scripts/20 MysteryGift/003 GamePlay/001 Main.rb:

module GamePlay
# Mystery Gift scene -- a minimal scene that displays and closes with B
class MysteryGift < BaseCleanUpdate::FrameBalanced
# Create the scene
def initialize
super
@running = true
end

# Handle keyboard input each frame
# @return [Boolean]
def update_inputs
return automatic_input_update
end

# Update graphics each frame
def update_graphics
@base_ui.update_background_animation
end

private

# Create all the graphics for the scene
def create_graphics
create_viewport
create_base_ui
Graphics.sort_z
end

# Create the base UI with button texts
def create_base_ui
@base_ui = UI::MysteryGiftBase.new(@viewport, button_texts)
end

# Return the button texts for the ctrl buttons [A, X, Y, B]
# @return [Array<String, nil>]
def button_texts
return [nil, nil, nil, 'Quit']
end

# Action triggered by the B button -- quit the scene
def action_b
@running = false
end
end
end

GamePlay.mystery_gift_class = GamePlay::MysteryGift
  • super calls GamePlay::Base#initialize which creates the message window and the internal clock of the scene.
  • @running = true indicates that the scene is active. Setting it to false exits the scene.
  • create_graphics is automatically called by the framework after initialize. It creates the viewport, instantiates the base UI, then calls Graphics.sort_z to sort all visual elements by depth.
  • create_viewport is inherited from GamePlay::Base. It creates the main viewport @viewport at z=10_000, which ensures the scene displays above game elements.
  • create_base_ui instantiates the GenericBase subclass defined earlier. The second argument button_texts defines the ctrl button texts.
  • button_texts returns an array of 4 elements corresponding to the ctrl buttons in order [A, X, Y, B]. Setting nil hides the corresponding button. Here, only the B button is visible with the text "Quit". (We move this text to the CSV in the i18n guide.)
  • update_inputs is called every frame by the framework. automatic_input_update checks each key and calls the matching action_* method if it is pressed and defined. Here only action_b exists, so pressing B exits.
  • action_b sets @running to false to exit the scene. In the keyboard guide we extract update_inputs and the actions into their own file, 003 Input.rb, and add navigation.
  • update_graphics is called every frame to update animations. update_background_animation animates the background scrolling (a no-op here since we disabled it).
  • The last line GamePlay.mystery_gift_class = GamePlay::MysteryGift registers the class in the accessor defined in 000 Entry.rb, which makes GamePlay.open_mystery_gift work.

Lifecycle​

The framework executes the scene following a precise cycle:

  1. initialize: the constructor is called. You initialize state variables but do not create any graphics.
  2. create_graphics: the framework calls this method once, right after initialize. This is where you create viewports, the base UI, and (later) the composition.
  3. Update loop: every frame, the framework calls in order:
    • update_inputs: keyboard input handling
    • update_mouse(moved): mouse input handling (added in the mouse guide)
    • update_graphics: animation updates
  4. The loop repeats until @running is set to false.

This cycle is the same for all PSDK scenes. The framework automatically handles frame balancing through the BaseCleanUpdate::FrameBalanced class. In our minimal scene, we have not defined update_mouse yet -- we add it in the mouse guide.

Try it​

With the three files in place, open the scene from a map event ("Script call") or the PSDK console:

GamePlay.open_mystery_gift

You should see the custom background with a "Quit" button at the bottom, and pressing B closes the scene.

Conclusion​

  • A UI scene needs at minimum three files: 000 Entry.rb (entry point), 003 GamePlay/000 Base.rb (GenericBase subclass), and 003 GamePlay/001 Main.rb (scene class).
  • Always use GenericBase for the base UI layer. Never manually rebuild the background, button bar, or ctrl buttons.
  • The inherited create_viewport creates the viewport at z=10_000.
  • The button_texts array controls ctrl button visibility: nil hides the button, a string displays it with the given text.
  • The framework calls update_inputs only if you define it; automatic_input_update routes a key press to its action_* method. Set @running to false to exit the scene.
  • Register the class at the bottom of 001 Main.rb with GamePlay.mystery_gift_class = GamePlay::MysteryGift.