Making Variables Behave Properly in Ren'py

Tags: , , , , ,

This is the approach I take to making Ren'py variables behave properly during rollback and loading, including the ability to iterate through a list. There's probably better ways to do this, but this is what I got.

Big thanks to Bob from Bob C Games, and everyone else who gave me useful advice on the LGBTQ+ Visual Novel Dev discord server.

Basics:

I'm not going to try to fully explain how variables work in Ren'py beyond the stuff in the documentation, because to be honest I don't entirely understand it. But a few useful facts:


So for example, a simple project might have a "00 variable definitions.rpy" file that looks like this:
define ernie = Character("Ernie")
define bert = Character("Bert")

default ernie_happiness = 0 default bert_happiness = 0

And then in the script you might have something like
menu:
"Who's your favourite?"
"Bert":
$bert_happiness += 1
bert "Thanks."
"Ernie":
$ernie_happiness += 1
ernie "Yay!!"
"I like everyone":
$bert_happiness += 1
$ernie_happiness += 1
"Ernie and Bert are very happy!"    
and the variables should all work the way you want.

Which is fine for small numbers of variables but gets impractical when you have lots of variables you want to iterate through. The rest of this post describes my somewhat hacky way around this.

Initialising variables in a renpy file created via a python script:

You could use another language to do this but python is what I'm used to. You can download the latest version of python via the python website.

Here's an example python file that creates a "00 variable definitions.rpy" renpy file:
name_list = ["ernie","bert"]

def make_variable(name): output = "## Variables for " + name.capitalize() + "\n\n" output += "define " + name + " = Character("" + name.capitalize() + "")\n" output += "default "+ name + "_happiness = 0\n\n" return output

f=open("00 variable definitions.rpy","w") f.write("define name_list = ["ernie","bert"]\n\n") for name in name_list: f.write(make_variable(name)) f.close

If this file is called "make_variables.py" it would be run from the command line inside the relevant folder with the command "python make_variables.py". This would produce a file called "00 variable definitions.rpy" that looks like this:
define name_list = ["ernie","bert"]

##Variables for Ernie define ernie = Character("Ernie") default ernie_happiness = 0

## Variables for Bert define bert = Character("Bert") default bert_happiness = 0

The extra variable "name_list" is added so we can iterate through the names as below.

Looping through variables with settattr and getattr

Normally when you call or set a variable in python or Ren'py you use the variable's name. But the functions settattr and getattr use the string of the variable's name, which allows us to use string functions.

So for example, here's some simple Ren'py code:
$hungry = True
if hungry:
"I am hungry!"
And here's some equivalent code using settattr and getattr to access renpy's 'store' object, which contains all the variables:
$setattr(store,"hungry", True)
if getattr(store,"hungry"):
"I am hungry!"
Now for a more complicated example. Instead of:
python:
bert_happiness += 1
ernie_happiness += 1
you can write:
python:
for name in name_list:
setattr(store, name + "_happiness", getattr(store, name + "_happiness") + 1)
Obviously this is a bit overcomplicated for two variables, but for longer collections of variables it's can be super useful.

It can be useful to define python functions to use in your script. For example, here's a function to test if all the characters are happy:
init python:

def all_happy(): for name in name_list: if getattr(store, name + "_happiness") < 1:

return False

return True

You could call it in your script like this:
if all_happy():
"Everyone is happy!"
else:
"At least one person is unhappy."