This guide is intended to take you through the most common ways to use variables in visual novel games made with Ren'Py, starting with the fundamentals. A quiz at the end will test your knowledge on the variable types introduced here.
It's recommended that you have a passing familiarity with conditional statements (if
/elif
/else
) and choice menus (menu:
) in Ren'Py to get the most out of the examples used below. These sections in the Ren'Py quickstart should be enough to get you up to speed:
Variables are one of the basic building blocks of programming. They keep track of values you'd like to use during your program. In coding, a value is a term for any kind of information you'd like to store and use during a program. Values fall into one of two main categories: constants and variables.
A constant, as the name suggests, should have a constant value. It shouldn't change over the course of a game. In Ren'Py, you use define
to declare (set up) a constant e.g. define dark_brown = "#604430"
. Other built-in features in Ren'Py are expected to be constants too, with some exceptions. For example, the GUI[1] styles which are used to customize the appearance of the game UI, once defined, aren't expected to change. Ren'Py does not keep track of constants in save files, since they're not expected to change from their initial value.
A variable, on the other hand, is a value that can be updated over the course of a game. You use default
to declare a variable in Ren'Py e.g. default xia_love_points = 0
. Most things you use in the script will be variables, so we'll cover those first.
[1] GUI stands for Graphical User Interface. It refers to things like the buttons and menus you see on-screen when interacting with a program like a phone app or a game. Similarly, UI stands for User Interface and is a slightly more broad term that encompasses any systems the user uses to interact with a program. For something like a Ren'Py game, these terms are mostly interchangeable.↩
There are lots of values that you can assign to variables. The most common ones are listed below.
Simple Variable Types:
- Booleans - A boolean (often called a
bool
for short) is one of eitherTrue
orFalse
. - Integers - Numbers without decimal places like
1
,-72
,0
, and516487
. May be calledint
for short. - Floats - Numbers with decimal places, like
0.0
,3.14159
,-500043.2
and0.00004
.Float
is short forFloating Point Number
, named for the fact that the decimal point can "float" anywhere in the number, since there isn't a fixed number of digits before or after the decimal point e.g.100.05
has the decimal point after the third number but4.9543
has it after the first number, even though both have 5 digits. - Strings - Words you put inside quotes. Python and Ren'Py allow you to declare strings with either apostrophes (
'like this'
) or with quotations ("like this"
). Anything inside quotes is a string. So"1"
is a string, not an integer, because it's in quotes. Other example strings are"This is a string"
,'Pi is approximately equal to 3.14159'
, and"!!!!"
. None
- This is actually a very special kind of value, theNone
value. In other programming languages,None
is approximately equivalent toNULL
. For the most part, you can think ofNone
as a bit like a specialFalse
value.
Complex Variable Types:
These won't be covered in this initial tutorial, but are included here for reference since they're very useful.
- Lists - This is basically what it says; a list made up of other variable types. All you need to do to make a list is put it inside square brackets
[]
. Each item in the list is separated by a comma. Lists don't need to contain all the same variable types, either e.g.["This", 0, "is a valid list"]
or[True, 3.14, 15, False]
.- Lists can even contain other variables - that is, you could have something like
default name = "MC"
and then a list like[name, 24, "student"]
where the first item in the list,name
is the variablename
with the initial value"MC"
. - Note that declaring lists with
[]
is different from using[]
inside dialogue, where "Hello, [name]!" will display as "Hello, MC!" in-game. In dialogue, this tells Ren'Py you'd like to substitute the variable's value inside the dialogue, so it doesn't literally print "Hello, [name]!" and instead shows the value ofname
.
- Lists can even contain other variables - that is, you could have something like
- Dictionaries - Dictionaries are made up of key-value pairs. You look up a dictionary entry with its key and get the value back, just like how you look up a word (the key) in a real-life dictionary and get its definition (the value).
- Dictionary keys can't be variables (since it wouldn't make sense for the word in a dictionary to spontaneously change while still having the same definition), but dictionary values can be variables (like how real-world dictionaries may update the definition of a word after it acquires new meaning).
And then there are some other variable types we won't be covering here, but are listed here in case you'd like to look up more about them:
- Classes
- Sets
- Tuples
Before we cover each variable type and when to use it in the next section, you first need to know how to set them up in your game. The most important thing to know about setting up variables is that you should always be using default
to declare them (set them up) before the game starts. In previous Ren'Py versions, you'd declare the variable with a line of Python (using $ var = True
-style statements), usually in the start
label. However, recent versions use default
to declare the variables even earlier, before the game starts.
default
is essential for save compatibility and reducing errors. If Ren'Py has never seen a variable before, default
gives it a default value so you won't get any errors that a variable is undefined. Consider the following code:
# Version 1
label start:
menu:
"Go to the store":
$ went_to_store = True
"You went to the store."
"Go to the park":
$ went_to_park = True
"You went to the park."
"You had a nice trip."
if went_to_store:
"You didn't buy anything, though."
elif went_to_park:
"You sat on a bench by the pond."
"All in all, it was an alright day."
return
If the player goes to the store, went_to_store
is given the value True
. When we get to the line if went_to_store
, since went_to_store
is True, we see the line "You didn't buy anything, though."
On the other hand, if the player goes to the park, went_to_park
is given the value True. When we get to the line if went_to_store
, Ren'Py has not seen the variable went_to_store
yet, so it causes an error.
A slightly improved solution might look like:
# Version 2
label start:
$ went_to_store = False
$ went_to_park = False
menu:
"Go to the store":
$ went_to_store = True
"You went to the store."
"Go to the park":
$ went_to_park = True
"You went to the park."
"You had a nice trip."
if went_to_store:
"You didn't buy anything, though."
elif went_to_park:
"You sat on a bench by the pond."
"All in all, it was an alright day."
return
which would solve the issue earlier where went_to_store
wasn't a known variable for players who went to the park.
However, let's say that you released a demo of your game which had the first code (Version 1). A player went through your game and went to the park. They then made a save file while at the line "You had a nice trip." You inform the player that there was an error with the code and you've updated it to Version 2. The player updates to the new version, then loads their save game and clicks to continue. NameError: name 'went_to_store' is not defined
. What happened?
Even though the player updated their code, in their save game, they've already passed the point where the new line $ went_to_store = False
was added, so it doesn't run and the variable still isn't assigned a value. They run into the same issue as Version 1, but at least in this version they can begin a new game from the start and won't get the error message.
Luckily, there's a solution to both of these problems, and that's default
:
# Version 3
default went_to_store = False
default went_to_park = False
label start:
menu:
"Go to the store":
$ went_to_store = True
"You went to the store."
"Go to the park":
$ went_to_park = True
"You went to the park."
"You had a nice trip."
if went_to_store:
"You didn't buy anything, though."
elif went_to_park:
"You sat on a bench by the pond."
"All in all, it was an alright day."
return
Now when our player loads up their save file at the line "You had a nice trip", Ren'Py sees that this save file doesn't have a value for went_to_store
yet, so it gives it its default value of False
. It does have a value for went_to_park
, so it leaves that variable alone. Now when the player clicks to continue, they will skip the line "You didn't buy anything, though."
since went_to_store
is False
and move on to the line elif went_to_park
without throwing any errors. went_to_park
is True, so it shows the line "You sat on a bench by the pond." Success!
In summary, you will use default
to set up the initial value of your variable before the game starts, and use $
to change that value during gameplay. Note that default
and define
go outside of labels; that is, they should occur during initialization[2] and not inside of a label. So the following two are not the same:
# Correct! This is run before the game starts.
default watched_tv = False
label start:
menu:
"Watch TV":
$ watched_tv = True
"Go to sleep":
$ watched_tv = False
return
label start:
# Incorrect! If you want this to be set up
# before the game starts, default must be
# outside of any labels.
default watched_tv = False
menu:
"Watch TV":
$ watched_tv = True
"Go to sleep":
$ watched_tv = False
return
[2] Initialization refers to the period of time before the game starts when the game is getting set up. When you hit Launch Game on the Ren'Py launcher, it takes it a few seconds before your game actually opens. The main tasks performed during that time are initializing values like variables and images, loading script files and menu screens into memory to display them, etc. To initialize a variable means to give it some value during this time, as opposed to giving it a value after the game has already started.↩
Now we'll go over what situations you might want to use each variable type for, and then have a quick quiz to see what you've learned.
Booleans are usually the first variable type most people find out about, and are probably the most common across Ren'Py games. As mentioned, they are one of two values: True
or False
.
In a default Ren'Py game, you'll see booleans already in use in several places. For example, in screens.rpy
there is a screen called screen navigation
, and a line inside there that says if main_menu:
. main_menu
is a boolean variable that Ren'Py sets to be True
if the player is on the main menu and False
if they're in-game. In this case, if the player is on the main menu, they should be shown a Start
button to start the game. If they're already in-game though, there's no need to show them a start button.
Places where you SHOULD use Booleans:
- Tracking player actions e.g. did they give Ashwin a gift? True/False
gave_ashwin_gift = True
- Keeping track of binary choices e.g. did the player lie about knowing who Xia is?
lied_about_xia = False
- Toggling preferences e.g. do they have animations toggled on or off?
persistent.animations_on = True
Places where you should NOT use Booleans:
- When you have 2+ values that are mutually exclusive[3] e.g.
player_height_tall = True
,player_height_average = False
,player_height_short = False
- In this case, what happens if
player_height_tall
ANDplayer_height_short
are somehow both True? What happens if all three are False? Because they are separate variables, there's a lot of room for error. It's better to use one of the other variable types mentioned below.
- In this case, what happens if
- When tracking points e.g.
player_zoran_love1 = True
,player_zoran_love2 = True
- It's better to use something like an integer to handle this and sum point totals during the game
Example boolean use:
default player_ate_apple = False
label start():
"One day, Snow White was visited by a strange old lady selling apples."
menu:
"Eat the apple":
$ player_ate_apple = True
"She ate the apple. It was lovely and red and juicy."
"Don't eat the apple.":
$ player_ate_apple = False
"She shook her head at the old woman and decided not to take the apple."
"After the woman left, Snow White returned to her cleaning."
if player_ate_apple:
"She began to feel quite unwell and fainted in the kitchen."
jump ate_apple
else:
jump end_day_1
This is a good use for boolean values as there are only two possibilities - either the player ate the apple (True) or they didn't (False). This doesn't conflict with anything else they could have done. You may also note that the $ player_ate_apple = False
line under the "Don't eat the apple."
choice is optional because player_ate_apple
begins with a value of False
. So, unless it is otherwise changed, it will still be False whenever you go to check it. However, it's good to add it for clarity and to prevent accidental errors in case player_ate_apple
was somehow set to True
before reaching this choice menu.
You may also notice that there is a conditional statement there - the thing that says if player_ate_apple
. This is a useful feature of booleans is that you can just check if boolean_variable
to see if boolean_variable
is True.
The else
statement basically says "if the condition(s) before this statement were NOT true, then do this". So in the case of
if player_ate_apple:
jump ate_apple
else:
jump end_day_1
when player_ate_apple
is False
, the line if player_ate_apple
isn't True (the player didn't eat the apple). So the program moves on to the next condition, which is the else
line and jumps to the end_day_1
label instead.
[3] Mutually exclusive means that of two or more related situations, if one of them is True then the other(s) must be False.
In everyday language, something like the kitchen light being on or off is mutually exclusive: if the light is on it cannot also be off, and if it is off it cannot also be on. ↩
Click for more explanation on mutually exclusive values
More than one "situation" may be mutually exclusive. For example, in a game, perhaps you might have 1, 2, or 3 cookies in your inventory. If you have 1 cookie you can't also have 2 cookies or 3 cookies and so on. Either you have one cookie, or you have two cookies, or you have three cookies. Or perhaps the player can choose a suit of cards to be trump in a card game - choosing hearts as trump means clubs, diamonds, and spades cannot be trump.
Unless there are only two possibilities, or the options can be simplified to two possibilities (such as "either you have cookies or you don't" and the number of cookies doesn't matter, or "either hearts is trump or it isn't" and the specific non-heart suit that is trump doesn't matter), then you should use a different variable type like integers (for the cookies) or strings (for the card suits) to prevent situations where you end up with something like
default has_one_cookie = True
default has_two_cookies = True
default has_three_cookies = True
What does the above code even mean? How many cookies does the player really have? One? Three? Six? As you'll see in the example for integers, something like
default num_cookies = 1
is better because there's no way for a player to simultaneously have 1 and 2 cookies.
Integers are excellent for tracking any sort of numerical values in your game, such as love point totals for characters or the number of times the player has gone to the park. Remember that an integer is any non-decimal number e.g. -5, 20, 0, or 124083
In a default Ren'Py game, integers are used for some preferences, such as the number of characters displayed per-second (also known as CPS characters per second), and for lots of defined GUI values like the text size of in-game dialogue or the width (in pixels) of the dialogue box.
Places where you SHOULD use Integers:
- When keeping track of collected points e.g. how many affection points do they have with Xia?
xia_love_points = 0
andxia_love_points += 1
- When counting how many times a certain action has been done e.g. how many times has the player visited the park?
player_park_visits = 1
- Tracking the number of actions the player has left before time runs out e.g. there's only enough time to dance with two people at the ball. Once you've danced with two people, the ball is over.
num_dance_partners = 2
- Tracking how many items a player is holding or has acquired e.g. How many life jackets did the player manage to get?
num_life_jackets = 3
Places where you should NOT use Integers:
- Variables where there are only two outcomes e.g. either they have enough life jackets or they don't, and there's no way to acquire/lose just one or two (instead of all the life jackets)
- In this case, a boolean would be more appropriate (see the note on mutual exclusivity above[3])
- Keeping track of multiple distinct but diverse options e.g. the player can be either tall or short. You use a variable like
player_height = 0
for short and1
for tall- This is more subjective; it's not a bad choice per se, but in the case of player heights, it can make your code harder to read by using numbers for non-numerical concepts. Additionally, what would happen if you wanted to allow players to be "average" height? Do you use
0.5
?2
? In this case, a better choice would be to use strings likeplayer_height = "short"
,player_height = "average"
etc - However, if you wanted to represent height as actual numerical values like 150cm, then an integer would be a good choice, especially if you're doing comparisons like
if player_height < 150
->"You couldn't reach the top shelf."
- This is more subjective; it's not a bad choice per se, but in the case of player heights, it can make your code harder to read by using numbers for non-numerical concepts. Additionally, what would happen if you wanted to allow players to be "average" height? Do you use
Example integer use:
default zoran_love_points = 0
label start():
"Zoran shuffles awkwardly, looking down at his feet."
menu:
"Make a joke.":
$ zoran_love_points += 1
"He smiles when you finish telling the joke."
"Tell him to get over himself.":
$ zoran_love_points -= 1
"He looks almost angry at you."
"Then he gets up and leaves."
Ren'Py has a couple of useful shortcuts for setting integer values. After you've set up your variable with a statement like default zoran_love_points = 0
you can increase it in several ways:
$ zoran_love_points = zoran_love_points + 2
$ zoran_love_points = 2
$ zoran_love_points += 2
The first statement adds two points to whatever the existing value of zoran_love_points
is. If it starts at 0, then after $ zoran_love_points = zoran_love_points + 2
runs, zoran_love_points
will be equal to 2.
The second statement directly sets zoran_love_points
to the value 2. In the case of love points, this usually isn't what you want because you want to tally the total points the player has earned with Zoran, not overwrite the value. If a player was able to get 1 point with Zoran before reaching this statement, they should get two additional points for a total of 3. However, this can be useful in other situations, like when resetting the value of a variable.
The third statement is actually identical to the first, just in a shorter form. +=
tells Ren'Py you want to take the original value of the variable named on the left and add the number on the right to it. So, if zoran_love_points
started as 3, then zoran_love_points += 2
will cause zoran_love_points
to be equal to 5
.
This also applies to negative numbers, which use -=
instead of +=
. To remove 1 point from zoran_love_points
, you can do $ zoran_love_points -= 1
.
Floats are very similar to integers, but are used for numbers with decimal places like 3.1415, -19.0, and 0.2
In a default Ren'Py game, floats are mostly used for alignment properties of GUI elements like the choice buttons. The alignment property, align for short, positions things like buttons on the screen. It uses floats to represent a percentage of the screen rather than a set number of pixels. So, a value like 0.5
indicates the alignment should be 50% of the way down/across the screen (aka centered).
Places where you SHOULD use Floats:
- Tracking numbers which have decimal places, such as how much money the player has (including cents).
money = 100.00
- Using decimal places as percentages
grade = 30.0/40.0
orgrade = 0.9
Places where you should NOT use Floats:
- Anywhere that you need distinct values or need to compare exact numbers.
- Floats can't be exact due to the infinite nature of decimal points. When it comes to numbers with a lot of decimal places like pi, the computer has to limit how many digits it will use so it can do calculations, which means that there will be some rounding errors.
- So if you find yourself comparing two floats like
if grade == 0.9
, you may want to rethink if you should use integers OR check for a range instead e.g.if grade >= 0.85 and grade <= 0.95
- Same rules where integers would be bad also generally apply to floats
Example float use:
default cash = 10.00
define flower_price = 4.99
define necklace_price = 8.99
define postcard_price = 2.49
label start():
"You go to the market to make some purchases."
menu market:
"What will you buy?"
"Flower ($[flower_price])" if cash >= flower_price:
$ cash -= flower_price
"Necklace ($[necklace_price])" if cash >= necklace_price:
$ cash -= necklace_price
"Postcard ($[postcard_price])" if cash >= postcard_price:
$ cash -= postcard_price
"Don't purchase anything else":
jump leave_market
jump market
label leave_market():
"You finished buying everything you needed. You now have $[cash] left."
Before the start label, we define some set prices for items at the market. These values don't change during gameplay, but they're useful to compare against instead of writing out the prices each time, and if we decide later that a flower should cost $5.50 instead of $4.99, we only have to update the initial definition to change it everywhere that price is used.
Then, at the market, each choice checks if the player has at least enough cash to cover the purchase of a given item (if cash >= flower_price
). If so, the player can purchase that item and the funds are deducted from the total amount of cash that they have ($ cash -= flower_price
). The [var]
format inside of the choice text means that Ren'Py will substitute[4] in the value of the given variable instead of literally displaying [var]
. So the player will initially see a menu which looks like:
Flower ($4.99)
Necklace ($8.99)
Postcard ($2.49)
This part works very much like integers, but for floats you don't want to do direct comparisons like if cash == 4.99
. Instead you can do if cash > 4.98 and cash < 5.0
if you need to do a tight comparison, but most cases when you're doing float comparisons should be something more like if cash >= 4.99
. This is because you can't guarantee that a float will be the precise number you give it due to rounding and limits in representing infinite decimal places.
[4] The formal coding term for this sort of variable substitution is interpolation. So, for something like:
$ name = "MC"
"Hello there, [name]."
the variable name
is interpolated in the sentence "Hello there, (...)." so that the player sees the line "Hello there, MC." ↩
Strings are one of the most flexible variable types. They'll contain just about anything so long as you put it in quotes. As a result, they're useful for holding a variety of information.
In a default Ren'Py game, strings are used for things like defining colour codes to use for text (e.g. "#ff0000") and for the name of your game and what the current version is. They're also used to specify file paths for things like images, sound effects, and font files.
Places where you SHOULD use Strings:
- When you have a lot of possible values for a variable that correspond to words e.g. keeping track of what colour hair the player has
hair_color = "black"
- When you want to use a variable in dialogue during the script e.g.
player_height = "short"
and thenp "Yeah, I'm pretty [player_height]."
. This would display to the player like"Yeah, I'm pretty short."
- When a variable corresponds to an image or filename you want to use e.g.
xia_hair = "long"
and thenimage xia happy = "Characters/xia/[xia_hair].png"
. Like variable substitution in dialogue, this will search for an image atimages/Characters/xia/long.png
. If you later change Xia's hair to be short ($ xia_hair = "short"
), then the game would instead look for the fileimages/Characters/xia/short.png
. - If you want to have branching storylines reconvene at some common event, then branch off again depending on past choices.
- For example, the player can be on Xia, Zoran, or Ashwin's romance routes. There is a common event to all three routes where the player spends the day at work. After they finish their job, the next scene depends on who they are romancing. Before you jump to the common event label, you can set up a variable like
$ next_scene = "zoran_scene_4"
at the end of each specific route, and then at the end of the common event you can writejump expression next_scene
to jump to the label saved in thenext_scene
variable (so for Zoran it would jump to a label namedzoran_scene_4
)jump expression
is a special variation of thejump
statement that lets you use a string to get the label name to jump to.
- For example, the player can be on Xia, Zoran, or Ashwin's romance routes. There is a common event to all three routes where the player spends the day at work. After they finish their job, the next scene depends on who they are romancing. Before you jump to the common event label, you can set up a variable like
Places where you should NOT use Strings:
- When you're keeping track of counters (numbers that increase/decrease during the game, like affection points or counting how many times the player has said a swear word) or other numerical values
- Usually integers are better for this. Sometimes floats are also helpful.
- If the variable only has two possible values in a yes/no-style pattern
- An exception to this is if you use the variable to determine which image to use for a character e.g. the character has two different outfits, their work outfit and their normal outfit. You have a variable
ashwin_outfit = "work"
orashwin_outfit = "normal"
. Even though Ashwin is either wearing/not wearing their work uniform, you might use this variable inside an image definition likeimage ash = "Characters/Ashwin/[ashwin_outfit].png"
so it makes sense to keep it as a string so you can take advantage of Ren'Py's variable substitution. With the above format, Ren'Py will either look for the imageimages/Characters/Ashwin/work.png
orimages/Characters/Ashwin/normal.png
depending on the value ofashwin_outfit
.
- An exception to this is if you use the variable to determine which image to use for a character e.g. the character has two different outfits, their work outfit and their normal outfit. You have a variable
Example string use:
default gift = None
label start():
"At the mall, you decided you wanted to buy Ashwin something."
menu:
"Buy them a bath bomb.":
$ gift = "bath bomb"
"Buy them a self-help book.":
$ gift = "self-help book"
"Buy them a digital camera.":
$ gift = "digital camera"
"Later that day, you were able to give them the [gift] you bought earlier."
"They liked it a lot."
The player can only get one gift for Ashwin, so it makes sense to store this variable as a string. Even more useful is that we can have Ren'Py substitute it in the sentence after the menu so that if the player bought Ashwin a bath bomb the line reads "Later that day, you were able to give them the bath bomb you bought earlier." This saves us having to write out three separate sentences for each gift with only the name of the gift changed, but you can also customize later text by checking for conditions like if gift == "bath bomb"
(conditionals are covered in more detail in a later section).
Strings vs Booleans
It's very common for people to use booleans (True/False) instead of strings (words in quotes) in their script when strings are a better choice to reduce errors while still remembering multiple options. Consider the following situation:
During your game, a round of rock-paper-scissors is played which determines who gets to eat the last cookie. The player gets to choose which of rock, paper, or scissors they would like to play, and if they win, they earn the cookie. You're keeping track of the results like so:
default npc_rock = False
default npc_paper = False
default npc_scissors = False
default player_wins = False
label start:
# Round 1
# You could do some logic here, like using
# renpy.random to randomly select which of
# rock/paper/scissors the non-player character
# (NPC) should use, but we'll just set it directly.
$ npc_paper = True
label rock_paper_scissors:
# Show which move the NPC chose
show paper_img
menu:
"Rock":
if npc_rock:
"It's a tie! Go again."
jump rock_paper_scissors
if npc_paper:
$ player_wins = False
if npc_scissors:
$ player_wins = True
"Paper":
if npc_rock:
$ player_wins = True
if npc_paper:
"It's a tie! Go again."
jump rock_paper_scissors
if npc_scissors:
$ player_wins = False
"Scissors":
if npc_rock:
$ player_wins = False
if npc_paper:
$ player_wins = True
if npc_scissors:
"It's a tie! Go again."
jump rock_paper_scissors
if player_wins:
"You won!"
jump later_on
else:
"You lost!"
jump no_cookie_for_you
There's a lot happening here, but most of it is just repeat code for all the options. For example, let's look at the code under the "Rock" choice in the menu:
"Rock":
if npc_rock:
"It's a tie! Go again."
jump rock_paper_scissors
if npc_paper:
$ player_wins = False
if npc_scissors
$ player_wins = True
First, we check if npc_rock
aka did the NPC play "Rock"? If so, and the player chose rock, then it's a tie and we'll jump to the rock_paper_scissors label so the player can try again, no penalty.
Next, we check if npc_paper
(did the NPC play "Paper"?). If they did, and our player chose rock, then the player loses, so we set player_wins
to False
.
Finally, we check if npc_scissors
(the NPC played scissors). The player played rock, so they win. We set player_wins
to True
and continue on.
After the player has played the game, the game continues as usual, either jumping to the label later_on
if the player won, or the label no_cookie_for_you
if the player lost.
All seems pretty normal so far. Later in your script, the characters decide to play another game of rock-paper-scissors, this time for the last piece of pizza. You decide to use the same format for this new round:
label later_on:
# Set up the NPC's move
$ npc_rock = True
label rock_paper_scissors2:
show rock_img
# This menu is the same as the original
menu:
"Rock":
if npc_rock:
"It's a tie! Go again."
jump rock_paper_scissors2
if npc_paper:
$ player_wins = False
if npc_scissors:
$ player_wins = True
"Paper":
if npc_rock:
$ player_wins = True
if npc_paper:
"It's a tie! Go again."
jump rock_paper_scissors2
if npc_scissors:
$ player_wins = False
"Scissors":
if npc_rock:
$ player_wins = False
if npc_paper:
$ player_wins = True
if npc_scissors:
"It's a tie! Go again."
jump rock_paper_scissors2
if player_wins:
"You won!"
jump eat_the_pizza
else:
"You lost!"
jump no_pizza_for_you
Can you spot what the issue is here?
We forgot to reset the NPC's original choice! So by the time we get to the rock_paper_scissors2
label, both npc_paper
and npc_rock
are True
!
What does that even mean? You can't play both rock and paper at the same time. Now let's look at what happens the second time when the player chooses Scissors:
"Scissors":
if npc_rock:
# This is True!
$ player_wins = False
if npc_paper:
# This is also True!
$ player_wins = True
if npc_scissors:
"It's a tie! Go again."
jump rock_paper_scissors2
Since both npc_rock
and npc_paper
are True
, player_wins
is first set to False
, and then set to True
! Even though the NPC was supposed to play rock and we showed rock_img
to the player, the player now inexplicably wins the round even though they played scissors when the NPC played rock (and rock beats scissors).
One way to try to fix this is to make sure you reset each of the rock/paper/scissors variables before starting a new game e.g.
label later_on:
# Set up the NPC's move
$ npc_rock = True
$ npc_paper = False
$ npc_scissors = False
But this still leaves us vulnerable to accidental configurations like more than one value being True or all three being False if we're not careful, and it's already a lot of extra typing even though we only have three options. This is where strings come in!
Here's a rewrite of the rock-paper-scissors game, using strings:
default npc_move = "rock" # New!
default player_wins = False
label start:
# Round 1
# You could do some logic here, like using
# renpy.random to randomly select which of
# rock/paper/scissors the non-player character
# (NPC) should use, but we'll just set it directly.
$ npc_move = "paper" # New!
label rock_paper_scissors:
# Show which move the NPC chose
show paper_img
menu:
"Rock":
if npc_move == "rock":
"It's a tie! Go again."
jump rock_paper_scissors
if npc_move == "paper":
$ player_wins = False
if npc_move == "scissors":
$ player_wins = True
"Paper":
if npc_move == "rock":
$ player_wins = True
if npc_move == "paper":
"It's a tie! Go again."
jump rock_paper_scissors
if npc_move == "scissors":
$ player_wins = False
"Scissors":
if npc_move == "rock":
$ player_wins = False
if npc_move == "paper":
$ player_wins = True
if npc_move == "scissors":
"It's a tie! Go again."
jump rock_paper_scissors
if player_wins:
"You won!"
jump later_on
else:
"You lost!"
jump no_cookie_for_you
Now if we set npc_move
to "rock"
later on for the second round, it's impossible to have conflicts with the previous game of rock-paper-scissors, since either npc_move
is equal to "rock"
or it's equal to "paper"
or "scissors"
, and it's impossible for it to be equal to more than one. Note that ==
compares two values directly to see if they're equal/the same, so in the menu a line like if npc_move == "rock"
is True if npc_move
has the value "rock"
(since "rock" == "rock"
). if npc_move = "rock"
with only one =
is incorrect programming syntax and will cause an error, because only one =
is used to set the value of a variable, not to compare with other values.
Amazing, you got this far! Now we have a few questions to see if you understand when to use each variable type.
You're keeping track of whether the player has a part-time job. If the player does have a part-time job, sometimes the script will mention that they're not working today, or that they just finished a shift.
Which variable type should be used?
- Boolean (True/False)
- Integer (non-decimal numbers)
- Float (numbers with decimals)
- String (numbers/symbols/letters inside quotes)
Click for answer
The best answer is 1. Boolean
. In this case, we don't care what job the player has, just whether they have a job or not. The variable would look something like default has_job = False
and can be updated with lines like $ has_job = True
.
Integers and floats aren't suitable here because we aren't tracking any kind of numerical value.
A string would be the best choice if we wanted to know what kind of job the player had. For example, if dialogue changed based on whether the player had a job at the mall vs. at a book store, then it would make sense to have something like player_job
where player_job
could be one of None
, "mall"
, or "book store"
.
The player can choose pronouns at the start of the game (he/him, she/her, or they/them). Sometimes other characters will use these pronouns when talking about the player. You want to write a sentence like "Did she say she knew about it?" where "she" uses the player's pronouns.
Which variable type should be used?
- Boolean (True/False)
- Integer (non-decimal numbers)
- Float (numbers with decimals)
- String (numbers/symbols/letters inside quotes)
Click for answer
The best answer is 4. String
. There are three possible sets of pronouns which are mutually exclusive and you want to use them in dialogue, so a string is the way to go (and in this case, likely several strings). One possible way to handle this dialogue, for example, might be:
default they = "they"
default them = "them"
label start:
menu:
"She/her pronouns":
$ they = "she"
$ them = "her"
"He/him pronouns":
$ they = "he"
$ them = "him"
"They/them pronouns":
$ they = "they"
$ them = "them"
"Xia" "Did [they] say [they] knew about it?"
This will correctly display as "Did she say she knew about it?" for a player with she/her pronouns (note that the variable name itself is they
, which is sometimes set to the string "they"
but may be set to another value like "he"
). Further variables may need to be defined, particularly if a sentence requires verb conjugations (e.g. "she is going" vs "they are going"). Using strings will allow the pronoun variables to be used directly in sentences and also prevents accidental mixing-and-matching of pronouns.
In your game, the player can make both good and bad choices. You want to check if they've made more good choices than bad choices to see which ending they get.
Which variable type should be used?
- Boolean (True/False)
- Integer (non-decimal numbers)
- Float (numbers with decimals)
- String (numbers/symbols/letters inside quotes)
Click for answer
The best answer is 2. Integer
. Depending on how your game is set up, you can either have one variable like default ending_points = 0
which you then adjust via
menu:
"A bad end choice.":
$ ending_points -= 1
"A good end choice.":
$ ending_points += 1
And at the end, you can check if the player has a positive number of points (indicating they made more good end choices than bad) or a negative number (indicating they made more bad end choices than good).
Alternatively, if you have several ranges of endings based on how many good/bad end choices the player made, you might have two variables like default good_end_points = 0
and default bad_end_points = 0
. You'd then adjust them like
menu:
"A bad end choice.":
$ bad_end_points += 1
"A good end choice.":
$ good_end_points += 1
It's fine to have two variables in this case since the two are not mutually exclusive. While you may only get one of either good or bad end points for any given choice, getting more bad end points doesn't affect the number of good end points you can earn.
For your ending you could check something like
if bad_end_points > 10:
# The player made more than 10 bad choices
jump super_bad_end
elif good_end_points > 10:
# The player made more than 10 good choices
jump excellent_end
elif bad_end_points > 5:
# The player made 6-10 bad choices
jump bad_end
elif good_end_points > 5:
# The player made 6-10 good choices
jump good_end
else:
# The player didn't get more than 5 good or bad points
jump normal_end
You're trying to keep track of how the player reacted to Xia at the beginning of the game. There are three possible reactions - anger, surprise, and excitement. The player can only react with one of those emotions (no combinations). If you reacted with anger, Xia will bring that up later in the story.
Which variable type should be used?
- Boolean (True/False)
- Integer (non-decimal numbers)
- Float (numbers with decimals)
- String (numbers/symbols/letters inside quotes)
Click for answer
The best answer is 1. Boolean
. While there are three possible reactions, only one of them matters to bring up in the story later - the anger reaction. For this reason, it makes sense to have a variable like xia_reaction_angry = False
which gets set to True
if the player reacts angrily.
However, if you wanted Xia to bring up the player's reaction and comment on it depending on the reaction, then you would use strings. There are more than two values and it's not a yes/no-style variable, so in that case it wouldn't make sense to use booleans. The player can't be both angry and surprised at seeing Xia, so you should have one variable keep track of which reaction the player had. In this case, the variable would look more like default xia_reaction = "surprised"
and then you'd set it to something like $ xia_reaction = "angry"
or $ xia_reaction = "surprised"
during the game. Notably, the variable begins as "surprised"
so that there isn't a situation in which the player didn't react to Xia.
What you don't want in either case is something like
default xia_reaction_angry = False
default xia_reaction_surprised = False
default xia_reaction_excited = False
This is because the reactions are mutually exclusive, so the player can only react with one emotion out of the three. This kind of setup is vulnerable to unintentional configurations like both xia_reaction_angry
and xia_reaction_excited
being True, or all three being False when you go to check which reaction the player had. If strings are used instead, it's impossible for the player to have had more than one type of reaction.
The player has the option while shopping to buy Zoran a book: Pride & Prejudice or Frankenstein. If they bought Zoran a book, he will later tell the player what he thought of it and comment on the plot.
Which variable type should be used?
- Boolean (True/False)
- Integer (non-decimal numbers)
- Float (numbers with decimals)
- String (numbers/symbols/letters inside quotes)
Click for answer
The best answer is 4. String
. There are two possible books, Pride & Prejudice and Frankenstein, which might mean that a boolean seems like a good choice, but the player also has the option to not buy Zoran a book at all, so there is a third option, not buying a book. Since these options are all mutually exclusive - either the player didn't buy Zoran a book, or they bought him one of Pride & Prejudice or Frankenstein - a string offers the best way to keep track of all three possibilities.
We'd need at least two booleans to keep track of all three possibilities, which is bad because only one situation can happen at a time so we don't want more than one variable. You could also use an integer here, but assigning numbers to book titles will make your code harder to read and remember, so a string is the best choice.
default bought_zoran_book = False
label start:
menu:
"At the store, you see some books."
"Buy Pride & Prejudice for Zoran.":
$ bought_zoran_book = "Pride & Prejudice"
"Buy Frankenstein for Zoran.":
$ bought_zoran_book = "Frankenstein"
"Suggest you buy some ice cream together.":
$ bought_zoran_book = False
"It was a really nice day."
if bought_zoran_book == "Frankenstein":
"Zoran" "You know, it's really weird everyone thinks Frankenstein is the name of the monster, not the scientist."
elif bought_zoran_book == "Pride & Prejudice":
"Zoran" "I love the slow burn romance in this one."
else:
"Zoran" "Thanks again for the ice cream."
return
And finally, a flow chart to give you a rough idea of when to use each variable type.
- Does the thing you're keeping track of involve numbers or increasing/decreasing values? (e.g. increasing/decreasing number of items held, keeping track of personality points, using an energy system to determine if the player can perform an action, etc)
- Yes -> Go to #2
- No -> Go to #3
- (It involves numbers) Will you need decimal places? (e.g. for things like percentages or money amounts that involve cents)
- Yes -> Floats
- No -> Integers
- (It does not involve numbers) How many possibilities are you keeping track of?
- Two -> Go to #4
- More than two -> Go to #6
- (Only two possibilities) Will you be using this variable directly in dialogue or as part of an image definition? e.g. "It's [player_eye_color] because I wanted it to match your eyes." or
image zoran = "Characters/Zoran/[zoran_outfit].png"
- Yes -> String
- No -> Go to #5
- (Won't use in dialogue or images) Will you use this value later to save something like a label name to jump to?
- Yes -> String
- No -> Boolean
- (More than two possibilities) Is it possible for two or more of the possible options to occur at the same time? (e.g. the player can own both a cat and a dog, just a cat, just a dog, or neither)
- Yes -> Booleans (one for each possibility e.g.
owns_cat = True
,owns_dog = False
)- (Bonus answer) This often a good place to use lists as well e.g.
owned_pets = []
which can becomeowned_pets = ["cat", "dog"]
and you can check if "dog" is in the list to know if the player owns a dog.
- (Bonus answer) This often a good place to use lists as well e.g.
- No -> Go to #7
- Yes -> Booleans (one for each possibility e.g.
- (Possibilities are mutually exclusive) What would make your code clearer, writing out a whole word or using numbers to represent each possibility? (e.g. the player has three possible shirt types. In your images folder, you have called these
player_shirt1.png
,player_shirt2.png
, andplayer_shirt3.png
. You could have a variable likeplayer_shirt = 1
without loss of clarity.)- Whole words -> String
- Numbers -> Integer
And that's all! Thank you for reading. Feel free to leave a comment or contact me if you have further questions or feedback.