Ren'Py Variable Transition Speed

Mimir's Lab

Member
Game Developer
Sep 30, 2019
225
980
Hey guys, wonder if you can help me. I used to be a decent beginner programmer so I only sorta know how to program (lol). The VN I'm working on has a couple thousand images and I make heavy use of transitions but I fear the transitions might get on the nerves of some people. So the solution I thought up was to create a Transition Speed slider in preferences and it works for the most part:

Python:
## Extra code for Preferences screen goes here
##
label _("Transition Speed")
bar value FieldValue(persistent, 'transition_speed', 2.0, max_is_zero=False, offset=0, step=.2) xmaximum 525 alt "Transition Speed"

## End Preferences screen code

## Create custom transition to use slider speed
define dissolveM = Dissolve(0.5 * persistent.transition_speed)
default persistent.transition_speed = 1.0

## How I call the custom transition in the script
scene s001_i001 with dissolveM
I can change and retrieve the value of the slider no problem. The problem I'm running into is that the Dissolve object is being created at the start of the program and the dissolve duration is being taken from what the value of persistent.transition_speed is at the start of the program. This means if someone were to change the slider in preferences for Transition Speed, it would only take effect once the app is restarted. I want the changes to take place instantaneously.

The solution I thought I had was to use an action, since FieldValue supports an action call, to create a new Dissolve object for dissolveM:
Python:
label _("Transition Speed")
bar value FieldValue(persistent, 'transition_speed', 2.0, max_is_zero=False, offset=0, step=.2, action= SetVariable("dissolveM", Dissolve(0.5 * persistent.transition_speed))) xmaximum 525 alt "Transition Speed"
The problem with this code is that I'm getting all sorts of weirdness going on because I think the action is being called before the value for persistent.transition_speed is changed. Not only that, I'm not even sure how many times it's calling the action, whether the action is only called when the slider has stopped moving or whether an action is called for every frame that the slider is moved. Another possible solution I thought of is to have every dissolveM call check to see if a flag is set for transition speed being changed, if so, create a new Dissolve object with the new transition_speed value. But I don't quite understand the crisscross of Renpy and python so I don't know how to create a function for that. Is there a solution I'm missing here?
 
Apr 24, 2020
192
257
I tried playing around with it, but came to the same conclusion as you. Something feels borked.

Instead of just changing the transition as an action directly, I instead used a function to do the same, with the addition of printing the value to the console.
From what I can tell, the persistent.transition_speed changes seem to happen as you would expect, but somehow the transition doesn't change. However, if I call the same function through a label or the console, then the transition gets updated.
 
  • Like
Reactions: Mimir's Lab

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,548
4,635
I don't have an explicit answer or knowledge, but to maintain what limited performance it has, Renp'y tries to cache stuff as much as possible.

Could you write the slider so that when the value is changed it calls to a python function that both sets the persistent value and overwrites the dissolveM object with an updated Dissolve instance?
 
Apr 24, 2020
192
257
Could you write the slider so that when the value is changed it calls to a python function that both sets the persistent value and overwrites the dissolveM object with an updated Dissolve instance?
That's what I did, but it didn't work. When the slider calls the function the value is changed, but the transition never updates. However you can just call the function through the console and as part of a label and it will work just fine.
 
  • Like
Reactions: osanaiko

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,548
4,635
So the (image with the transition attached) is what is cached.

Bummer.

This is a gnarley enough question it might make sense to reach out to pytom.
 

Niv-Mizzet the Firemind

Active Member
Mar 15, 2020
574
1,120
Try putting the change in a label, like this:
Python:
label change_dissolve:
    dissolveM = Dissolve(0.5 * persistent.transition_speed)
    return
Python:
label _("Transition Speed")
bar value FieldValue(persistent, 'transition_speed', 2.0, max_is_zero=False, offset=0, step=.2, action Call("change_dissolve")) xmaximum 525 alt "Transition Speed"
It should be called whenever the value changes, according to the wiki.
 

Mimir's Lab

Member
Game Developer
Sep 30, 2019
225
980
Try putting the change in a label, like this:
Python:
label change_dissolve:
    dissolveM = Dissolve(0.5 * persistent.transition_speed)
    return
Python:
label _("Transition Speed")
bar value FieldValue(persistent, 'transition_speed', 2.0, max_is_zero=False, offset=0, step=.2, action Call("change_dissolve")) xmaximum 525 alt "Transition Speed"
It should be called whenever the value changes, according to the wiki.
I tried that but when the label is called, it instantly throws the user out of the preferences screen and back into the game. Weird.

Also, is there a way to keep the variable persistent.transition_speed independent of rollbacks? When I try rolling back after changing the slider in preferences, it does all sorts of wonky stuff with transition speed.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,971
16,228
Try putting the change in a label, like this:
Keeping aside the fact that the label would then be called every time the slider move, why assigning the value in a label when it can be done directly from the screen ?

Python:
label _("Transition Speed")
bar value FieldValue(persistent, 'transition_speed', 2.0, max_is_zero=False, offset=0, step=.2, 
           # Here's the change
           action SetVariable( "dissolveM", Dissolve(0.5 * persistent.transition_speed) )
           xmaximum 525 alt "Transition Speed"
 

Mimir's Lab

Member
Game Developer
Sep 30, 2019
225
980
Keeping aside the fact that the label would then be called every time the slider move, why assigning the value in a label when it can be done directly from the screen ?

Python:
label _("Transition Speed")
bar value FieldValue(persistent, 'transition_speed', 2.0, max_is_zero=False, offset=0, step=.2,
           # Here's the change
           action SetVariable( "dissolveM", Dissolve(0.5 * persistent.transition_speed) )
           xmaximum 525 alt "Transition Speed"
Is there a difference between this and what I posted in OP besides the lack of an '='? Because I'm getting really weird results when changing the slider. The transitions definitely don't feel like what they're set to on the slider.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,971
16,228
Is there a difference between this and what I posted in OP [...]
Oops, sorry. Surely noticed that it was what you already tried, but clearly forgot it the time I got to the message talking about the label :(


Because I'm getting really weird results when changing the slider.
You shouldn't, unless...

Ok, yeah, got it. Should have slept before answering (it's 1PM here).

The problem is that SetVariable( "dissolveM", Dissolve(0.5 * persistent.transition_speed) ) mean:
give to "dissolveM" the value of "Dissolve( 0.5 * persistent.transition_speed)" that was created the last time the screen have been refreshed. What is absolutely not what you want.

To achieve what you want you need to split things, in order to generate the Dissolve object when the value is assigned, and only at this moment.
A solution would looks like SetVariable( "dissolveM", Function( Dissolve, 0.5 * persistent.transition_speed ) ), but even this wouldn't do it, because the "0.5 * persistent.transition_speed" would still be generated only at refresh time.

This only let Function as possible option, what isn't necessarily a bad thing, since it simplify the action:
Code:
init python:
    def generateDissolve():
        store.dissolveM= Dissolve( 0.5 * persistent.transition_speed )


screen Preferences():
[...]
  bar value FieldValue( [...] action=Function( generateDissolve ) ) [...]
And this time the Dissolve object should be created in real time, each time the generateDissolve function will be called. What is exactly what you are looking for.


Edit: Typo
 
Last edited:
Apr 24, 2020
192
257
And this time the Dissolve object should be created in real time, each time the generateDissolve function will be called. What is exactly what you are looking for.
Seems like what I tried to do, but just for clarity here's the simple function I called:
Python:
init python:
    def UpdateDis():
        global dissolveM
        print("Called with value: %f"%(persistent.transition_speed))
        dissolveM = Dissolve(0.5 * persistent.transition_speed)
 

Mimir's Lab

Member
Game Developer
Sep 30, 2019
225
980
So I tried the solution and almost everything works as I want, however, when you rollback with mouse wheel after changing transition speed, it returns to what the transition speed was before you changed the slider. Is there a way to prevent renpy from tracking the variable between rollbacks so that it remains at the speed I set it?
 
Apr 24, 2020
192
257
I found a solution... but it feels really dirty.

says that you can assign a class to be unaffected by rollbacks, so the simple solution was simply to pack your dissolve into such a class.
Here's the code:
Python:
define perma = ZeroRollback()

init -10 python:
    def generateDissolve():
        store.perma.dissolveM = Dissolve( 0.5 * persistent.transition_speed )

    class ZeroRollback(NoRollback):
        dissolveM = Dissolve( 0.5 * persistent.transition_speed )
Obviously you will now need to use perma.dissolveM for the transition, or whatever you decide to rename the things.
 
  • Like
Reactions: osanaiko
Apr 24, 2020
192
257
Um...

After posting my previous answer, it got me thinking about anne O'nymous's previous solution, which was:
Python:
init python:
    def generateDissolve():
        return Dissolve( 0.5 * persistent.transition_speed )
Why aren't we just using that function for the transition, rather than trying to fight renpy and figuring out how to store it? Sure it will mean that we are remaking it every time a transition is called, but something else is wrong with the game if we are running into performance issues.

So the simple solution would be to just do this:
Python:
show eileen happy with generateDissolve()