Screen Language #6

Screens with arguments, use and transclude statements

I believe that up until now, we've been coding screens that will always be the same whenever they're shown. We could change that by using some variables (Check out Basics #7 to learn about variables), true, but what if it's something insignificant that we want to change? Variables should hold meaningful things, how Characters look or what the player's choices were, we shouldn't store every little thing in a variable.

Before getting to arguments, today's first topic, let's create a simple screen called frameScreen to start with. Nothing fancy, just a frame in the middle of the screen, with some text centered inside of it. The screen gets called when we Start the project.

screen frameScreen():

    frame:
        align (0.5, 0.5)
        xysize (400, 200)

        text "I am inside the frame!":
            align (0.5, 0.5)

label start():

    call screen frameScreen

With the screen prepared, let's talk about argumentsWe can think of arguments as temporary variables, which are given a value every time the screen is shown. Let's demonstrate that with our screen.

screen frameScreen(textToShow):

    frame:
        align (0.5, 0.5)
        xysize (400, 200)

        text textToShow:
            align (0.5, 0.5)

label start():

    call screen frameScreen(textToShow = "I am inside the frame!")

First, we tell the screen to expect an argument, by writing the argument's name in the parentheses after the screen's name. In other words, whatever we write in, that's what our "temporary variable" will be named, so in my case, it's name is textToShow.

Next, we delete the string, "I am inside the frame!", from the screen's text statement, and we make it show whatever the value of textToShow is.

Finally, when calling the screen, we will include parentheses behind the screen's name once again. This let's us specify what the value of textToShow should be when the screen is shown, by writing the argument's name inside the parentheses, and following it up with an equal sign and the value

As it is the same exact string that was written inside the screen in the first code block, the screen will show the same exact text, making the result identical.

Now, arguments that are just named in the parentheses do not have a default value. This means, if we were to call the screen without giving the argument - with just call screen frameScreen - the argument textToShow would have no value, and Ren'Py would brought up an error.

To remedy this, follow up the argument's name inside the parentheses with an equal sign and the string. It writes the same way as when giving the argument when calling the screen. With the default value now set, the screen no longer minds being called without the argument, and doing so will once again produce the same result.

screen frameScreen(textToShow = "I am inside the frame!"):

    frame:
        align (0.5, 0.5)
        xysize (400, 200)

        text textToShow:
            align (0.5, 0.5)

label start():

    call screen frameScreen

As you might've thought, we can give screens multiple arguments, not just a single one. frameScreen now takes two arguments, so we'll pass them just how we did last time - argument's name, equal sign, value - separating them with a comma.

You can spread the arguments across multiple lines like I did here, but you don't have to. The main reason I did so here is that I wanted to keep the code thinner for the website :)

screen frameScreen(firstText, secondText):

    frame:
        align (0.5, 0.5)
        xysize (400, 200)

        text firstText:
            xalign 0.5

        text secondText:
            yalign 0.5

label start():

    call screen frameScreen(firstText = "I am centered horizontally!",
    secondText = "I am centered too, but vertically.")

And this is the result. Two texts displayed - first centered horizontally, second centered vertically - both inside a frame

Before we continue on with arguments, let me introduce to you a new statement. It's name is use. The use statement is essentially used to insert one screen into another screen.

To show it in practice, I'll create a second screen, textScreen, and use the use statement to insert it into frameScreen. Yet again, the result is the same.
Do note however, this screen isn't entirely the same code-wise - The text is not inside the frame but placed on top of it.

screen frameScreen():

    frame:
        align (0.5, 0.5)
        xysize (400, 200)

    use textScreen

screen textScreen():

    text "I am inside the frame!":
        align (0.5, 0.5)

label start():

    call screen frameScreen

I think that's a pretty simple concept.

Now, the use statement can take advantage of arguments, too. When using the use statement, you can pass values to screen's arguments in the same exact way we did in the second code block - by including parentheses and writing the argument's name and it's value inside.

screen frameScreen():

    frame:
        align (0.5, 0.5)
        xysize (400, 200)

    use textScreen(textToShow = "I am inside the frame!")

screen textScreen(textToShow):

    text textToShow:
        align (0.5, 0.5)

label start():

    call screen frameScreen

What the use statement combined with arguments allows us to do is re-use a screen multiple times. In the next code block, we have two use statements, both with different values given to textToShow. This means that now, there will be two texts on top of the frame instead of one.

screen frameScreen():

    frame:
        align (0.5, 0.5)
        xysize (400, 200)

    use textScreen(textToShow = "I am inside the frame!")
    use textScreen(textToShow = "I will make this confusing.")

screen textScreen(textToShow):

    text textToShow:
        align (0.5, 0.5)

label start():

    call screen frameScreen

Awkwardly, as textScreen is set to center the text, the two texts end up being placed on top of each other.

I have one more new statement to show you, named transclude. The name is intimidating, but using it is actually way simpler than it might seem at first sight.

By including the transclude statement in a screen that we intend to insert somewhere with the use statement, we will be able to make a code block for the use statement, and everything inside this block will be inserted inside the used screen. Let me demonstrate:

screen frameScreen():

    frame:
        align (0.5, 0.5)
        xysize (400, 200)

        transclude

screen screenWithTexts():

    use frameScreen:

        text "I am inside the frame!":
            xalign 0.5

        text "I no longer make things confusing.":
            yalign 0.5

label start():

    call screen screenWithTexts

Do notice that this time, the frameScreen is not the one being called, but the new screenWithTexts is. screenWithTexts takes frameScreen and places two texts inside of it - inside meaning in place of the transclude statement

As a result, the texts are inside the frame again, and can interact with it as such - for example, align relative to it, rather than to the whole screen.


Remember how we re-used a screen before? It gets so much better with transclude. This hasn't been a bad example - using transclude inside a frame. This way, we could prepare a background, and then place whatever we want on top of it, anywhere.

Final code block. A complex example combining everything that we've learned today, as well as things from the past. See if you can figure this one out, I'll include comments to make it easier. Copying it over to your project and seeing how it looks will help a lot.

# All sorts of colors for backgrounds.
define red = Solid("#800a0a")
define orange = Solid("#ff7605")
define yellow = Solid("#f3ff17")

screen frameScreen( framePosition ):

    # The bottom, red frame.
    frame:
        background red
        
        # At a given position.
        pos framePosition
        xysize (400, 220)

        # The smaller, orange frame.
        frame:
            background orange

            # Centered in the red frame.
            align (0.5, 0.5)
            xysize (370, 190)

            transclude

        # The yellow frame, just a decoration.
        frame:
            background yellow

            # Slightly off the right side.
            align (1.0, 0.5)
            xoffset -20
            xysize (60, 250)

screen mainScreen():

    # First frame, top left.
    use frameScreen( framePosition = (100, 100) ):

        text "I am inside the frame!":
            yalign 0.5
            color "#4a1c00"

    # Second frame, bottom middle.
    use frameScreen( framePosition = (400, 400) ):

        vbox:
            spacing 5

            yalign 0.5
            xoffset 30

            text "One"
            text "Two"
            text "Three"
            text "Four"
            text "What's behind"
            text "        the fridge door"

    # Third frame, top right.
    use frameScreen( framePosition = (700, 100) ):

        add "gui/window_icon.png":
            align (0.2, 0.5)


label start():

    call screen mainScreen

And that's done. Time for our usual summary of what we have learned today.

  • Giving screens arguments, both with and without a default value.
  • Passing an argument to a screen when calling it.
  • Passing multiple arguments.
  • Combining screens with the use statement.
  • Passing arguments to screens through the use statement.
  • Showing the same screen multiple times with different argument values.
  • Using transclude to insert things into a used screen.

And hopefully more from the final code block. If you understand all of it, congratulations, you've gotten very far. I'm proud of you, and I can't wait to teach you more.