Build a Dynamic Choice Menu in Ren'Py that Adapts to Player State
Create a Ren'Py menu that updates automatically based on the player’s inventory. Learn how to use screen language, Python logic, and dynamic visibility to keep your UI clean and responsive.
07 Sept 2026, 15:58 UTC

The Problem: Static Menus Hide Player Progress
In many visual novels and narrative games, players interact with a menu that offers choices such as “Talk to A”, “Open the chest”, or “Use the key”. If that menu remains static, it can become cluttered or misleading once the player’s inventory changes. A menu that still shows “Use the key” after the key has been consumed wastes screen real estate and can confuse the player.
Thesis: Use Screens to Build Reactive Menus
Ren'Py’s screen language separates the UI layout from the game logic. By defining a screen once and letting the engine refresh it automatically when variables change, you can create a menu that shows only the options that are currently relevant. This keeps the codebase tidy and the interface clear.
Designing a State‑Aware Screen
To make a menu that reacts to the player’s state, you need three things:
- Game variables that represent the state (e.g., inventory items).
- Python logic that evaluates conditions inside the screen.
- Screen declarations that use
visibleandifclauses to show or hide items.
Below is a minimal screen that displays a list of actions based on the player’s inventory. Each button calls a Python function or jumps to a label when pressed.
# screens.rpy
screen dynamic_menu():
tag menu
frame:
xalign 0.5
yalign 0.5
xsize 400
style "menu_frame"
vbox:
spacing 10
label "What would you like to do?"
# The "Use the key" option only appears if the key is in the inventory
if "key" in inventory:
textbutton "Use the key" action Function(use_key)
# The "Open the chest" option only appears if the chest hasn’t been opened yet
if not chest_opened:
textbutton "Open the chest" action Jump("open_chest")
# A generic option that is always visible
textbutton "Check inventory" action Show("inventory_screen")
Key points:
visibleis optional; theifclause inside the screen is enough to hide the button.- Ren'Py automatically re‑evaluates the
ifclauses whenever a variable changes, so the menu updates instantly. - All UI logic lives in
screens.rpy, keeping the story script clean.
A Worked Example: Inventory‑Based Choices
Below is a small project that demonstrates the pattern. Create a new Ren'Py project and add the following files.
- game/screen.rpy – contains the
dynamic_menuscreen shown above. - game/script.rpy – defines inventory variables and calls the menu.
- game/renpy.cfg – ensures the project uses Ren'Py 7.0+ (the default for new projects).
game/script.rpy
# Define the inventory as a Python list
init python:
inventory = []
chest_opened = False
label start:
"You enter the dimly lit room."
call screen dynamic_menu
"You decide to use your key."
jump end
label open_chest:
"You open the chest and find a key."
$ inventory.append("key")
$ chest_opened = True
call screen dynamic_menu
jump start
label use_key:
"You use the key to unlock the door."
$ inventory.remove("key")
call screen dynamic_menu
jump end
label end:
"The game ends."
Run the game with the Ren'Py launcher. The menu initially shows only “Open the chest” and “Check inventory”. After opening the chest, the “Use the key” button appears. Using the key removes it from the menu again. Notice how the screen refreshes automatically after each action.
Trade‑offs and Performance
Dynamic screens are powerful but come with some caveats:
- Compatibility: The screen language used above requires Ren'Py 7.0 or newer. Older projects that still use the legacy UI system will need to migrate.
- Performance: If a screen contains dozens of
ifstatements that evaluate complex Python logic, the engine will re‑evaluate them each frame. On low‑end hardware, this can cause a minor frame‑rate drop. For most menus, the impact is negligible. - Debugging: Because the screen automatically updates, bugs can surface when a variable changes unexpectedly. Adding print statements or using the
renpy.printfunction can help trace state changes.
Take‑aways and Next Steps
By using Ren'Py’s screen language for dynamic menus:
- You keep logic and UI separate, making the code easier to maintain.
- Menus stay clean and responsive to player actions.
- Adding new items or conditions is as simple as updating the inventory list or adding a new
ifclause.
Next steps for more advanced use cases:
- Use
renpy.show_screenandrenpy.hide_screento animate menu transitions. - Store inventory data in a dictionary to attach metadata (e.g., item description, icon).
- Leverage the
onhandler to add hover or click effects without cluttering the screen definition.
Try extending the example: add a “Use the map” button that only appears after the player finds a map item. Experiment with positioning the menu on the left side of the screen or using a grid layout.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.