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.rbis loaded before anything inside the001 PFM/folder. - The
001 PFM/folder contains the persistence layer (optional), the002 UI/folder contains the visual components, the003 GamePlay/folder contains the scene logic. - In this first guide, we only create three files:
000 Entry.rb,003 GamePlay/000 Base.rb, and003 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_classcreates 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_giftis the public method that the game calls to open the scene. It usescurrent_scene.call_scenewhich 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
supercallsGamePlay::Base#initializewhich creates the message window and the internal clock of the scene.@running = trueindicates that the scene is active. Setting it tofalseexits the scene.create_graphicsis automatically called by the framework afterinitialize. It creates the viewport, instantiates the base UI, then callsGraphics.sort_zto sort all visual elements by depth.create_viewportis inherited fromGamePlay::Base. It creates the main viewport@viewportat z=10_000, which ensures the scene displays above game elements.create_base_uiinstantiates the GenericBase subclass defined earlier. The second argumentbutton_textsdefines the ctrl button texts.button_textsreturns an array of 4 elements corresponding to the ctrl buttons in order [A, X, Y, B]. Settingnilhides 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_inputsis called every frame by the framework.automatic_input_updatechecks each key and calls the matchingaction_*method if it is pressed and defined. Here onlyaction_bexists, so pressing B exits.action_bsets@runningtofalseto exit the scene. In the keyboard guide we extractupdate_inputsand the actions into their own file,003 Input.rb, and add navigation.update_graphicsis called every frame to update animations.update_background_animationanimates the background scrolling (a no-op here since we disabled it).- The last line
GamePlay.mystery_gift_class = GamePlay::MysteryGiftregisters the class in the accessor defined in000 Entry.rb, which makesGamePlay.open_mystery_giftwork.
Lifecycle
The framework executes the scene following a precise cycle:
initialize: the constructor is called. You initialize state variables but do not create any graphics.create_graphics: the framework calls this method once, right afterinitialize. This is where you create viewports, the base UI, and (later) the composition.- Update loop: every frame, the framework calls in order:
update_inputs: keyboard input handlingupdate_mouse(moved): mouse input handling (added in the mouse guide)update_graphics: animation updates
- The loop repeats until
@runningis set tofalse.
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), and003 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_viewportcreates the viewport at z=10_000. - The
button_textsarray controls ctrl button visibility:nilhides the button, a string displays it with the given text. - The framework calls
update_inputsonly if you define it;automatic_input_updateroutes a key press to itsaction_*method. Set@runningtofalseto exit the scene. - Register the class at the bottom of
001 Main.rbwithGamePlay.mystery_gift_class = GamePlay::MysteryGift.