Ren'Py is a programming language and runtime, intended to ease the creation of visual-novel type games. It contains features that make it easy to display thoughts, dialogue, and menus; to display images to the user; to write game logic; and to support the saving and loading of games.
Ren'Py is implemented on top of python, and that python heritage shows through in many places. Many Ren'Py statements allow python expressions to be used, and there are also Ren'Py statements that allow for the execution of arbitrary python code. Many of the less-used features of Ren'Py are exposed to the user by way of python. By only requiring use of the simplest features of python, it's hoped that Ren'Py will be usable by all game authors.
The following is a simple but complete Ren'Py script. The colors are added to make it easier to read, and aren't part of the script proper.
init: image whitehouse = Image("whitehouse.jpg") image eileen happy = Image("eileen_happy.png") image eileen upset = Image("eileen_upset.png") label start: $ e = Character('Eileen') scene whitehouse show eileen happy e "I'm standing in front of the White House." show eileen upset e "I once wanted to go on a tour of the West Wing, but you have to know somebody to get in." "For some reason, she really seems upset about this." e "I considered sneaking in, but that probably isn't a good idea."
This example, shows many aspects of a Ren'Py script. The first four lines of the script serve to load in three images. After the label indicating the start of the game, a character is declared. The script then proceeds to display a picture of a character on top of a background image, and to have the character say two lines of dialogue, changing her picture in between. The POV character then thinks a line of dialogue, before the character says her final line.
We'll go into detail into what each of the statements here does over the course of this tutorial. For now, however let me just point out that the first 6 statements inialize the game, while the last 7 (starting with "scene") show images and display dialogue. As such, the bulk of a game is more like the last 7 then the first 6.
Of particular note is that a keyword isn't required to introduce dialogue. This allows visual novels consisting mostly of dialogue to be expressed in a concise form.
The largest division of a Ren'Py script is into files. By default, Ren'Py reads the script from all files ending in .rpy found in the game underneath the directory in which Ren'Py is installed. These script files may be read in any order, and all of them together make up a Ren'Py script.
Each of these files is divided into a series of logical lines. The first logical line of a file begins at the start of a file, and another logical line begins after each logical line ends, until the end of the file is reached. By default, a logical line is terminated by the first newline encountered. However, a line will not terminate if any of the following are true:
These rules should be the same as for Python.
Ren'Py also supports comments. A comment begins with a hash mark that is not contained within a string, and continues to, but does not include, the next newline character. Some examples are:
# This line contains only a comment. scene whitehouse # This line contains a statement as well.
If, after eliminating comments, a logical line is empty, that logical line is ignored.
Logical lines are then combined into blocks. Two logical lines are in the same block if the lines have the same indentation preceding them, and no logical line with a lesser amount of indentation occurs between the two lines. In the following example:
line 1 line a line b line 2 line c line d
There are three blocks. One block contains lines 1 and 2, another lines a and b, and the third contains lines c and d. This example can also serve to illustrate the concept of a block associated with a line. A block is associated with a line if the block starts on the next logical line following the line. For example, the block containing lines a and b is associated with line 1.
There are three kinds of blocks in an Ren'Py program. The most common is a block containing Ren'Py statements. Other blocks may contain menu entries or python code. The top-level block (the one that contains the first line of a file) is always a block of Ren'Py statements.
Before we can discuss statements, however, we must first discuss the tokens statements are built up out of. So here's a short list of all the tokens we use.
Keywords are words that appear in the source code. They're used to introduce a statement, or to delimit parts of a statement. You'll see keywords throughout the descriptions of statements. In grammar rules, keywords are in quotes.
A names consist of an alphabetic character or number, followed by zero or more alphabetic characters or underscores, so long as the string isn't a keyword.
An image_name is a list of one or more names, separated by a space.
A string begins with a " or a ', and continues until a matching unescaped " or ' is reached. Runs of whitespace inside a string are collapsed into a single space character, allowing strings to span multiple lines. The \ character is used inside the string to escape special characters, such as whitespace, quotes, and (as \n) to include a newline.
A simple_expression is a python expression that starts with a name, a string, or any python expression in parenthesis. This may be followed by any number of the following:
In general, simple expressions are strings, names, or method calls. They are not expected to contain operators.
A python_expression is an arbitrary python expression that may not include a colon. These expressions are generally used to express the conditions in the if and while statements.
We will be giving grammar rules for some of the statements. In these rules, a word in quotes means that that word is literally expected. Parenthesis are used to group things together, but they don't correspond to anything in the source code. Star, question mark, and plus are used to indicate that the token or group they are to the right of can occur zero or more, zero or one, or one or more times, respectively.
If we give a name for the rule, it will be separated from the body of the rule with a crude ascii-art arrow (->).
As the bulk of the content of a visual novel is presented to the user in the form of dialogue or thoughts, it's important that the ability to display text to the user be as convenient as possible. In Ren'Py, both actions are done through the say statement. The say statement doesn't require a keyword to introduce it. Instead, it consists of either a single string, or a simple_expression followed by a string.
We can distinguish two forms of the say statement, depending on if the simple_expression is provided. The single-argument form of say consists only of a single string. This form causes the string to be displayed to the user without any label as to who is saying it. Conventionally, this is used to indicate to the user thoughts or narration.
"I moved to my left, and she moved to her right." "So we were still blocking each other's path." "I then moved to my right, and at the same time she moved to her left." "We could be at this all day."
The two-argument form of the say statement first evaluates the expression to see what its value is. If the expression returns a string, that string is used as a character name to indicate who is saying the dialogue. If it returns an object, that object is responsible for displaying the dialogue to the user.
The most common type of object used in a dialogue statement is a Character object. Character objects have associated with them a name and a color. When a character object is asked to display a line of dialogue, it labels it with the character name in the character's signature color. In general, strings are used to indicate the names of lesser characters or ones who we have not discovered the name of yet, while character objects are used to indicate important characters.
"Girl" "Hi, my name is Eileen." e "Starting today, I'll be living here."
Finally, the string in a dialogue is subject to interpolation of variables. A string variable can be interpolated with %(name)s, while a number requires %(name)d. For example:
e "I know all about you." e "I know that you're %(player_age)d years old, and your zodiac sign is %(player_sign)s."
Menus present a user with a list of choices that can be made. In a visual novel game, menus are the primary means by which the user can influence the game's story.
A menu statement consists simply of the word menu, an optional name, and a colon. If the name is supplied it's treated as a label for this menu statement, as if the menu statement was preceded by a label statement. (See the section on control flow for details about the label statement.)
The menu statement must have a block associated with it. This block must contain one or more menuitems in it. There are three kinds of menuitems that can be contained in a menu block.
The first kind of menuitem is simply a string. This string is placed into a menu as a caption that cannot be selected. In general, captions are used to indicate what the menu is for, especially when it is not clear from the choices.
The second kind of menuitem gives a choice the user can make. Each choice must have a block of Ren'Py statements associated with the choice. If the choice is selected by the user, then block of statements associated with the choice is executed. A choice may also have an optional if clause that includes a python expression. This clause gives a condition that must be satisfied for the choice to be presented to the user. The terminating colon is what indicates that this menuitem is a choice.
The third kind of menuitem gives an expression that yields a set. There may only be one of this kind of menuitem per menu. If present, it's used to filter the list of choices shown to the user.
When a menu is to be shown to the user, the first thing that happens is that a list of captions and choices is built up from the menuitems associated with the menu. Each of the choices that has an expression associated with it has that expression evaluated, and if it evaluates to false, that choice is removed from the list. Finally, if a set is present, it is checked to see if the text of a choice is in the set. If the text is found, the choice is removed from the list.
If no choices survive this process, the menu is not displayed and execution continues with the next statement. Otherwise, the menu is presented to the user. When the user makes a choice, the text of that choice is added to the set (if one is present), and execution continues with the block associated with the choice. When that block finishes, execution continues with the statement after the menu.
Here's a fairly complicated menu that uses all three kinds of menuitems. Most menus in actual games will not be this complicated.
menu what_to_do: # Ensure that we can only do a given thing once. set what_to_do_set "What should we do today?" "Go to the movies.": "We went to the movies." "Go shopping.": "We went shopping, and the girls bought swimsuits." $ have_swimsuits = True "Go to the beach." if have_swimsuits: "We went to the beach together. I got to see the girls in their new swimsuits."
This menu will only allow a given activity to be chosen once, and will allow the user to chose to go to the beach only if the user has chosen to go shopping.
Without the ability to display images to the user, a visual novel would be a text adventure. Ren'Py controls image display by using a scene list, a list of things to be displayed to the user. Every time an interaction starts (that is, a line of dialogue or a menu is displayed), the things in the scene list are drawn to the screen, with the first being in the back and the last being in the front. A number of statements manipulate the scene list. Before we can explain them, however, we should first define a few terms.
An image_name is a space-separated list of names that's used to refer to an image. This list of names may not include keywords in it. The first element of the image name is known as the image tag, and is treated specially.
A Displayable is a python object implementing an interface that allows it to be displayed to the screen. A transform is a function that, when applied to a Displayable, returns a new Displayable. Transforms are used to change the way an image is displayed to the user. A transform_list is a comma-separated list of transforms. Display lists are applied from left to right.
An image_spec is an image name, an optional at list of transformers, and an optional with list of transformers. The at list is used to contain transforms that apply for the life of the image, such as transforms that change the placement of the image on the screen. The with list, on the other hand, contains transforms that should only be applied the first time the image is displayed, such as transitions. When the user finishes the next interaction, the with clause is removed from the image.
The first display statement is the image statement, which does binds an image name with a displayable defining that image. As the list of name bindings, is never saved, the image statement can only appear inside of an init block. The most popular python expression to use here is Image, which takes as an argument an image filename to load. Another popular choice is Animation, which is defined elsewhere in this document.
An example of image in use is:
init: image eileen happy = Image("eileen/happy.png") image eileen upset = Image("eileen/upset.png")
The next display statement is the show statement, which takes an image specifier and displays it on the screen. If an image with the same tag as the image given in the spec already exists on the scene list, it is replaced with the newly displayed Displayable. Otherwise, the new one is added to the end of the scene list (that is, closest to the user).
If a with list is present, the image, transformed by the at and with lists is displayed to the user for the next interaction. At the end of that interaction, it is replaced by the image transformed with only the with clause. If no with list exists, the image transformed with the at list is immediately displayed.
Automatically replacing an image with the same tag is a useful feature that allows characters to change expression without having to explicitly hide the old image.
The scene statement first clears the scene_list. If the optional image_spec is present, it is shown as if it was shown with the show statement. The best use for the image_spec on a scene command is show a background for the scene.
We can put together the scene and show statements to get the following example:
scene living_room show eileen happy at left e "I'm feeling happy right now." show eileen upset at left e "But sometimes, I can get upset for no good reason."
The hide statement is used to remove an image from the scene list. If the image_spec does not contain a with list, an image matching the tag of the image_spec is removed from the scene list immediately. (In this form, the image name doesn't need to actually exist, so long as the tag is recognizable.) If the image_spec does have a with clause, the image_spec is shown until the end of the next interaction, and only then removed from the scene_list. This allows the hiding of an image to include a transition effect.
Hide is a rarely used display statement. The show statement automatically replaces an old image when a character changes emotion, and the scene image removes all images when the scene changes. Hide is generally only used for when a character leaves in the middle of a scene.
e "Well, I'll be going now." hide eileen "And with that, she left."
These four statements, along with the library of Displayables and transforms provided with Ren'Py, should be enough to render most scenes needed in a visual novel type game.
Control statements change the order in which statements in a Ren'Py script execute. These statements allow for control transfers, conditional execution, and procedure calls.
The label statements assigns a name to a point in the program, allowing control to be transfered to this point by the jump or call statements. The label statement may have a block associated with it. If it does, the statement executed after the label is the first statement in the block. Otherwise, the next statement to be executed is the first statement after the label.
The jump statement unconditionally transfers control to the statement with the given name. If the name does not exist, an error is raised.
label loop_start: e "Oh no! It looks like we're trapped in an infinite loop." jump loop_start
The call statement transfers control to the location given. It also pushes the name of the return site onto the return stack, allowing the return statement to return to the statement after the call site.
If the optional from clause is present, it has the effect of including a label statement with the given name as the statement immediately following the call statement. An explicit label is required here to ensure that saved games with return stacks can return to the proper place when loaded on a changed script. On the other hand, from clauses may be distracting when a game is still under development. A script will be provided to add from clauses to call statements right before a game is released.
If the return stack is not empty, the return statement pops the top return site off of it and transfers control there. Otherwise, it terminates execution without raising an error.
e "First, we will call a subroutine." call subroutine from _call_site_1 e "Finally, we will exit the program." return label subroutine: e "Next, we will return from the subroutine." return
The if statement is used to conditionally execute a block of statements. It is the only statement that consists of more than one logical line in the same block. The initial if statement may be followed by zero or more elif clauses, concluded with an optional else clause. The expression is evaluated for each clause in turn, and if it evaluates to a true value, then the block associated with that clause is executed. If no expression evaluates to true, then the block associated with the else clause is executed. (If else clause exists, execution immediately continues with the next statement.) In any case, at the end of the block, control is transferred to the statement following the if statement.
if points >= 10: e "Congratulations! You're getting the best ending!" elif points >= 5: e "It's the good ending for you." else: e "Sorry, you're about to get the bad ending."
The while statement executes its block while the expression is true. Specifically, each time the while statement executes, it evaluates the expression. If the expression is true, control is transferred to the first statement of the block associated with the while loop. If it is false, then control is transferred to the next statement. The while statement is the statement that normally executes after the last statement in the block, causing the condition to be evaluated again and the loop to repeat.
This definition of a while loop means that it would be hard to implement statements like python's "continue" or "break". These statements can be easily faked with labels in the right places and jumps to those labels. This definition also means that it's possible to jump into the middle of the block associated with a while loop and, if at the end of the block the condition is true, have the while loop repeat the block.
while not endgame: "It's now morning. Time to get up and seize the day." call morning call afternoon call evening "Well, time to call it a night." "Now it's time to wake up and face the endgame."
The pass statement can be used where a block is required, but there's no statement that can be placed in that block. When executed, pass has no effect.
For example, pass can be used in a menu if we don't want to take any action when a choice is selected.
menu: "Should I go to the movies?" "Yes": call go_see_movie "No": pass "Now it's getting close to dinner time, and I'm starving."
The init statement is used to introduce a block of code that should be run when the game first starts. When the game is first loaded, the script is scanned for init blocks, and code in init blocks is run in an arbitrary order. An init statement encountered during execution, however, is treated as a pass statement, and the block is not executed.
There are two Ren'Py statements that allow python statements to be mixed with Ren'Py code. Any statement beginning with a dollar-sign ('$') will be interpreted as python code extending to the end of the logical line. This form can only include a python statement containing a single logical line. (So python control constructs cannot be used.)
The other way to introduce python statements is with a python block statement. The block associated with this statement, along with any block inside those blocks, is interpreted as python code that is passed to the python interpreter. The block nesting structure is reflected in the python code that is interpreted, so that python control structures will work as advertised.
If the optional hide keyword is added to a python block statements, local variables created in the block will not be added to the store. The variables in the store can be accessed as attributes of the store, however.
# Toggle fullscreen mode. $ config.fullscreen = not config.fullscreen # Pointless python that uses a loop. python: for i in ('e', 'l'): globals()[i].points = 0
In general, if a Ren'Py construct exists that does what you want (like while or if), it should be used in preference to a python block, unless a large amount of code is to be executed with no user interaction.
When Ren'Py is first invoked, it first tries to parse all the .rpy files in the game directory. If at least one .rpy file exists, it is loaded, and the script is then written out in a serialized form. If no .rpy files exist, but the serialized script does exist, the serialized script is read back in from disk.
Once the script is loaded, the first thing that occurs is that it is scanned for init blocks. These init blocks are then run immediately, in no particular order. The init blocks should do things like loading images and changing Ren'Py configuration. On no account should an init block try to display an image or interact with the user, as the display system is not yet initialized, and so such interaction will not work.
After the last init block has finished running, the display is initialized, and the actual game can begin. It's expected that each game will have a label named "start". The game is begun by jumping to this start label. Execution proceeds from there, terminating when the end of a file or a return statement is reached.