Can renpys screen object action SetVariable() change a variables that are not used in the screen the action it is called in.

Can renpy’s screen object action SetVariable() change a variables that are not used in the screen the action it is called in? i cannot find any documentation about how scopes of screens and their children work, from what i have seen the answer would be no, it also will not let a function that returns something to set any variable that is outside of the screen also, it seems. Although it will let a function read another variable. If anyone can provide insight or point me to some documentation other than this page here

https://www.renpy.org/doc/html/screen_actions.html#SetVariable

that would be helpfull!

Ok after mush more testing to my game which is a work in progress i have discovered a number of other cases where a variable used in a screen is, instead of using the global variable, has a local version initialized from the global, then the local is then modified by the screen not the global, and the local is displayed from then on. i have not noticed these other cases untill now becuse they did not need to be used outside the screen. So can anyone confirm that screens can only change local variables, but can read global ones.

I can not say for sure on my end as I usually call custom methods and classes myself from screens.

If I get some time I will try to do some digging on my side. If you had some test code you are willing to share that would help others research the issue with you.

Ok so to show you the basic problem here is a quick script file i made. if you start a new project in the renpy sdk and just replace the scripty.rpy with this one and lunch it should demonstrate the problem using up and down on the screen appears to change the variable dogs but if you open the console and check the dogs variable its still its initialized value, further opening the console causes the game to reload the script which then resets the screen local version back to the global. Point being here is the global dogs never changes.script.zip (377 Bytes)

I think if you use a screen to call to a section of the script with the variable changes, that would work. I remember doing something like that for one of my games before.

Open and view the short script that i linked in the post above that is what is happening. I am using renpys screen action for setting a variable. From my research and testing since that post, to the best of my understanding is that the renpy interpreter which predicatively executes code has multiple branches run at once and doesn’t always merge the variables back to the main line until after certain actions and knows which branch to actually use. The console throws a even bigger wrench into this as i have also learned because it pauses everything and does not merge first, which is why in my example when you check the variable dogs its wrong with whats on the screen. In short it does actualy change it just the timing is off with when you would expect it to change.

I think I may see what is going on here. So I modified your test scrip and it is actually working for me thus far. I can set dogs to any value I like and when I return from the screen dogs displays with the correct value. This value also persists through saves as expected. Below is the script I used for testing and for reference I used renpy 7.3.5.606 for testing.

init python:
    dogs = 5

screen test():
    hbox:
        xpos 100
        ypos 100
        fixed:
            xsize 100
            ysize 100
            align (.5,.5)
            text "[dogs]"
        vbox:
            align (.5,.5)
            button:
                xsize 100
                ysize 100
                align (.5,.5)
                text "up"
                action SetVariable("dogs", dogs + 1)
            button:
                xsize 100
                ysize 100
                align (.5,.5)
                text "down"
                action SetVariable("dogs", dogs - 1)
            button:
                xsize 100
                ysize 100
                align (1,1)
                text "quit"
                action Return()


label start:

    while True: #simply to keep the screen called for testing purposes
        "[dogs]"
        call screen test


    return

So as far as I can tell the value updates correctly during run time, but I did notice something that I think is the issue you are dealing with. If I was to set the value from 10 to say 20 and saved the game without returning from the screen, if I reloaded the game the value would be reset to 10. So this shifts the question to not why is the value not changing to why is renpy not saving the value on the screen and that is due to how renpy handles saving data.

Now a quick note, I think its been about a year since I deep dived into renpy’s code so take what I say with a grain of salt.

The way renpy saves data is not by saving the current game state, instead renpy saves “snapshots” of the game and the snapshots are not snapshots of the games full state but instead a collection of what values changed since the last call in the script. When renpy loads a save file, on a basic level, it actually replays the game for you from the start to where you saved to bring you back to your saved game state.

This is a very cleaver system and this is how renpy can allow you to look over your game history when you load a save or backtrack from your current point if that is enabled as it basically pops the changes off of a stack. As a quick side note this also means renpy likes its games to be deterministic.

Now what I think is going on is screens do not cause a new snapshot to be formed until they return. This makes a lot of sense in order to keep the amount of data that would need to be saved down, and I think that is what you are seeing. That or you are using a different version then me that may have a bug.

Oh yes i know you can make it work by making it merge by returning from the screen my example was written as such to point out a specific case where the screen was not returned or merged. But, that’s my point if i’m showing a complex screen with buttons while having dialog appear below and then that also uses a variable set in the screen, if the screen had not returned, or been forced to merge the variable could be seen wrong by the python engine. And it could display wrong. Which is the way more complex cases i was ruining into. The same as opening the console while the screen is still called. You forced the screen to “update” in effect. I have learned to code around this being careful about making screens return and re-show timing wise to prevent this.

Saving is a whole different beast in renpy, which is also effected by when the screens return and merge data, but a lot of the time if something doesn’t appear to effect the story branching renpy wont save it. Screens by my experience are not considered part of story branching. an example of a way to make renpy save a variable is a menu option changes a variable, that variable would then be seen to have been important to story branching. Another simpler one is to do a simple $ dogs += 1 in a label that gets called in the story, it would also then be seen as important to the story. In this case i think it is evaluating dogs to be inconsequential.

Note: this is all in hind site with a lot more knowledge of how it works at this point compared to when i first posted asking.

Ya I think I see what you are saying there. I would like to know a bit more about your “merge” case as the only time I experienced any issue with the value not being correctly set was when saving while in the screen and I am not sure if there is a runtime case you are referring to I am not aware of.

I think I see what you are saying though and if I understanding it correctly I believe this, unfortunately, is just how renpy works as opposed to some bug in the system. If you want to get around this snapshot system it uses under the hood to keep track of everything you have to either making changes to the engine or write your own system for handling it.