Ren'Py Performance upgrades for "Choice" and "dissolve"

Playstorepers

Member
May 24, 2020
160
79
Hey everyone,

I have this issue with RenPy:

I have an image with:

Python:
image my_img:
    choice:
        "gui/img1.jpg" with dissolve
    choice:
        "gui/img2.jpg" with dissolve
    ..........
    choice:
        "gui/img200.jpg" with dissolve
    pause 10.0
    repeat
And I embedded into a menu screen.
Let's say for example the mainmenu screen:


Python:
screen main_menu():
    tag menu
    add "my_img"
    imagebutton auto "gui/button/newgame_%s.png":
        xalign 0.2
        yalign 0.5
        focus_mask True
        action Start()
    imagebutton auto "gui/button/othermenu_%s.png":
        xalign 0.8
        yalign 0.5
        focus_mask True
        action ShowMenu("preferences")
If I now mash click one of the buttons (newgame or preferences), while the main_menu screen dissolves into the game (while entering the main menu, I mean), everything gets laggy and the background image sometimes gets killed, meaning i just get a black screen in the background.
It works better, if I choose less choices than 200, but still... there must be something I can do for a better performance, right?

if it helps, i have these in my options:
define config.enter_transition = dissolve
define config.exit_transition = dissolve

i'm almost positive, that dissolve is causing the issue as well, but i like the dissolve effect.

Thanks in advance
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,364
15,281
It works better, if I choose less choices than 200, but still...
Wait, you're saying that your image have more than 200 different choice ? No wonder that it end lagging.


there must be something I can do for a better performance, right?
Of course:
Code:
screen main_menu():
    tag menu
    add ( "gui/img{}.jpg".format( renpy.random.randint( 1, 200 ) ) )
    [...]
 
  • Like
Reactions: Playstorepers

Playstorepers

Member
May 24, 2020
160
79
first of all: Thanks for the suggestion, it really helps.

second: is it possible to have a random image every 10 seconds + 1 first image, that's always fixed?

Something that works like this:


Python:
image my_img:
    "gui/img1.jpg" with dissolve
    pause 10.0
    block:
        choice:
            "gui/img2.jpg" with dissolve
        choice:
            "gui/img3.jpg" with dissolve
        ..........
        choice:
            "gui/img200.jpg" with dissolve
        pause 10.0
        repeat
I'm trying to come up with something, but I can't really implement timer stuff like the one i described in a screen with my current ren'py knowledge.
 

Playstorepers

Member
May 24, 2020
160
79
Wait, you're saying that your image have more than 200 different choice ? No wonder that it end lagging.




Of course:
Code:
screen main_menu():
    tag menu
    add ( "gui/img{}.jpg".format( renpy.random.randint( 1, 200 ) ) )
    [...]

Oh, and would it help with the perfomance, if i'd fucked around with it like this:

Python:
image xyz:
    choice:
        choice:
            choice:
                choice:
                    choice:
                        "image1.etc."
                    choice:
                        "image2.etc."
                choice:
                    choice:
                        "image3.etc."
                    choice:
                        "image4.etc."
                       
                ......
Effectively only having two choice objects at a time?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,364
15,281
Oh, and would it help with the perfomance, if i'd fucked around with it like this:
[...]
Effectively only having two choice objects at a time?
Don't really remember how choice works, but I highly doubt that it would change anything. At worse what you'll win would be lost by the number of consecutive choice Ren'py would have to make.

But anyway I gave you the solution. It fit in one line and would only add few micro seconds to the display of a regular image.
 

Playstorepers

Member
May 24, 2020
160
79
first of all: Thanks for the suggestion, it really helps.

second: is it possible to have a random image every 10 seconds + 1 first image, that's always fixed?

Something that works like this:


Python:
image my_img:
    "gui/img1.jpg" with dissolve
    pause 10.0
    block:
        choice:
            "gui/img2.jpg" with dissolve
        choice:
            "gui/img3.jpg" with dissolve
        ..........
        choice:
            "gui/img200.jpg" with dissolve
        pause 10.0
        repeat
I'm trying to come up with something, but I can't really implement timer stuff like the one i described in a screen with my current ren'py knowledge.

Call me crazy, but I don't see how I can apply this timer into your answer...
Maybe I'm just stupid though.

EDIT:
So after some research I saw, that I can implement timer in screens. However I am struggling to reset the timer every time I re-enter the main menu.

Code:
#From a very old code I found:
init python:

    import random
    pictrs = ["gui/img1.jpg", "gui/img2.jpg", ......]
    pictr = pictrs [0]
 
screen main_menu():
    add "pictr"
    timer 10.0 action SetScreenVariable("pictr", random.choice(pictrs)), With(dissolve) repeat True
It works well, it's just that if I leave the main menu and come back to it, the new image always flickers between a transition to the next instantly.
I would like to reset the timer at least, so there won't be any changes for the next 10 seconds every time I enter the main_menu.
Does anyone know, how I do that?

Thank you in advance.

EDIT 2:

Thank you again for your help, I did something very weird now, but hey as long as it works:

Code:
screen main_menu():
    tag menu
    default pictrs = ["gui/pic1.jpg","gui/pic2.jpg",.........]
    default pictr = pictrs[0]
  
    add pictr
  
    timer 10.0 action SetScreenVariable("pictr", random.choice(pictrs)), With(dissolve) repeat True
  
    ....
I'm positive, it's possible to do it more elegantly, but it works and it doesn't lag anymore, I'm happy.

Thanks again for your help anne.

EDIT 3:
Well shit...
Does anyone know, why Ren'Py random doesn't change, if called upon a second time?
That's the problem, that I'm struggling with rn:

Python:
default pctrs = ["pic1.jpg", "pic2.jpg",........, "pic200.jpg"]

#followed by:

image mmpctr:
    pctrs[0]
    pause 7.0
    block:
        pctrs[renpy.random.randint (0, 199)] with dissolve
        pause 7.0
        repeat
I checked it: Ren'Py runs the repeat loop, however the renpy.random.randint(0,199) never gets reshuffled in the repeat loop.
Anyone have an idea, how to give it another roll every time the loop runs past?
 
Last edited:

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,364
15,281
I checked it: Ren'Py runs the repeat loop, however the renpy.random.randint(0,199) never gets reshuffled in the repeat loop.
I assume that it's because Ren'py rewrite few parts of the random module for it to be rollback compliant.


Anyone have an idea, how to give it another roll every time the loop runs past?
Use directly the original module:
Code:
init python:
    import random

image mmpctr:
    pctrs[0]
    pause 7.0
    block:
        pctrs[random.randint (0, 199)] with dissolve
        pause 7.0
        repeat
 

Playstorepers

Member
May 24, 2020
160
79
I assume that it's because Ren'py rewrite few parts of the random module for it to be rollback compliant.




Use directly the original module:
Code:
init python:
    import random

image mmpctr:
    pctrs[0]
    pause 7.0
    block:
        pctrs[random.randint (0, 199)] with dissolve
        pause 7.0
        repeat
That was the first thing, that I tried. Still didn't work, but thanks anyways.

According to the internet, it's a result of the fact, that ATL (i mean image) can't be modified once its generated (Source: ). Later in this thread, you can also see, that random.randint doesn't work either. And I tested it myself, as mentioned above.

However it should be changeable with a function, but I have no idea, how to create this function, since I never created functions, especially not in atl.

The best I could do, was scavenge the internet for a working substitution which lacks the dissolve. And I have no clue how to add dissolve to it:

Python:
init python:
#Code that changes the picture, while making sure not to pick the same
    import random
    prev_image = None
    def show_stuff(st, at):
        global prev_image
        global counter
        next_image = prev_image
        # keep selecting image until it isn't a duplicate
        while next_image == prev_image:
            next_image = renpy.random.choice(pctrs)# Either here or below, I have to add a dissolve
        prev_image = next_image
        return (next_image, 7.0)# Either here or above I have to add a dissolve
       
#And then:
image pctr:
    pctrs[0]
    pause 7.0
    DynamicDisplayable(show_stuff) with dissolve#This dissolve only works for the first time
Any help would be very much appreciated. It's such a "simple" problem, but the rabbit hole goes deep...

Thank you in advance
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,364
15,281
According to the internet, it's a result of the fact, that ATL (i mean image) can't be modified once its generated
Oh, yeah, forgot about this ; sorry, rough week at works.
This being said, it don't mean that ATL are really frozen. It can be dynamic but the change will only be visible each time a new interaction start:
Python:
init python:
    def randomizedImage( trans, st, at ):
        store.randImg = "path/to/image/imageBase{}.jpg".format( renpy.random.randint(1, 10 ) )
        #  Ren'py will call again the function in 1.0 second.
        return 1.0

define randImg = "path/to/image/imageBase1.jpg"

image myATL:
    #  Fill perform text interpolation each time it display the image.
    # Therefore the effectively show image will change each time.
    "[randImg]"
    #  Randomly choose the next image.
    #  Also act as "pause 1.0" and "repeat".
    function randomizedImage
But, as I said, the image will only change each time there's a new interaction, what significantly reduce the interest of the method.

This being said:
Code:
init python:
    def pctrsRandomized( trans, st, at ):
        store.pictr = "pic{}.jpg".format( renpy.random.randint(1, 200 ) )
        return None

define pctrs = ["pic1.jpg", "pic2.jpg",........, "pic200.jpg"]
default pictr = "pic1.jpg"

image mmpctr:
     function pctrsRandomized
      "[pictr]"


screen main_menu():

    timer 7.0 repeat True action Hide( "main_menu" ), Show( "main_menu" )
    add "[mmpctr]"
    [...]
Would update the image every 7 seconds.

If your initial attempt with screen and add was having a problem, it's because you used a screen variable that was reset to its default value each time the screen was displayed. But using an external variable solve this issue, since each time the screen is displayed, it will start with the previous image, instead of the default image.
Plus, in the code above, the ATL image perform the randomization before the image is displayed, ensuring that it will be a new one ; minus the really few cases where the random value will match the previous one.

You could also have solved it by randomly defining the said default image : default pictr = random.choice(pictrs)


However it should be changeable with a function, [...]
No, the function only apply to the transform values (position, size, effect, etc.) but it don't change the image itself.
There's still the possibility to have something like:
Code:
init python:
    def randomizedImage():
        return renpy.random.choice( listOfImage )

image myImage:
    randomizedImage()
But there's more limitations than the version using the Function ATL statement.


Edit:
A fucking big typo in the image definition.
Sorry about this, was thinking about the function approach, but used the custom approach one :(
 
Last edited:
  • Like
Reactions: Playstorepers

Playstorepers

Member
May 24, 2020
160
79
Oh, yeah, forgot about this ; sorry, rough week at works.
This being said, it don't mean that ATL are really frozen. It can be dynamic but the change will only be visible each time a new interaction start:
Python:
init python:
    def randomizedImage( trans, st, at ):
        store.randImg = "path/to/image/imageBase{}.jpg".format( renpy.random.randint(1, 10 ) )
        #  Ren'py will call again the function in 1.0 second.
        return 1.0

define randImg = "path/to/image/imageBase1.jpg"

image myATL:
    #  Fill perform text interpolation each time it display the image.
    # Therefore the effectively show image will change each time.
    "[randImg]"
    #  Randomly choose the next image.
    #  Also act as "pause 1.0" and "repeat".
    randomizedImage()
But, as I said, the image will only change each time there's a new interaction, what significantly reduce the interest of the method.

This being said:
Code:
init python:
    def pctrsRandomized( trans, st, at ):
        store.pictr = "pic{}.jpg".format( renpy.random.randint(1, 200 ) )
        return None

define pctrs = ["pic1.jpg", "pic2.jpg",........, "pic200.jpg"]
default pictr = "pic1.jpg"

image mmpctr:
     pctrsRandomized()
      "[pictr]"


screen main_menu():

    timer 7.0 repeat True action Hide( "main_menu" ), Show( "main_menu" )
    add "[mmpctr]"
    [...]
Would update the image every 7 seconds.

If your initial attempt with screen and add was having a problem, it's because you used a screen variable that was reset to its default value each time the screen was displayed. But using an external variable solve this issue, since each time the screen is displayed, it will start with the previous image, instead of the default image.
Plus, in the code above, the ATL image perform the randomization before the image is displayed, ensuring that it will be a new one ; minus the really few cases where the random value will match the previous one.

You could also have solved it by randomly defining the said default image : default pictr = random.choice(pictrs)




No, the function only apply to the transform values (position, size, effect, etc.) but it don't change the image itself.
There's still the possibility to have something like:
Code:
init python:
    def randomizedImage():
        return renpy.random.choice( listOfImage )

image myImage:
    randomizedImage()
But there's more limitations than the version using the Function ATL statement.
Dude. There is absolutely no reason to apologize. You're writing a whole tutorial, every time and your knowledge is really appreciated.

timer 7.0 repeat True action Hide( "main_menu" ), Show( "main_menu" )

That was my second idea and I even tried something similar (look post #6 edit 2), but I decided to change it, since I wanted to have the dissolve feature with the images. I could easily add dissolve in the main_menu for example, however, once you add the dissolve feature into a timer into a screen, it eats up your input during the dissolve (leftclick makes you "skip the dissolve" instead of actually pressing the buttons).

So I was kinda forced to stop using timer in the screen or give up the dissolve.

Your explanations are really incredible though. I do understand how Ren'Py works better and I do understand, why some approaches of mine won't work.

Function being limited to the display properties and not the image itself is heart-breaking, though. It was a nice workaround idea.

Right now, I went with the shitty workaround of having 30 pics randomized with random.randint and then looping around (nobody does notice the difference, cuz it will loop once every 300sec/5 minutes, which is honestly long enough for players to forget the first pic), but it's kinda frustrating, that such a "simple" problem can't be solved that easily.

Thanks anyways for everything and for helping me with my issues.