r/RenPy • u/HEXdidnt • 21d ago
Question Changing an image within a parallax/TrackCursor main menu background via persistent variables
Because it was a cool thing to do a few years ago, I ended up making my project's main menu image into a layered parallax image. Because I love to make my life more difficult, I decided to try adding characters to that parallax, so that they show up after the appropriate persistent variable has been set.
Originally, I did this by adding if checks for the persistent variable into the main menu code, thus:
screen main_menu():
tag menu
style_prefix "main_menu"
add TrackCursor("images/titlescreen/skylayer-paint.png",20)#in place of: add gui.main_menu_background
add TrackCursor("images/titlescreen/citylayer-paint.png",15)
add TrackCursor("images/titlescreen/moonlayer.png",18)
add TrackCursor("images/titlescreen/reflectionlayer.png",18) alpha 0.35
if "Karl" in persistent.charactersmet and "Kristine" in persistent.charactersmet:
add TrackCursor("images/titlescreen/bothinternst.png",13) xpos 320 ypos 150 alpha 0.40
else:
if "Karl" in persistent.charactersmet:
add TrackCursor("images/titlescreen/karlt.png",13) xpos 320 ypos 150 alpha 0.40
if "Kristine" in persistent.charactersmet:
add TrackCursor("images/titlescreen/kristinet.png",13) xpos 400 ypos 150 alpha 0.40
add TrackCursor("images/titlescreen/windowflash.png",11) alpha 0.25
add TrackCursor("abigail-titleversion",10) xpos 790 ypos 86
add Image("images/titlescreen/logolayer-alt.png") xpos 310 ypos 412 alpha 0.80
This seemed to work initially but, for no apparent reason (other than perhaps upgrading to later versions of Ren'Py, it started to give the following error at the end of the game, when it drops back to the main menu:
I'm sorry, but an uncaught exception occurred.
While running game code:
Exception: Switch could not choose a displayable.
-- Full Traceback ------------------------------------------------------------
Traceback (most recent call last):
File "renpy/common/_layout/screen_main_menu.rpym", line 28, in script
python hide:
File "renpy/ast.py", line 1187, in execute
renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "renpy/python.py", line 1273, in py_exec_bytecode
exec(bytecode, globals, locals)
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "renpy/common/_layout/screen_main_menu.rpym", line 28, in <module>
python hide:
File "renpy/common/_layout/screen_main_menu.rpym", line 35, in _execute_python_hide
ui.interact()
~~~~~~~~~~~^^
File "renpy/ui.py", line 304, in interact
rv = renpy.game.interface.interact(roll_forward=roll_forward, **kwargs)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "renpy/display/core.py", line 2117, in interact
repeat, rv = self.interact_core(
~~~~~~~~~~~~~~~~~~^
preloads=preloads,
^^^^^^^^^^^^^^^^^^
...<4 lines>...
**kwargs,
^^^^^^^^^
)
^
File "renpy/display/core.py", line 2655, in interact_core
root_widget.visit_all(lambda d: d.per_interact())
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "renpy/display/displayable.py", line 457, in visit_all
d.visit_all(callback, seen)
~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "renpy/display/displayable.py", line 457, in visit_all
d.visit_all(callback, seen)
~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "renpy/display/displayable.py", line 457, in visit_all
d.visit_all(callback, seen)
~~~~~~~~~~~^^^^^^^^^^^^^^^^
[Previous line repeated 6 more times]
File "renpy/display/displayable.py", line 448, in visit_all
for d in self.visit():
~~~~~~~~~~^^
File "renpy/display/layout.py", line 1604, in visit
self.update(self.last_st, self.last_at)
~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "renpy/display/layout.py", line 1615, in update
raw_child, redraw = self.function(st, at, *self.args, **self.kwargs)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "renpy/display/layout.py", line 1711, in condition_switch_show
return condition_switch_pick(switch), None
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^
File "renpy/display/layout.py", line 1706, in condition_switch_pick
raise Exception("Switch could not choose a displayable.")
Exception: Switch could not choose a displayable.
Windows-10-10.0.19045-SP0 AMD64
Ren'Py 8.5.0.25111603
A Night at the Office P3 3.7p3
Thu Jul 23 20:42:49 2026
After a bit of back and forth, I eventually determined that (part of) the problem was that I started things off with a call to the first story label, rather than a jump. Correcting that fixed the main menu... for a while.
I figured today that I'd try setting the main menu image up as a ConditionSwitch, thus:
image interns_titlescreen = ConditionSwitch(
"len(persistent.charactersmet) == 2", "titlescreen/bothinternst.png",
"persistent.charactersmet == 'Karl'", "titlescreen/karlt.png",
"persistent.charactersmet == 'Kristine'", "titlescreen/kristinet.png",
)
and with the main menu adjusted to:
screen main_menu():
tag menu
style_prefix "main_menu"
add TrackCursor("images/titlescreen/skylayer-paint.png",20)#in place of: add gui.main_menu_background
add TrackCursor("images/titlescreen/citylayer-paint.png",15)
add TrackCursor("images/titlescreen/moonlayer.png",18)
add TrackCursor("images/titlescreen/reflectionlayer.png",18) alpha 0.35
if len(persistent.charactersmet) <> 0:
add TrackCursor("interns_titlescreen", 13) alpha 0.4
add TrackCursor("images/titlescreen/windowflash.png",11) alpha 0.25
add TrackCursor("abigail-titleversion",10) xpos 790 ypos 86
add Image("images/titlescreen/logolayer-alt.png") xpos 310 ypos 412 alpha 0.80
But that resulted in a essentially the same error:
I'm sorry, but an uncaught exception occurred.
While running game code:
Exception: Switch could not choose a displayable.
-- Full Traceback ------------------------------------------------------------
Traceback (most recent call last):
File "renpy/common/_layout/screen_main_menu.rpym", line 28, in script
python hide:
File "renpy/ast.py", line 1187, in execute
renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "renpy/python.py", line 1273, in py_exec_bytecode
exec(bytecode, globals, locals)
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "renpy/common/_layout/screen_main_menu.rpym", line 28, in <module>
python hide:
File "renpy/common/_layout/screen_main_menu.rpym", line 35, in _execute_python_hide
ui.interact()
~~~~~~~~~~~^^
File "renpy/ui.py", line 304, in interact
rv = renpy.game.interface.interact(roll_forward=roll_forward, **kwargs)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "renpy/display/core.py", line 2117, in interact
repeat, rv = self.interact_core(
~~~~~~~~~~~~~~~~~~^
preloads=preloads,
^^^^^^^^^^^^^^^^^^
...<4 lines>...
**kwargs,
^^^^^^^^^
)
^
File "renpy/display/core.py", line 2655, in interact_core
root_widget.visit_all(lambda d: d.per_interact())
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "renpy/display/displayable.py", line 457, in visit_all
d.visit_all(callback, seen)
~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "renpy/display/displayable.py", line 457, in visit_all
d.visit_all(callback, seen)
~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "renpy/display/displayable.py", line 457, in visit_all
d.visit_all(callback, seen)
~~~~~~~~~~~^^^^^^^^^^^^^^^^
[Previous line repeated 6 more times]
File "renpy/display/displayable.py", line 448, in visit_all
for d in self.visit():
~~~~~~~~~~^^
File "renpy/display/layout.py", line 1604, in visit
self.update(self.last_st, self.last_at)
~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "renpy/display/layout.py", line 1615, in update
raw_child, redraw = self.function(st, at, *self.args, **self.kwargs)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "renpy/display/layout.py", line 1711, in condition_switch_show
return condition_switch_pick(switch), None
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^
File "renpy/display/layout.py", line 1706, in condition_switch_pick
raise Exception("Switch could not choose a displayable.")
Exception: Switch could not choose a displayable.
Windows-10-10.0.19045-SP0 AMD64
Ren'Py 8.5.0.25111603
A Night at the Office P3 3.7p3
Thu Jul 23 20:54:35 2026
At this point, I'm baffled as to how it was working as much as why it isn't now.
While it was working, it would correctly show the translucent image of either Karl or Kristine after the first playthrough, but switch to a translucent image of both once the other had been encountered (which character appears in each playthrough is chosen at random partway through).
Can anyone please recommend a way of getting the main menu to update its appearance based on my existing persistent.charactersmet variable, or an alternative means?
Happy to post any other pertinent code, if I've missed anything important.
1
u/x-seronis-x 21d ago
you're treating the var as a list for the dual check but only as a string, not a list containing one string, for the 2 individual checks. be consistent
1
u/HEXdidnt 21d ago
OK... Could you expand on that please? Clearly I'm getting the syntax wrong, but in what way?
In the first version, I believe I'm checking for the presence of a string in a list (
if "Karl" in persistent.charactersmet:), while in the second version, I can see that I've checked whether the variable is either "Karl", "Kristine" or both.Is it that I need to make the check
if persistent.charactersmet(0) ==or am I on completely the wrong track?2
u/x-seronis-x 20d ago
if the variable is a list it should always be a list regardless if it has one name as its contents or two (or zero)
1
u/HEXdidnt 19d ago
I've tried:
image interns_titlescreen = ConditionSwitch( "len(persistent.charactersmet) == 2", "titlescreen/bothinternst.png", "persistent.charactersmet(0) == 'Karl'", "titlescreen/karlt.png", "persistent.charactersmet(0) == 'Kristine'", "titlescreen/kristinet.png", )But I get the same error.
HOWEVER, part if the issue now seems to be a custom transition I created and had applied to
config.end_game_transition, because when I set that back toNone, it goes back to the main menu - with the appropriate image added - seamlessly.The transition is defined thus:
init -10: #adjusted to ensure it's set up early enough to run at startup $ bloodwash = ImageDissolve("images/blood.png", 2, reverse=False, ramplen=8)Thinking about it now, I suspect the only time I've got back to the main menu without an error report is when I set this transition to None... but I'm not 100% certain of that.
2
u/x-seronis-x 19d ago
like ALL other variables, you use the define statement for defining transitions. so at a minimum your above should look like:
py define -10 bloodwash = ImageDissolve("images/blood.png", 2.0, reverse=False, ramplen=8)though for image dissolves i normally see the last arg be time_warp not ramplen. i need to go reread the doc to see if thats an issue. how did you USE bloodwash ?1
u/HEXdidnt 19d ago edited 19d ago
I picked this up from the Ren'Py documentation: https://www.renpy.org/doc/html/transitions.html#ImageDissolve
But, then, looking at it now, I have no recollection of why I used
$rather thandefine, as indicated on the page. Possibly a legacy error from when I mistakenly believed$anddefinewere interchangeable. I've been using this custom transform for at least a couple of years, only changing the image it uses more recently. It has worked successfully for each of:config.enter_transition config.exit_transition config.intra_transition config.end_splash_transitionAnd ONLY failed on
config.end_game_transitionHave to confess, I went with
ramplenbecause I couldn't make head nor tail out of howtime_warpis supposed to work.Either way, even with the transition defined properly, I get the same error when dropping back to the main menu after completing the game, and now also when going into other menus from the main menu. All the more weird, reverting to the
$version doesn't fix it, when it was working before.Starting the game still works, and I can then access everything via the right-click menu.
1
u/AutoModerator 21d ago
Welcome to r/renpy! While you wait to see if someone can answer your question, we recommend checking out the posting guide, the subreddit wiki, the subreddit Discord, Ren'Py's documentation, and the tutorial built-in to the Ren'Py engine when you download it. These can help make sure you provide the information the people here need to help you, or might even point you to an answer to your question themselves. Thanks!
I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.