The Definitive Default Define Ren’Py Article

The Definitive Default Define Ren’Py Article

Have you just been told to “default your variables”? Having trouble getting your variables to save and load properly? Not sure why you’re getting a NameError in your script? Here is the tutorial for you.

Difficulty Level: Beginner

This tutorial expects you to have a basic idea of what variables are and how to use them in your game. I recommend you start with A Quick Primer on Variables in Ren’Py if you’re new to variables in general.

Default

Though I’ve touched on this topic in A Quick Primer on Variables in Ren’Py, I haven’t gone into depth about the difference in using default from other methods like define or just declaring it in an init python block.

default should be used to declare every single changeable value in your game, with extremely few exceptions (such as very minor temporary values which are never reused). These changeable values are commonly called variables. The default statement happens at init ( initialization) time, which is the period of time between when you hit “launch” and when the game actually opens.

default should always be at the leftmost level of indentation inside a Ren’Py rpy file (sometimes also called the “top level”), and not inside something like a label. (Note that default can be used inside screens to create local variables, but that’s outside of the scope of this tutorial).

Variables you declare with default are saved in save files and remembered for later. This is important because it is the only method which correctly allows for save compatibility. You will run into problems with all other variable declaration methods.

default is also notable because it helps with save compatibility between game versions. If you update your game to add new content, past save files will automatically get the default value provided in your default statement if a variable didn’t exist at the time the save was made. I explain this further in A Quick Primer on Variables in Ren’Py.

An example of default looks like:

## POINTS
default xia_points = 0
default ashwin_points = 0
default zoran_points = 0

## CHOICES
default went_to_park = False

label start:
    "Once upon a time..."

Define

define should be used exclusively for constant values (aka  constants), that is, values which do not change over the course of playing the game.

Most GUI values are declared using define, because once they are set up, they should not change during gameplay. They should also remain unaffected through different save files. It’s also very common to declare Character objects using define, such as define x = Character("Xia"), since a character’s name and any styles relating to displaying their dialogue should generally be set before the game and not touched afterwards.

Values declared with define are not saved or loaded, and do not participate in rollback. Attempting to change them during gameplay may result in undefined behaviour (aka, it could do what you want, but likely it will cause problems down the line).

An example of what define looks like:

define gui.text_font = "DejaVuSans.ttf"
define gui.name_text_font = "ComicSans.ttf"

label start:
    "You" "I sure do love Comic Sans."

init python

If you declare a value in an init python block, it’s treated approximately the same way as if it had been declared using define. Something like

init python:
    settings_color = "#ffffff"

is approximately equivalent to

define settings_color = "#ffffff"

I say “approximately” because using define instead of declaring it in an init python block provides several advantages, such as including Lint information for checking your script for errors. See also: https://www.renpy.org/doc/html/python.html#define-statement.

So, in short: this code is run during init time (before the game starts), and these values are not saved and loaded nor do they participate in rollback. If you attempt to change them during gameplay you may get undefined behaviour. You should treat values defined in an init python block as constants.

Using $ in script

If you set a variable using $ in script (e.g. $ xia_points = 0 after the start label) without using default to declare that variable beforehand, you will run into problems with save compatibility. See: Why use default?

If you have a label that looks like

label variables():
    $ xia_points = 0
    $ ashwin_points = 0
    $ zoran_points = 0

with just a bunch of lines setting up variables inside a label, there is an extremely high chance you should turn every single one of those lines into a default statement i.e.

default xia_points = 0
default ashwin_points = 0
default zoran_points = 0

label start():
    "Now the game starts."

As described in more detail in the link above, if you do not use default, players who saved their game after the code setting up the variable will encounter a  NameError  if you ever try to access that variable (because it won’t exist in their save file). You will also run into issues if you ever end up with a path in your game where a particular variable is never set, and then later on you attempt to check that variable.

For example, if you have a variable that you set like $ encountered_dragon = True during your game, which you didn’t set up with default, for players who played in a way that a line like $ encountered_dragon = True was never run, checking if encountered_dragon: will throw a NameError and crash the game.

Save Files and Declaration Methods

The different methods of setting up variables cause them to interact differently with Ren’Py’s save and load system.

Save files and default

As described earlier in this article, using default to set up a variable means it will be saved when the player makes a save file, and the value at the time they saved will be loaded when they load that save file. This does mean that if you change the starting value of a variable you set up with default, those changes will not affect game saves made prior to the change.

For example, consider the following code:

## POINTS
default xia_points = 0

label start:
    "Once upon a time..."
    # Make a save on this next line
    "There lived a little girl in a cottage in the woods."

Let’s say later, after making this save file, I decided that I actually wanted to change the starting point values:

## POINTS
default xia_points = 10 ## NEW!

label start:
    "Once upon a time..."

Now, if a player starts a new game, they will begin the game with xia_points = 10.

However, if I load my old save file – the one I made on the line “There lived a little girl in a cottage in the woods.” – xia_points will be equal to 0. That’s because at the time I made the save, that’s what xia_points was equal to. Even though I have the line default xia_points = 10, when Ren’Py loads my save file, it sees that xia_points already has a value, 0, so it doesn’t change it.

This is because Ren’Py has no way of knowing if you want to change that value now that it’s been saved, nor what to change it to if you do. If you imagine a save file where the player has accumulated 15 points with Xia to bring xia_points up to 15, loading their save after updating default xia_points = 10 probably shouldn’t change xia_points to be 10 and discard their progress.

If xia_points should be equal to 10 in all new and old save files, it’s possible you should use define instead; continue reading below for more on that. Otherwise, if you are in development with an unreleased game, just know that you can’t use old save files to test variables you recently changed the default values of. And if you’re supporting an episodic game or updating a release, you will probably need to make use of the after_load label and after_load callbacks. These topics will eventually be covered in a separate tutorial on designing for save compatibility.

Save files and define

When you set up a value with define, it is expected to be constant. This means Ren’Py will not save it in save files, will generally not check for updates on it (to save on computing time for things like screen prediction), and will assume all save files should use whatever value it was set up with in the define statement.

So, say you started with the following code:

define audio.zorans_theme = "audio/music/slow_jazz_piano.ogg"

In your code, you play this song with play music zorans_theme during scenes with Zoran. Later, you find a different piece of music you want to use for Zoran’s theme instead. You update your define statement:

define audio.zorans_theme = "audio/music/slow_jazz_orchestra.ogg"

Because you used define to set up this value, regardless of whether the player starts a new game or loads up an old one made before the song change, play music zorans_theme will always play the current value of audio.zorans_theme, so in this case, the slow_jazz_orchestra version rather than the slow_jazz_piano one. If you had used default instead, old save files would still look for slow_jazz_piano, which is unlikely to be what you wanted in this case.

Examples

If you’re wondering whether to use default or define, think about what should happen if a player loads an old save file of your game. Should the value stay at whatever it was when they saved the game? Or should it update?

If the value should be tracked in a particular playthrough and it changes over the course of that playthrough, use default to correctly flag it for save files. This is also true for persistent variables, which change throughout the game but aren’t tied to a particular playthrough.

If changing the value should instead mean that all save files and aspects of the game should use whatever the new value is, then you should use define. Remember that define doesn’t necessarily mean you can’t change the value – just that you can’t change it as part of gameplay and only as part of development. You might decide to update the game’s default font, for example. If the default font is updated, everywhere that uses that font should be updated.

Consider the below examples.

Example 1

You have a side character b = Character("Baker") who says dialogue for the baker character. Later in development, you decide to give the baker a name, Ines. All save files should show dialogue being from Ines instead of Baker. Should you use default or define to declare the Character?

define is the correct choice here. You would update the declaration to read define b = Character("Ines") and all lines said by b for any save file would now use the name Ines for dialogue by the baker.

Example 2

You picked the pink-and-black default theme when setting up your Ren’Py game. Now you have custom assets and want to update the colours to be pale blue and white. Should default or define be used to declare these values (e.g. text colours)?

define is the correct choice. It wouldn’t make sense to load a save file and have it bring up the old pink-and-black colour scheme just because that’s what it was when that file was saved. All save files should use the new blue-and-white colours. This is why GUI values are declared with define in gui.rpy like define gui.text_color = "#ffffff".

Example 3

You release version 1.0 of your game demo. In the v1.1 update you are preparing which adds more scenes to the demo, you decide you need a way to track how many affection points the player gets with a particular character. There are some choices in the scenes from v1.0 that should increase these affection points. How should you track the affection points?

default is the correct choice here, probably something like default xia_points = 0. For players who made a save after the parts where they would have received affection points with Xia, there is no way to retroactively give them those points unless you had some way of knowing they made those choices (which you would’ve had to code in v1.0, like $ gave_xia_flower = True, and then you can use that in an after_load label or after_load callbacks to figure out how many points the player should have).

Regardless, the player should still be able to continue from a base level of 0 points to gather more affection points on their current save. This value will be changed during gameplay, so you must use default.

Example 4

You’re working on a turn-based battle system where characters have various stats that can increase with a small amount of randomness when they level up. You want to cap their stats at some max value (say, 100), but you might end up changing this number depending on testing. How should you declare this maximum stat value?

The correct choice here is likely define. Unless there is a point in the script after which the stat cap should increase, you probably want the stat cap to just be a constant value used for all save files. If you later decide the stat cap should be 120 instead of 100, a player with an old save file should have the new stat cap automatically.

Example 5

You are trying to track the stats for the stat system mentioned above. Which method should you use to declare them?

You should use default for these stats since they change throughout gameplay and are specific to each save file.

Example 6

Your game has animations, but you want a toggle in the settings screen so players can turn them off in case they’re distracting or too resource-intensive. This setting should be independent of the save file – that is, if the player turns off animations on one save file, the animations will remain off if they load another file (the same way their volume settings don’t change if they load different files). The player should be able to toggle this setting whenever they like during gameplay. How should you declare this animation toggle?

You should use default along with  persistent  to ensure the variable is save-independent. default will let you set up the initial value of a persistent variable, but it won’t change it after the initial value is set. This example might look like default persistent.animations_on = True which will be run once, the very first time the player starts the game. After that it will remember whatever value the player set it to last. default is used here because the value changes, and the game should remember the last value it was set to instead of resetting it each launch.

See also: Basics of Ren’Py #9: Persistent Data over on Lezalith’s website for more information on persistent data.

Example 7 (Advanced)

Note: This example assumes you have knowledge of classes and lists. You can feel free to skip it if you aren’t yet familiar with these concepts.

You have a LoveInterest class which tracks information like affection points and personality traits for a given love interest in the game. You want a list of all the LoveInterest objects so you can loop over it to display their information in a Profile screen. Ideally, it would be nice if this list could automatically update itself if you ever make a new LoveInterest.

This is a tricky situation. Ordinarily, if the values being added to this list were immutable (such as strings or integers), you could define the list and values. However, the values being added to the list are objects, which have attributes like affection_points that may change over the course of the game.

Due to this, if you were to define and then automatically populate a list like define love_interests = [ ] (e.g. by appending each object to the list inside the constructor), the objects in the list will change on every launch of the game (i.e. they will have different addresses). This is bad, because it means when you load a save file, the LoveInterest objects in the list are not the same as the ones you’re modifying in-game.

One solution to this is to use default love_interests, either automatically filling it or adding the LoveInterest objects to it after they’re declared. Then, if you create a new LoveInterest who should be part of the love_interest list, you can use the after_load label (or callbacks) to ensure they are added to the list for old save files. Alternatively, you may consider populating a defined list with the string names of your LoveInterest variables such that you can fetch them using a method like getattr(store, "x") later.

Summary

Declaration MethodExplanation
defaultShould be used for all values that change during gameplay. Sets up a variable with a value when the game launches, before you start or load a game. Declares variables with these default values in old save files if they didn’t previously exist. Remembers the value at the time it was saved and loads that when a game is loaded.
defineUsed only for constant values aka values which never change. Are not saved in save files and will be reset upon every game launch. Can cause undefined behaviour if you change them during gameplay.
init pythonActs functionally equivalent to define. These values should never changeinit python code runs every game launch. Will not be saved in save files and can cause undefined behaviour if you change them during gameplay.
$ in-script or python: in scriptWithout default, these variables do not exist unless that particular line of code is run. Can lead to issues with save compatibility and NameError if a variable is not created by the time you go to check it. Otherwise, can be used without problem to change variables set up with default.

Next Steps

If you’re new to Ren’Py in general, it’s probably a good idea to take a look through my tutorials on variables to learn more about tracking information in your game. You might also be interested in my collaboration with Lezalith over on his website, which explains persistent variables.

Leave a Reply