Ren’Py is an authoring system for interactive narratives, originally intended to facilitate the development of visual novels. It has an easy-to-use scripting language that also provides access to the full Python programming language that the system uses under the hood. Ren’Py supports builds for major desktop and mobile platforms and in recent versions even supports HTML5 export. Nifty!
This tutorial goes over the basics of how Ren’Py works, with a focus on the aspects of the scripting language most related (in my opinion) to creating dialogue and branching narratives.
To begin, download Ren’Py and uncompress the files to a convenient location. Start the Ren’Py Launcher application appropriate for your platform. Once the Launcher starts, click Create New Project and follow the prompts.
Creating a new project creates a folder with boilerplate code and assets. The options in the “Open Directory” section of the Launcher will show this folder in your operating system’s file browser.
Code for your project should go in a file called
script.npy
. You can edit this with any text editor.
Double-clicking on the file in the Launcher (under “Edit File”) will
open the file in your default text editor.
To run your game, click Launch Project. While the game is running,
press Shift+D
to open a menu that shows various helpful
debug scripts.
Something to note about Ren’Py: It’s Python-based, which means that whitespace matters. Indentation marks blocks of code. (In other programming languages, you’d do this same work with curly brackets.)
One of the main ways to organize your Ren’Py games is through the use
of labels. Labels group a sequence of statements that can be
jump
ed to (see below). The only required label in your
Ren’Py game is start
. The game begins at this label.
The following example shows Ren’Py doing the thing that Ren’Py does best: display text to the screen and wait for the user’s confirmation.
label start:
narrator "Hello, world!"
narrator "This is a test."
(Indentation is how Ren’Py groups statements, so make sure to get the indentation right.)
The syntax to display text is <character> string
.
The word narrator
denotes a special built-in character used
to show text without attribution. Because this is so common, Ren’Py just
lets you leave this out:
label start:
"Hello, world!"
"This is a test."
Displaying another attribution:
label start:
"Mortimer" "Hello, world!"
"Horatio" "This is a test."
Absent any other statements to control the flow of the game, labels will flow from one to the next:
label start:
"This is a test."
"Nice."
label farmyard:
"Let's go to a farmyard!"
label forest:
"Now we're deep in a forest."
The jump
statement moves execution to the named
label:
label start:
"This is a test."
jump forest
label farmyard:
"Let's go to a farmyard!"
label forest:
"Now we're deep in a forest."
(Jumping between labels isn’t terribly useful on its own, but is very powerful when combined with menus; see below.)
By the way, game execution stops when the end of the script is
reached, or when the return
statement is used:
label start:
"This is a test."
jump forest
label farmyard:
"Let's go to a farmyard!"
return
label forest:
"Now we're deep in a forest."
jump farmyard
Also, you can add comments to your code with #
:
# here's where it all begins!
label start:
narrator "Hello, world!"
narrator "This is a test." # so it is!
Aside from showing text and waiting for confirmation, the other main user interface element that Ren’Py provides is the menu. A menu presents a number of options and lets the user pick among them. Here’s an example:
label start:
menu:
"Breakfast":
"Delicious bacon and eggs!"
"Brunch":
"Bacon and eggs and mimosas!"
"Lunch":
"Soup and salad special..."
"I hope you enjoyed your meal!"
The menu:
keyword introduces a sequence of options, each
indented once. When the user picks an option, the statements indented
beneath that option’s text are executed. Execution then resumes at the
statement after the menu
block.
Menus are especially effective when used in combination with the
jump
statement, which causes the game to start executing
statements starting at the named label. This allows you to create simple
structures of branching narrative:
label start:
"Welcome to the hall... of choices"
"Alexandra" "Choose, but choose wisely."
menu:
"Cheese and crackers":
"Ah yes, fancy appetizers! Nothing better."
"Alexandra" "A wonderful choice."
jump cheese
"Fast food burger":
"Sometimes you've got to treat yourself."
"Alexandra" "I'm loving it!"
jump burger
label cheese:
"Alexandra" "Enjoy your brie!"
return
label burger:
"Alexandra" "I'm such a huge Shake Shack fan!"
return
Statements beginning with dollar signs ($
) are
interpreted as Python code. You don’t need to be a Python wizard to use
Ren’Py! But there are a few common patterns that it’s useful to be
familiar with. One of the main ways to use Python code in your game is
to is to set or modify variables, allowing you to keep track of
game state. The default
keyword at the beginning of your
script sets the default value for these variables. Variables set this
way are global and can be used in other labels. The following example
presents a menu to the user and sets variables based on their
choice:
default tomato = 0
default funicular = 0
label start:
"Alexandra" "Tomato or funicular?"
menu:
"Tomato":
$ tomato = 1
"You eye the tomato. Its ripeness beckons you."
"Funicular":
$ funicular = 1
"You've always wanted to ride one of these!"
"Neither":
"You're not sure how to decide among such riches. You hesitate."
if tomato == 1:
"Alexandra" "Ah yes, my favorite fruit!"
elif funicular == 1:
"Alexandra" "Enjoy your time on the mountain!"
else:
"Alexandra" "Hmph. You should be more decisive."
The if
/elif
/else
structure at
the end of this example is based on Python syntax (elif
is
Python’s way of writing else if
).
These variables don’t have to be numbers; they can contain any Python data type. This example imagines a simple system for tracking character traits using a Python dictionary:
default traits = {'kind': 0, 'smart': 0, 'hungry': 0}
label start:
"Alexandra" "How are you doing today?"
menu:
"I'm well. It's good to see you! How are you?":
$ traits['kind'] += 1
"I am operating within established parameters. You?":
$ traits['smart'] += 1
"I'm hungry. Very hungry.":
$ traits['hungry'] += 1
if traits['hungry'] > 0:
"Alexandra" "You poor soul! Here, have some soup."
else:
"Alexandra" "I'm fine, thank you! Now, down to business."
A very helpful feature for debugging Ren’Py scripts is the
console. Type Shift+O to open it. You can execute arbitrary
Python commands in the console, but a particularly helpful thing to try
out is to just type the name of the variable you’re using to keep track
of game state and press Enter. You’ll see the value that is currently
stored in the variable. (Type exit
to close the
console.)
Visual novels are famously… well, visual. So let’s add some
visual media to the game. Ren’Py has a few commands you can use to show
images. The first is the show
command, whose syntax looks
like this:
show tag attribute1 attribute2 attribute3 ...
Ren’Py will load an image from the images
folder in your
project directory based on the tag
and attributes that you
supply to the show
command. For example, the following:
show kitty smiling
… would load and display “kitty smiling.png” from your images folder. (Case is ignored when looking for files. Background art, discussed below, can also be supplied in JPEG format.)
Ren’Py will keep an image displayed with show
on the
screen until one of the following happens: (a) the show
command is called again with the same tag (but different attributes);
(b) the hide
command is called with the image’s tag; (c)
the scene
command is called (see below).
I have supplied a file containing a few sample visual assets that you can download and put in your project’s images folder if you want to follow along with the examples below. (You can access your project’s images folder by clicking “images” under “Open directory” in the Ren’Py launcher.)
label start:
show adelaide
"Adelaide" "Hello."
show adelaide glow
"Adelaide" "HOW ARE YOU"
hide adelaide
"Adelaide vanishes in a glowing puff of smoke!"
Images are shown centered in the middle of the screen by default,
with the bottom edge of the image aligned to the bottom of the screen.
To adjust this, you can supply a position to the
show
command. The syntax looks like this:
show tag attribute1 attribute2 ... at <position>
… where <position>
can be replaced with
left
, right
or center
. (The
technical term for these positions in Ren’Py is
transformations. The left
, right
,
center
and truecenter
transformations are
built-in, but you can program your own
transformations as well.)
The hide
command, as mentioned above, hides the image on
screen with the given tag.
An alternative way to display an image is scene
, which
hides all other images currently on screen and the displays the
specified image (following the filename conventions related above):
label start:
scene bg orbit
show adelaide
"Adelaide" "Welp, it was good to see you! Have fun on the surface."
scene bg surface
"The great cratered surface of the planet stretches before you."
Ren’Py comes with a number of built in transition effects, which affect the way that images appear and disappear when you add them to the screen. A full list of built-in transition effects is available in the Ren’Py tutorial. Here’s an example, riffing on the short script above:
label start:
scene bg orbit
show adelaide
"Adelaide" "Welp, it was good to see you! Have fun on the surface."
scene bg surface
with dissolve
"The great cratered surface of the planet stretches before you."
show adelaide
with moveinleft
"Adelaide" "Whoops, you forgot your glasses. Here you go! Bye!"
hide adelaide
with moveoutright
Ren’Py also provides easy commands for playing sounds and video. See the Music and Sound portion of the official tutorial, or the Movie section in the Ren’Py documentation.
It’s common in Ren’Py to create Character
objects for
each character that speaks. For more detail about the many affordances
of Character
objects, see the Dialogue and
Narration section from the Ren’Py documentation. For our purposes,
making a Character
object has two benefits:
"Adelaide" "Hello"
), and let you
change the name of the character without having to search and replace
every instance of the character’s name throughout the game;Making Character
objects looks like this:
define a = Character('Adelaide', color="#ff4040")
define b = Character('Bernardine', color="#00c000")
label start:
show adelaide
a "Hello."
show bernardine
b "Hi."
More information on text styles and interpolation.
define a = Character('Adelaide', color="#ff4040")
label start:
scene bg orbit
show adelaide at left
a "How are {i}you{/i} feeling today?"
menu:
"Just rotten.":
$ mood = "crummy"
"Pretty great!":
$ mood = "fine"
a "Hmm, okay. It's good to know you're in a [mood] mood."
a "A {b}very{/b} [mood] mood."