Hannah Clare Wray Hazi

I love making beautiful things.

Sep 16, 2019

Telling Stories With Python And Ren'Py

This is a write-up of a workshop I've given at CamPUG and PyCon UK 2019. It was a lot of fun to deliver, and my participants came away with their own mini-games written in Ren'Py. I was impressed by the range of creative stories, everything from getting lost in Cardiff, storming Area 51, coaxing a grumpy cat, visiting a music festival, going to space, defeating animated fleas, visiting every pub in Cambridge, a child's guide to the Divine Right of Kings, interactive graffiti and more!

I hope this post will be useful as a reference for workshop participants and those who couldn't make it along.

What is Interactive Fiction?

You might be familiar with the concept of interactive fiction from "Choose Your Own Adventure" books.

Ever since computers came on the scene there has been interactive fiction here, too. Zork, one of the earliest examples, is a dungeon-crawling game where the player explores The Great Underground Empire by typing text commands.

Using later tools like Parchment and Twine people are still creating these text-based interactive fiction games. A text-based game I've enjoyed recently is Moonlit Tower by sci-fi author Yoon Ha Lee.

Visual Novels are a genre of interactive fiction that combines pictures and text as a storytelling tool. They originated in Japan and many famous examples such as Fate/Stay Night and Symphonic Rain are Japanese. These games are often romance or relationship-themed, each path perhaps leading to a relationship with a different character.

Ren'Py

Ren'Py Icon

Ren'Py is a visual novel creation engine written in Python. It has its own syntax and style, but also allows you to embed pure Python for more complex gameplay mechanics.

It is cross-platform and works on Mac, Linux and Windows. All that you need alongside it is a simple text editor that can convert tabs to spaces, such as gedit.

What can you do in Ren'Py?

Label Jump For Branching Stories

Road Not Taken Screenshot

I would humbly suggest my own game The Road Not Taken as a useful introduction to the label / jump mechanic. Take a look in the script.rpy file to follow how the game works. It demonstrates how menus work in the game and how you can use them to move to different choices, similar to a GOTO statement. It also includes an example of embedding music within gameplay to add a mood to a scene. Feel free to copy and modify it as a basis for your own games.

Custom GUI

Hotel Shower Knob Screenshot

Hotel Shower Knob, a game where you have to puzzle out an unfamiliar hotel bathroom to take a shower, is a good example of a custom GUI. Inside options.rpy the creator replaced the usual cursor with a custom image of a hand:

## The mouse clicker thingy.

define config.mouse = { 'default' : [ ('cursor.png', 66, 95)] }

This makes for a unique visual experience. As the hand is Creative-Commons licenced, you could use it in your own games, too!

Embedded Python

Two Worlds Screenshot

Because Ren'Py is written in Python (2.7, if you're interested) it's easy to embed Python statements within it to acheive more complex effects such as creating mini-games. There are two ways to do this - prefixing single lines with $ or using an indented block with the python: statement. I used python: statements within Ren'Py to create Card objects for my game Two Worlds.

The key thing to notice is the definition of the Card class:

# Define the card objects used to play the games with
init:
    python:

        flowers = ["card-front-land-flower%d.png" % i for i in range(1, 3)]

        class Card(object):
            def __init__(self):
                self.face_up = False
                self.selected = False
                self.number = 0
                self.coords = [0, 0]
                self.back = "card-back-land.png"
                self.front = flowers[0]
                self.paired = False

            @property
            def coord_text(self):
                return "{}, {}".format(self.coords[0], self.coords[1])

This happens during the init: block, before the game starts, so it is available from the beginning. I subsequently used this in the sea_game.rpy and land_game.rpy files using the ui.interact() statement and action If to connect it with Ren'Py responses to on-screen clicks.

Persistent Data

Another useful trick is the ability to store persistent data between plays of the game.

Example From Long Live The Queen An example from Long Live The Queen

This allows more satisfying gameplay such as unlocking new routes after a complete playthrough, and keeping track of player stats like Strength or Skill. Anything that can be pickled is suitable for this treatment. A simple example:

if persistent.secret_unlocked:
        scene secret_room
        e "I see you've played before!"

To unlock this path the user must hit a piece of code that sets $persistent.secret_unlocked = True

persistent is a special keyword in Ren'Py, so you shouldn't use it for anything else. Unlike other variables, if you haven't yet initialised it when you reach the if statement Ren'Py won't complain.

Useful sources of free images and sounds for your games

There are various community projects to collect images and sound to use in games with Creative Commons or similar licences.

I've found px here a useful collection of images, particularly photos.

I used Fraqtive to create the card images and backgrounds for Two Worlds.

Useful sound sources include Free Music Archive and for sound effects, Free Sound

Example games written in Ren'Py

Benthic Love – Michaela Joffe, Sonya Hallett

Hotel Shower Knob – Yoss III

Death And Burial Of Poor Cock Robin – Lernakow

Long Live The Queen – Hanako Games

And check out the NaNoRenO game jam each year during the month of March – or better still, take part!