Ren'Py Tutorial Newbie guide to basic variables in RenPy

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,555
2,170
Newbie guide to basic variables in RenPy

RenPy is very flexible. Way more flexible than most of us will ever need.
But everyone has to start somewhere... and whilst the is great... it also tends to explain everything.
(though I will say, the " " section of the documentation is very good for a newbie like me).
Except, when you're just starting to write your first RenPy game... you don't need to know everything... just enough to get you started.

I hope that is what this guide does... explain "just" enough... about the basics of variables.
I am not a RenPy expert. If you're reading this guide for help... chances are I'm just a page and a half ahead of you in the manual.

So... onward...

At our level of programming, there are two "types" of variables.
Normal variables are saved by autosaves and saves, etc. And when you load your save game, their values are loaded too.
Static variables aren't saved. They aren't ever expected to change either. RenPy sets them each and every time your game runs.
For the pedants out there... Yes, I know "normal" and "static" aren't the real names. And yes, I know there are a lot more variations than just these two "types". But for the sake of an introduction... this is where I'm drawing the line.

The "$ {variable} = {value}" is an example of what I'm calling a normal variable. There are other ways to set them up, but $ is as good a way as any. More about the other ways later...
the "define a = Character("Angela")" would be an example of what I'm calling static variable. In this case, the variable would be "a".

RenPy has a strongly hinted at rule that static variables should never be changed by the code. They should be set once and left alone. You can change them, and examples I've seen do work. But the documentation says it isn't guaranteed to work and bad things might happen.

It's simpler to just not think of "define"s as variables. They are defines.

Okay, back to "normal" variables.

There are 3 main variable types. (again, there are more... just don't worry about it).


Boolean... A simple Yes/No. Though for historical computer reasons, the values are True and False (actually 1 and 0).
Python:
$ saw_angela_naked = True
# comment
if saw_angela_naked == True:
    a "I saw you... you know... watching me."
# another comment
if saw_angela_naked:
    a "I still haven't forgotten that you saw me naked."
if saw_angela_naked is True:
    a "another line of dialogue."
if not saw_angela_naked:
    a "You are a real gentleman sometimes."
if saw_angela_naked is False:
    a "and I love you for it."
As you can see, there are lots of ways you can check a boolean variables. "is True" works, "== True" too. But if you omit either, it'll still assume you are checking if it's true.
Also note that True and False are case sensitive. True is not the same as true.



Integer... A simple number.
Python:
$ kissed_angela = 0
# comment... add 1
$ kissed_angela += 1
# comment... subtract 3
$ kissed_angela -= 3
if kissed_angela > 5:
    jump day3_whoopie_time
else:
    jump day3_blown_out

String... Just text. Letters, numbers, punctuation... pretty much anything you can type.
Python:
# okay, maybe a bit over the top for an example....
python:
    pcname = renpy.input("What is your name? {i}(Press ENTER to use the default name 'Simon'){/i}", length=20, exclude=" 0123456789+=,.?!<>{}[]'\"\%")
    pcname = pcname.strip()

    if not pcname:
        pcname = "Simon"
Boolean is (almost) always True / False.
Integer is any number without quotes (") around it.
Strings are any text within quotes (").

RenPy sets the variable type when it first sets it's value.
It also gets a bit upset if you start mixing things up a bit. (Like trying to add 1 to a string, or some other such nonsense).

One thing to note is that the dollar sign ($) is not "set the value". It's effectively a shortcut for "python:". Generally speaking, if you're using 1 or 2 lines of python, you'd use $. If it's more lines that that, it's probably easier to use "python:".

The final thing I will mention is "default".

One problem with setting variables as you go along is that if you ever add new variables into existing code, there is a chance a player might load a save game where that new variable doesn't exist.

So you have some code that looks like this...
Python:
label start:
    a "Hi, my name is Angela."
    a "I'm so happy to meet you."
    a "Let's get on with the game."
    "<< END >>"
And sometime later you change it to this instead...
Python:
label start:
    $ kissed_angela = 0
    a "Hi, my name is Angela."
    a "Let me start by giving you a kiss."
    $ kissed_angela += 1
    a "I'm so happy to meet you."
    a "Let's get on with the game."
    if kissed_angela > 0:
        a "thank you for the kiss."
    "<< END >>"
Now. If your player saves at "Hi, my name is Angela." - everything is fine. Because even though the player loads AFTER the "$ kissed_angela = 0", (I think) the "+= 1" will also create the variable.

But. If your player saves at "I'm so happy to meet you.". Any player who saved using the old version and loaded using the new version will encounter an error when it reaches "if kissed_angela > 0:" because kissed_angela doesn't exist.

The solution to this is the "default" statement.
It works like "define", in so much as it sets everything up before the main game even starts running.
It doesn't matter where they are in the code, RenPy scans everything looking for things like define/default and sets all their values before (almost) anything else happens.

So things would look like this instead...
Python:
default kissed_angela = 0

label start:
    a "Hi, my name is Angela."
    a "Let me start by giving you a kiss."
    $ kissed_angela += 1
    a "I'm so happy to meet you."
    a "Let's get on with the game."
    if kissed_angela > 0:
        a "thank you for the kiss."
    "<< END >>"
So when your game first starts, kissed_angela is always 0. Even if the player is already past the place in the code where you added it.
If the player loads a save game that doesn't have the variable in... it doesn't matter, because the value has already been set to 0.
If the player loads a save game that DOES have the variable in... it just overwrites the default value with the value from the save file.
default can be used for any variable type. So "default seen_angela_naked = False" is just a valid as any other default line.

I know that's a lot of text to read. I could have left out the bit about default. Most games don't use it and still manage to work just fine... but I think it's safer and clearer to read if you use it.

More reading about variables:


 

Epadder

Programmer
Game Developer
Oct 25, 2016
567
1,046
Default vs $

In my experience using default for variable creation is the preferred method, using $ to create variables can lead to errors later if you're not extremely careful about it.

Using a real world example of how it leads to trouble:
In A Wife and A Mother when I was playing through (fresh save) it I started having a 'name not defined' error when I was towards the end. After digging through the code I realized what the problem was, the variable in question was only set after you made a choice that pushed you into a particular scenario. This scenario was still checked even though I had never been near that part of the story due to my earlier choices.

So if you are going to have a story that diverges but merges back into a common thread at different points, by setting variables with $ you have the potential of being unable to check variables reliably without the possibility of error.

Not to say that you can't set(initialize probably being a better term) some variables with $ and have a functioning game, but realistically there isn't a downside to using default that I know of.

Explanation of Define with Examples

So define is used to create variables for parts of your game that don't change, but you need to reference often. If you look into other programming languages or basic programming theory type things, "constant" is the term your likely to see that relates to the idea of define.

So in my own project I define the paths to my music files:
Python:
    define freia_bm = "Music/JP Soundworks - Boss1.ogg"
    define music_battle = "Music/JP Soundworks - Battle2.ogg"
    define music_world = "Music/Sappheiros - A Tale of Unexplored Worlds.mp3" # Analog Hero Looping
    define music_intro = "Music/In Legends.ogg" # Old World Vanishing
    define lonelymusic = "Music/Death.ogg"
    define townmusic = "Music/DFS - Teamwork.mp3"
    define quirkymusic = "Music/Shenanigans.ogg"
    define sadmusic = "Music/Alcaknight - Desolate.mp3"
    define sadbuthopefulmusic = "Music/Forever Forward.mp3"
    define victory_moodmusic = "Music/A Young Lady's Sorrows.mp3"
    define guitarjourney = "Music/Guitar on the Water.ogg"
    define lettinggointro = "Music/Letting Go Intro.ogg"
    define lettinggomain = "Music/Letting Go Main.ogg"
    define hope = "Music/Akashic_Records_-_Hopeful_Expectations.mp3"
    define darksecrets = "Music/12-26438-Celestial Aeon Project-Dark Secrets.mp3"
    define despairmusic = "Music/Alcaknight - Despair.mp3"
I do this so for a couple reasons:
  • Easier to type the variable name vs the entire path every-time
  • If I want to change this value (not when the game is running) it only has to be changed in one place.
The second reason is the biggest power and true value of using define, if you define values they only have to be changed in one place to effect everywhere that they were used.

So lets say you are using stats, and when the stats change they increase by 10 everytime.
Python:
label SomeEvent:
    $ somecharacter_lust += 10
label SomeOtherEvent:
    $ someoneelse_lust += 10
label YetAnotherEvent:
    $ yetanothercharacter_lust += 10
Then later in development you realize that "lust" stat needs to increase by 15 instead, so now you need to search your code and make that replacement manually. That is a pain in the ass and what if you miss one? That's a bug you just created.

It would have been better to define 10 as "lustincrease" instead so that you could do this:
Python:
define lustincrease = 15
label SomeEvent:
    $ somecharacter_lust += lustincrease
label SomeOtherEvent:
    $ someoneelse_lust += lustincrease
label YetAnotherEvent:
    $ yetanothercharacter_lust += lustincrease
So that instead of "hard-coding" the value you just use a variable to represent the current value, which you can always change later (once again not when the game is running to avoid potential errors).

On a related note:
It isn't a great idea to track stats as variables for each character unless you have a incredibly small number of stats and characters. I would advise you to look into using a class that creates a default object to hold that information in. I personally used the code from . This class extends the already existing 'Character' class to hold more information of your choosing.
@Maim Lain also has a on coding a dating sim which shows him creating a class to manage stats and events for characters.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,094
14,737
define Vs default :


The only difference between them is that a variable declared with default is savable from the start, while a variable declared with define will be saved only if its value have been directly changed. Which mean that, like said by @Epaddder , variable declared with define should be seen as constants.

Code:
init python:
    class MyClass():
        static = "This is a constant object"

define static = "This is a constant value."
define neverChange = MyClass()

label start:
    $ static = "This isn't anymore a constant value."
    $ neverChange.static = "This is still a constant object."
    $ neverChange = MyClass()
    $ neverChange.static = "This isn't anymore a constant object."
While the difference can seem meaningless, after all Ren'py deal with the save files by itself, in practice it can change everything in your game.

Firstly, it let you have a clean set of variables. Only savable variables are significant, since they are the only one that change during the game. So, when you use the embedded variable viewer (or an external one), you aren't bothered with insignificant values ; this because it only show variables that will be saved.

Secondly, it let you have some freedom for the recurrent messages. By example, you decide to display something like "Her affection have raised" each time the player perform a good action with one of the girls in the game. One way to do this is :
Code:
define raisedAffection = "Her affection have raised."

label start:
    menu:
        "Act like an asshole":
            pass
        "Be charming":
            $ affection += 1
            "[raisedAffection]"
All you need to change the message that will be displayed everywhere in your game, is to change the line starting by "define". Like the raisedAffection variable is not saved, the change will apply everywhere, whatever how far the player gone in your game.

Thirdly, it let you cap some counter without having to change your code each time you update the game (and so increase the max value). It can also let you have an easy "it's all for this version" feature that, once again, don't need that you update the code of your game with each update.

Code:
define lastKitchenScene = 2
default actualKitchenScene = 0


label kitchen:
    menu:
        "Act like an asshole":
            pass
        "Be charming":
            [the scene]
            call incKitchenScene
    [...]

label incKitchenScene:
    $ actualKitchenScene += 1
    if actualKitchenScene == lastKitchenScene:
        "It's all for this update, but more will be added in the future."
    return
Once again, just change the value you assign to lastKitchenScene and your code will be updated, whatever how far the player have gone with the scenes in the kitchen.
With all you "last[place]Scene" variables declared at the same place, while all the "inc[place]Scene" labels are here and there on your code, you need less than one minutes to change all values and will rarely forget to do it.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,094
14,737
Bellow a little script to help you visualize the difference between variables declared with default, define, in an init block and in a label block.

You don't have permission to view the spoiler content. Log in or register now.
 

mickydoo

Fudged it again.
Game Developer
Jan 5, 2018
2,446
3,545
@anne O'nymous I have been using a laymens way of defining variables so my new version will work from the start and the save by calling a label depending on where you start from. I just removed it from the label and put define Amy_Phone = False above the start where I define the player names and it worked, is there anything that can go wrong with this method?
 

goobdoob

Conversation Conqueror
Modder
Respected User
Dec 17, 2017
7,425
9,680
@anne O'nymous I have been using a laymens way of defining variables so my new version will work from the start and the save by calling a label depending on where you start from. I just removed it from the label and put define Amy_Phone = False above the start where I define the player names and it worked, is there anything that can go wrong with this method?
Better would be to put "default Amy_Phone = False". That will always get set to False on save load, if it's not defined already. You don't have to worry about positioning it like you talk about.
 
  • Like
Reactions: anne O'nymous

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,094
14,737
I just removed it from the label and put define Amy_Phone = False above the start where I define the player names and it worked, is there anything that can go wrong with this method?
It depend of what you use as variables. If they aren't explicit object, it will works fine, but if they are, everything will go wrong.
So, it's better that you replace define by default. It will ensure that the variables will always be saved and, like said by @goobdoob , will give them their default value if they aren't present in the save file.


[side note: seriously, it feel weird every time I need to say "everything that isn't an object". In Python the only thing that isn't an object is literal number ; even literal strings and booleans are objects...]
 
  • Like
Reactions: mickydoo

mickydoo

Fudged it again.
Game Developer
Jan 5, 2018
2,446
3,545
Where I am getting confused is in this example -

Ignoring the demo, my next chapter is the 2nd chapter. When a new player starts the game I want Amy_Phone = False so if you try and call someone it does not give you the Amy_Phone is not defined error. At the start of the second chapter she gives you her number so I make it Amy_Phone = True when she does. The way I am understanding the difference between define and default, if it's default on not define, if you save the game after she gives you the number, it will default back to False, or am I reading it wrong.

edit just read up, I had jumped here from another thread. - If the player loads a save game that DOES have the variable in... it just overwrites the default value with the value from the save file.

I get it now
 

goobdoob

Conversation Conqueror
Modder
Respected User
Dec 17, 2017
7,425
9,680
It depend of what you use as variables. If they aren't explicit object, it will works fine, but if they are, everything will go wrong.
So, it's better that you replace define by default. It will ensure that the variables will always be saved and, like said by @goobdoob , will give them their default value if they aren't present in the save file.


[side note: seriously, it feel weird every time I need to say "everything that isn't an object". In Python the only thing that isn't an object is literal number ; even literal strings and booleans are objects...]
Technically, literal numbers are objects.

Where I am getting confused is in this example -

Ignoring the demo, my next chapter is the 2nd chapter. When a new player starts the game I want Amy_Phone = False so if you try and call someone it does not give you the Amy_Phone is not defined error. At the start of the second chapter she gives you her number so I make it Amy_Phone = True when she does. The way I am understanding the difference between define and default, if it's default on not define, if you save the game after she gives you the number, it will default back to False, or am I reading it wrong.

edit just read up, I had jumped here from another thread. - If the player loads a save game that DOES have the variable in... it just overwrites the default value with the value from the save file.

I get it now
Yes, you've got it on the edit - the default value will be used if it's not set in the save. And of course, the start of the game there is no load, so the default will be used. Just put the variable definition (using default) before its first use and you'll be good, even if the save is from a version before the variable was introduced, and is located after the definition.
 
  • Like
Reactions: mickydoo

mickydoo

Fudged it again.
Game Developer
Jan 5, 2018
2,446
3,545
I just been trying it out and it's working perfect, you people have just made my life a whole lot easier, this issue has been doing my head in from the get go.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,094
14,737
Technically, literal numbers are objects.
But practically not ; at least for Ren'py (it's bad but lately I use the SDK console more than Python's one).
"string".title() and False.bit_length() will works. But 1.bit_length() will throw an exception because of the expected float.


But none of this help me to find a better way to say "except if it's object" ;)
 

goobdoob

Conversation Conqueror
Modder
Respected User
Dec 17, 2017
7,425
9,680
But practically not ; at least for Ren'py (it's bad but lately I use the SDK console more than Python's one).
"string".title() and False.bit_length() will works. But 1.bit_length() will throw an exception because of the expected float.


But none of this help me to find a better way to say "except if it's object" ;)
It's a bit sketchy. The documentation for type() says it takes an object. It will take a number. But a number won't let you do things like .bit_length(), like you said.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,555
2,170
At the start of the second chapter she gives you her number so I make it Amy_Phone = True when she does.
[...] if you save the game after she gives you the number, it will default back to False, or am I reading it wrong.

edit just read up, I had jumped here from another thread. - If the player loads a save game that DOES have the variable in... it just overwrites the default value with the value from the save file.
Yup. Exactly that.

defines don't get saved (and in theory, should never be changed).
defaults are always saved... at least at the level of complexity my OP was intended to help with.

Think of it working like this.
  • Your game starts... all variables setup using default are given their default values.
  • If your player starts the game... well, things just continue with that same default value, until it's changed by your code.
  • If your player loads a game instead... the value saved in that save file replaces the default. Maybe it's the same, maybe it's not. If the value isn't in the save file, things just continues with the default value.
The primary advantage is that, say you're working on ch3 and notice there was a variable you probably should have added in ch1... There are players out there with save files that don't include that variable. If you have a if amy_phone == True: in ch3 and one of those players reach it... RenPy crashes. If however you use default, that variable always exists. Players with save files that include it, will just overwrite the value each time they load. Players with save files that don't include it will get the default value.

It's not a perfect solution, because there's a good chance that because you're writing a porn game that you don't actually want the default value for most players loading old save files. But that's what label after_load: is for... and that is a way more complex topic than I wanted to cover in this "newbie friendly" thread.
 

khumak

Engaged Member
Oct 2, 2017
3,432
3,472
Great to see tutorials/guides like this. I've been modding for a little while now and considering trying to make my own game and this is exactly the type of thing I love to see to help me avoid shooting myself in the foot as I get started.