Ren'Py Screen refuse to hide with delayed hidding

-Awake-

Apocalypse Lovers
Game Developer
Aug 16, 2021
88
458
Hello,
I have a bug I'm trying to fix myself unsuccessfully so I was wondering maybe someone has already encounter that bug.

The bug is that an annoying screen is often refusing to hide on its own (not all the time). On the other hand, this only happens when the player is in "fast forward" (holding the "ctrl" key or having pressed the "tab" key by default in Renpy).

When the game passes very quickly on this screen (objective_discovered), it often happens to remain displayed indefinitely and I do not understand why.
(the picture ObjectiveNotify_Discovered_A.png). Here is the part of the code most likely to produce this bug :

screenshot0001.png


Code:
screen objective_discovered:
    add '0-0 Objective_discovered' at _notify_objectives
    timer 4.0 action Hide('objective_discovered')

screen objective_updated:
    add 'gui/ObjectiveNotify_Updated.png' at _notify_objectives
    timer 3.5 action Hide('objective_updated')

screen objective_accomplished:
    add '0-0 Objective_accomplished' at _notify_objectives
    timer 4.0 action Hide('objective_accomplished')
   

transform _notify_objectives:
    on show:
        xpos 0.45
        ypos 0.00
        alpha 0.5
        zoom 0.20
        linear 0.5 ypos 0.02 alpha 1.0 zoom 0.75 xpos 0.35

    on hide:
        linear 1.0 zoom 0.00 ypos 0.00 alpha 0.0 xpos 0.45
And here is an other screen producting the same strange behavors (exactly the same bug):

screenshot0002.png


Code:
screen notify_custom(message, img = None):
    zorder 100
    style_prefix "notify"
    frame at notify_appear:
        has hbox
        if img:
            add img yalign 0.5
        text "[message!t]" yalign 0.5
    timer 3.3 action Hide('notify_custom')



transform notify_appear:
    on show:
        alpha 0
        linear .25 alpha 1.0
    on hide:
        linear .5 alpha 0.0
So if anyone here have any idea from where this strange behavour is comming from don't hesitate to give me an hint !

Thank you!
 

Deleted member 2282952

Developing I SCREAM
Game Developer
May 1, 2020
416
868
Pretty interesting problem.

Hard to say without replicating the issue, but my guess would be that when the players fast forward, you occasionally reach a point when another popup with a timer appears - and Ren'Py might not support this.

Try adding a function to hide the previous item with a timer when a new timer is called. You can have a bool variable like Previous_Timer = True/False.

Might be off-base
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,141
14,825
The bug is that an annoying screen is often refusing to hide on its own (not all the time). On the other hand, this only happens when the player is in "fast forward" (holding the "ctrl" key or having pressed the "tab" key by default in Renpy).
It's a long time punctual bug that exist since years and have never really be identified.


[...] but my guess would be that when the players fast forward, you occasionally reach a point when another popup with a timer appears - and Ren'Py might not support this.
Ren'Py do not really care about this. Timers are in fact pure displayable, therefore they are called each time an event happen, like for buttons are by example. It's just that they are coded to not react outside of their time range.

I'll pass on the details, but my guess is that it happen when a screen is shown the instant it's previous iteration is hidden due to its timer.
Globally speaking, when a screen is shown, Ren'Py add it to some lists. And when the screen is hidden, it remove it from those lists.
Normally, an event happen at the start of an interaction, or at the end of the previous, it's the same thing in the end, so I never really cared to look further. Therefore the timer is triggered, the screen hidden, then Ren'Py proceed the code and show the screen as asked.
But due to the fast forward, it's possible that the two happen in the exact same time, meaning an "add it"/"remove it" conflict. Therefore, instead of the usual :
  • previous iteration removed from "display list" due to its timer
  • previous iteration removed from "event list" due to its timer
  • new iteration added to "display list" because shown
  • new iteration added to "event list" because shown
it's something like this that happen:
  • previous iteration removed from "display list"
  • new iteration added to "display list"
  • new iteration added to "event list" / or nothing happen because the screen is already in the list
  • previous iteration removed from "event list"
And like it's the same screen, it end being in the list of screens that have to be displayed, but not in the list of screens that need to be proceeded when an event happen. Therefore the timer is never called and can't act as expected.

After, to what extend it create an orphan iteration of the screen, I'm not totally sure. Ren'Py internals are sometimes really hard to decypher.


Try adding a function to hide the previous item with a timer when a new timer is called. You can have a bool variable like Previous_Timer = True/False.
Having a watchdog can be a good idea yes. But it's something really difficult to handle, because you need to catch when the screen is shown, and define a timer starting this moment.
 

Deleted member 2282952

Developing I SCREAM
Game Developer
May 1, 2020
416
868
It's a long time punctual bug that exist since years and have never really be identified.

Ren'Py do not really care about this. Timers are in fact pure displayable, therefore they are called each time an event happen, like for buttons are by example. It's just that they are coded to not react outside of their time range.

I'll pass on the details, but my guess is that it happen when a screen is shown the instant it's previous iteration is hidden due to its timer.
Globally speaking, when a screen is shown, Ren'Py add it to some lists. And when the screen is hidden, it remove it from those lists.
Huh, that's a cool info

Having a watchdog can be a good idea yes. But it's something really difficult to handle, because you need to catch when the screen is shown, and define a timer starting this moment.
Yeah, introduce a bunch of variables to pinpoint the issue, and keep randomly adding conditionals until something works - that's my 'advanced' approach.
 

-Awake-

Apocalypse Lovers
Game Developer
Aug 16, 2021
88
458
Hey thanks you guys for your fast answer, gonna try that solutions right away !
 

-Awake-

Apocalypse Lovers
Game Developer
Aug 16, 2021
88
458
Hey guys I've tried some stuff you've recommand since last time but I was unsuccefull. Moreover I'm afraid to mess up my code too much and don't want to modify 30000+ lines of code so any of you have a concret idea how could I do it by modifying this part only :


Python:
screen objective_discovered:
    add '0-0 Objective_discovered' at _notify_objectives
    timer 4.0 action Hide('objective_discovered')

screen objective_updated:
    add 'gui/ObjectiveNotify_Updated.png' at _notify_objectives
    timer 3.5 action Hide('objective_updated')

screen objective_accomplished:
    add '0-0 Objective_accomplished' at _notify_objectives
    timer 4.0 action Hide('objective_accomplished')
  

transform _notify_objectives:
    on show:
        xpos 0.45
        ypos 0.00
        alpha 0.5
        zoom 0.20
        linear 0.5 ypos 0.02 alpha 1.0 zoom 0.75 xpos 0.35

    on hide:
        linear 1.0 zoom 0.00 ypos 0.00 alpha 0.0 xpos 0.45
Or this part :

Python:
screen notify_custom(message, img = None):
    zorder 100
    style_prefix "notify"
    frame at notify_appear:
        has hbox
        if img:
            add img yalign 0.5
        text "[message!t]" yalign 0.5
    timer 3.3 action Hide('notify_custom')



transform notify_appear:
    on show:
        alpha 0
        linear .25 alpha 1.0
    on hide:
        linear .5 alpha 0.0
Thank you really much !
 

Bibifoc

Engaged Member
Apr 7, 2018
2,140
4,386
You should add your custom notif screen to the config.context_copy_remove_screens variable (which contains the usual notify screen by default).
Python:
init:
    $ config.context_copy_remove_screens.append("notify_custom")
 

-Awake-

Apocalypse Lovers
Game Developer
Aug 16, 2021
88
458
Hey thank you gonna try that right away. And do you think it would be a problem if I extend this solution to other screens?

Something like that :

Python:
init:
    $ config.context_copy_remove_screens.append("objective_discovered")
    $ config.context_copy_remove_screens.append("objective_updated")
    $ config.context_copy_remove_screens.append("objective_accomplished")
For that screen too :
Python:
screen objective_discovered:
    add '0-0 Objective_discovered' at _notify_objectives
    timer 4.0 action Hide('objective_discovered')

screen objective_updated:
    add 'gui/ObjectiveNotify_Updated.png' at _notify_objectives
    timer 3.5 action Hide('objective_updated')

screen objective_accomplished:
    add '0-0 Objective_accomplished' at _notify_objectives
    timer 4.0 action Hide('objective_accomplished')


transform _notify_objectives:
    on show:
        xpos 0.45
        ypos 0.00
        alpha 0.5
        zoom 0.20
        linear 0.5 ypos 0.02 alpha 1.0 zoom 0.75 xpos 0.35

    on hide:
        linear 1.0 zoom 0.00 ypos 0.00 alpha 0.0 xpos 0.45
 

-Awake-

Apocalypse Lovers
Game Developer
Aug 16, 2021
88
458
I've test it a couple of time. It's seems to be working! Then I've done an entire playthrough in fast forward and no screen has been stuck. Thank you really much !
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,141
14,825
I think all screens that end up hidden after a delay must be included in this list.
I agree, but it's a (relatively) too recent addition (version 7.3.3). Half the games actually in progress are based on an older version of Ren'Py that don't offer that possibility.