SetScreenVariable doesn't work in a "used" screen?

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
rames44
Veteran
Posts: 232
Joined: Sun May 29, 2016 4:38 pm
Contact:

SetScreenVariable doesn't work in a "used" screen?

#1 Post by rames44 » Tue Jun 26, 2018 1:11 pm

I'm using Renpy 7.0.0.196. I have a screen that I plan to re-use in several places that makes use of an internal screen variable, which changes based on actions the player takes and, in turn, allows the screen to alter itself. Using a SetScreenVariable action works just fine if I call the screen directly, but if I "use" the screen inside another and then call the "parent" screen, either (a) SetScreenVariable has no effect, or else (b) the screen doesn't update itself as a result the way it does if called directly.

Is this intended? Or am I doing something wrong?

Below is a greatly simplified, self-contained script that illustrates the issue. When the "child" screen is called directly, the SetScreenVariable actions update the "state" variable, causing the "ping" and "pong" buttons to alternate. However, when the "parent" screen is called (which "uses" the child screen), clicking on "ping" does not cause the "state" screen variable to change. (Or, if it does, the screen does not update to reflect that fact.)

Code: Select all

image bg = "#888"

label start:
    scene bg

    "First, calling the screen directly (press 'Return' when done)"

    call screen child

    "Now, calling the screen indirectly"

    call screen parent

    "Why the difference?"

    return

style button_text:
    color "#f00"
    hover_color "#ff0"

screen child():
    default state = 0

    text "State: [state]" align(0.0,0.0)
    textbutton "Return":
        align(1.0,0.0)
        action Return()

    if state == 0:
        textbutton "ping":
            align (0.25,0.5)
            action SetScreenVariable("state", 1)
    if state == 1:
        textbutton "pong":
            align (0.75,0.5)
            action SetScreenVariable("state", 0)

screen parent():
    text "Parent" align(0.5,0.0)
    use child
If I use a global variable and use SetVariable instead of SetScreenVariable, then both versions behave as expected, but that really isn't what I'd prefer to do.

User avatar
xavimat
Eileen-Class Veteran
Posts: 1458
Joined: Sat Feb 25, 2012 8:45 pm
Completed: Yeshua, Jesus Life, Cops&Robbers
Projects: Fear&Love, unknown
Organization: Pilgrim Creations
Github: xavi-mat
itch: pilgrimcreations
Location: Spain
Contact:

Re: SetScreenVariable doesn't work in a "used" screen?

#2 Post by xavimat » Tue Jun 26, 2018 2:03 pm

Try with the line "default state = 0" in the parent screen. And IIRC passing the variable in the use statement "use child (state)"
Comunidad Ren'Py en español: ¡Únete a nuestro Discord!
Rhaier Kingdom A Ren'Py Multiplayer Adventure Visual Novel.
Cops&Robbers A two-player experiment | Fear&Love Why can't we say I love you?
Honest Critique (Avatar made with Chibi Maker by ~gen8)

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: SetScreenVariable doesn't work in a "used" screen?

#3 Post by Remix » Wed Jun 27, 2018 9:37 am

SetScreenVariable uses renpy.current_screen() to get the zone/namespace so afaik there is no current way to 'easily' confine the variable to a child instance (in a used screen, renpy.current_screen() will return the parent rather than child)

Code: Select all

# highlight the action...
# show the relevant screen upon click

            action [SetScreenVariable("state", 1), 
                    Function(renpy.notify, renpy.current_screen() )  ]
Your best bet, depending on your use-case is likely to create a list of
default child_screen_vars = []
and reference the index (or pass as attribute) the one you want.

As the behaviour is counter-intuitive it might be worth adding as a git-bug-issue if RenpyTom doesn't spot this thread...
Frameworks & Scriptlets:

rames44
Veteran
Posts: 232
Joined: Sun May 29, 2016 4:38 pm
Contact:

Re: SetScreenVariable doesn't work in a "used" screen?

#4 Post by rames44 » Thu Jun 28, 2018 6:03 pm

After some experimentation, what @xavimat and @Remix said is correct.

To put it in my own vernacular, calls to SetScreenVariable and variable evaluation within a screen have different "visibility" rules.

Tests of variable values, such as:

Code: Select all

    if state == 1:
or

Code: Select all

    text "The current state is [state]"
behave as if the variable definition is scoped locally to the individual screen. Thus, if "state" is declared in the parent, it's not visible to the child and vice versa.

SetScreenVariable from the child, however, operates as if called in the parent. Thus, it can only affect screen variables declared in the top-most screen - it can't see or affect any variables that are declared only in the children.

So if a SetScreenVariable call is to affect a variable visible in the child:
1. The actual variable must be declared in the parent, and SetScreenVariable must use the name declared in the parent.
2. The parent must pass the variable to the child as a parameter. The name of the parameter within the child screen doesn't have to be the same as the name of the variable in the parent. The child's use of the variable will use the parameter name, not the name of the variable declared in the parent.

So, essentially, the parameter to the child allows both the child and the parent to have a reference to the same underlying object.

One presumes that this is because the parent screen and the child screen are, in fact, different Python objects, each with their own attributes, as opposed to the "use" statement literally merging in the child screen's code at compile time.

Once the rules are understood, all is good - it just wasn't obvious that SetScreenVariable isn't (for lack of a better term) called with the scope of the screen in which the action is declared.

Post Reply

Who is online

Users browsing this forum: span4ev