Basics of Ren'Py #7

Variables, interpolation and if/elif/else branches

Welcome to the seventh tutorial in the Basics of Ren'Py series! It's so great to keep doing these.

This time, a topic that I think is long overdue. In Part #5 of this series, we learned about menus, which let us branch a label into multiple paths, before joining the paths together and continuing with the story. A lot of times however, we'll want to remember the choice so that it can be brought up later, instead of branching the story straight after a menu

This is done through variables. While not mandatory, all variables should be declared before we use them inside labels. And there are two ways how to declare variables in Ren'Py - either the define statement or the default statement.

First, let's show an example of the latter, the define statement.

define myFavoriteFruit = "Oranges"

label start():

    "I bet you're wondering what my favorite fruit is."

    "Alright. [myFavoriteFruit]. I'm low-key addicted to orange juice."

    return

The define statement is followed by a variable name of our choice, then an equal sign, then the value of the variable. In my case, the variable is called myFavoriteFruit, and it's value is a string (text), "Oranges".

Another new thing (unless you've read the first part of my Ren'Py Python #1 series) is string interpolation. A scary name for a very simple feature - Do you see our variable name inside the square brackets in the second dialogue line? That's what's called string interpolation - It will be replaced with the value of the variable. Simple, no?

Now, the default statement. It is very similar, so similar in fact, that we can straight up just change the statement to default...

default myFavoriteFruit = "Oranges"

label start():

    "I bet you're wondering what my favorite fruit is."

    "Alright. [myFavoriteFruit]. I'm low-key addicted to orange juice."

    return

..and it will function in the exact same way as the version with define did.

So, what's the difference? It's a big one, one often overlooked, one that should absolutely not be overlooked. Yet, it's a very simple one.

Values of variables can be changed. We can prepare a variable named princessRescued with a value of None. Once the story gets to the dragon, it can be changed appropriately to True or False, and that can be brought up back in the castle, when the prince returns from his quest. 

Knowing this, here's the difference:

  • Variables declared with the define statement should NOT be ever changed. They're to be used when declaring Characters, images or other displayables, audio tracks or similar things that will always be the same. Variables that never change are properly called constants, but you barely ever see that in the Ren'py Docs.
  • Variables declared with the default statement CAN be changed to our heart's desire. These are used to register player choices, keep track of affection points with love interests, count down days to player's demise and such.

This is because, simply put, definevariables are not kept in save files, while defaulted variables are.


First, let's show an example of a good use for a defaulted variable. This one has a menu statement which leads us to a variable change. After that, the story continues, and the variable is brought up at the end to determine the ending.

This actually looks like a real game! We've really come far. 

define king = Character("King Thomas of Ren'Pynia")
define princess = Character("Princess Lucy of Ren'Pynia")
define knight = Character("You, a Knight of Ren'Pynia")

default princessRescued = None

label start():

    king "Princess Lucy has been taken by a dragon."
    knight "I shall go and rescue her!"

    "You've arrived at the dragon's tower."

    princess "My hero, watch out!"

    "The dragon suddenly appears behind you!"

    menu:
        knight "What should I do?"

        "Dodge to the left!":

            "You dodge to the left. The dragon turns to face you."
            "While turning, it accidentally hits the princess with it's tail."
            "You scare the dragon away, but the princess is gone."

            $ princessRescued = False

        "Swing your sword!":

            "You swing your sword."
            "The dragon gets scared and runs away!"
            princess "You saved me!"

            $ princessRescued = True

    knight "Alright, I think it's time to head back."
    "You turn away from the tower and venture back to the castle."

    king "My knight, you have returned!"
    king "How did your quest go?"

    if princessRescued:

        princess "Father!"
        king "My daughter, you're back!"
        king "You shall be rewarded handsomely, my knight!"
        knight "Thank you!"

        "(Good ending.)"

    else:

        knight "I tried my best, but..."
        king "Lucy is gone? No..."
        king "Leave. I want to be alone."

        "(Bad ending.)"

    return

It's a very long code, but a lot of it is familiar to us. At the very top, there is default and define used appropriately - define for the values that don't change, like the Characters, while default prepares the variable used in the story.

When the game starts, we encounter dialogue, followed by a menu. Following dialogue is determined by the player's choice, as well as whether the princess is rescued or not (Hitting her with it's tail? I know, I know. I suck at making up examples, okay??). Don't forget that since changing a variable is a python thing, we need to include a $ sign at the beginning of the line! 

Finally, the variable is brought up when you, the Knight of Ren'Pynia, return back to your king, and an ending it shown, based on what the player has selected. By "brought up", I mean that there's an if/else block.
We haven't encountered neither if nor else statements, but they're pretty easy to understand - if the variable following the if statement is True, the code block of the if statement plays out. Otherwise, the code block of the else statement plays out.

And of course, the return statement at the end, which will get us back to the main menu.


Even though if statements are easy to understand, I'll show you some more examples of how they can be used, to check for strings or numbers!
Quick note though: I'll declare the variables with default, since the name implies where they could be found in-game and what they'd track, even though I don't have any code present here to change the variables, so using define would theoretically be fine.

default visitedTheCastle = False

label start():

    if visitedTheCastle:
        "I've already been to the castle."

    else:
        "I haven't been to the castle yet."

    if not visitedTheCastle:
        "I haven't been to the castle yet."

    else:
        "I've already been to the castle."

    return

In the first example we see two if/else blocks. While the first one uses the same exact logic as the previous example does, where visitedTheCastle is False and thus the else block plays out, the second one has the not operator. Operator is just a fancy name, you can think of it as an another statement or a keyword.

not operators invert True values to False and False values to True. Meaning, not visitedTheCastle is read as not False, which is then inverted to become True. And since the condition of the if came out as True, this time, the if block plays out, and the else block is skipped.

default kingdomName = "Ren'Pynia"

default lovePoints = 2

label start():

    if kingdomName == "Ren'Pynia":
        "I'm in the Kingdom of Ren'Pynia!"

    if lovePoints == 3:
        "I have exactly 3 love points with the princess!"

    return

In the second example, we can see the comparison operator, which is written as a double equal sign. This is used to check whether the value of the variable is exactly what follows the operator - The first if statement checks if kingdomName is explicitly the string of "Ren'Pynia" (which it is), and the second if statement checks if lovePoints is exactly (which it is not).
One mistake that's common in Ren'Py is the incorrect use of equal signs. This is a perfect time to clear it up, so:

  • One equal sign (=) is used when setting a variable. For example, default visitedTheCastle = False.
  • Two equal signs (==) are used when checking a variable. For example, if visitedTheCastle == False.
default visitedTheCastle = False

default kingdomName = "Ren'Pynia"

label start():

    if 0 == False:
        "Theory test: This will pass."

    if visitedTheCastle is False:
        "I haven't been to the castle yet."

    if kingdomName is "Ren'Pynia":
        "Theory test: This will not pass."

    return

One thing to note with the comparison operator (the double equal sign) is that it shouldn't be used for False, True and None. The reason for this, if nothing else, is that (as seen in the first if) it evaluates 0 == False as True. Comparing a number and a True value makes no sense, causes trouble, and should be avoided at all cost.
For these values we use the is operator, as in the second if block. Unlike the comparison operator which, well, compares, the is operator checks whether the object is exactly the same. This is why the third if block won't pass - they may both be "Ren'Pynia" strings, but they're not the same objects - one is the defaulted kingdomName variable, and the other is written directly in the if statement.

default kingdomName = "Ren'Pynia"

label start():

    if kingdomName != "Ren'Pynia":
        "I have no idea what this kingdom is called."

    return

In the fourth example, we see another type of a comparison operator, this one written as an exclamation point followed by one equal sign. This if block will pass if kingdomName is anything but the string of "Ren'Pynia". It's a very simple operator, basically the opposite of it's == counterpart.

default lovePoints = 2

label start():

    if lovePoints >= 3:
        "The princess is madly in love with me! ([lovePoints] points)"

    elif lovePoints >= 1:
        "The princess isn't crazy about me. ([lovePoints] points)"

    else:
        "The princess doesn't like me. ([lovePoints] points)"

    return

Finally, in the fifth example we're comparing numbers. This example also has one elif block - this one is given a chance after if fails, before the code decides to run the else block. In our case, this means that the code will: 

  • First, check If lovePoints is 3 or more. If so, the best dialogue is chosen.
  • If that's not the case, check whether lovePoints is at least 1 or more. If so, the good dialogue is chosen.
  • If that's not the case, the worst dialogue is chosen.

Notice how all the possibilities are accounted for, as should usually be the case with if/elif/else blocks, so that you have perfect control of what can happen.

  • if covers 3 and any number greater than 3.
  • elif covers numbers between 3 and 1
  • else covers any number less than 1.

With all the numbers covered, the code always has an option to choose.


And I think I'll end the tutorial here. This tutorial is full of vital stuff, I really hope you I helped you get familiar with it!
So, what did we go over today? There was a lot of stuff:

  • What variables are, that they have a name and hold a value.
  • How to declare variables with the define statement.
  • How we can insert variable's value into dialogue with string interpolation
  • How to declare variables with the default statement.
  • What the difference between the define and default statement is.
  • How to change a variable's value, after it has been declared with the default statement.
  • How to check variable's value with if and else statements.
  • What elif statements are.
  • How to invert False and True values with the not operator.
  • How to check values with the comparison operators, == and !=.
  • How to check True, False and None values with the is operator.
  • How to compare numerical values.

As always, thank you for reading these, and I'll see you at the next tutorial.