Ren’Py Developer Update - December 2021
Welcome to the final developer update of 2021. In this update, I’ll talk about 2021 in review, what was developed in December, and what the plans are for the releases of Ren’Py 7.5 and 8.0. Finally, I’ll talk a bit about some of the things that you can do now to get your game ready to run on Ren’Py 8, which will be powered by Python 3.
2021 Year in Review
I’m very happy to say that 2021 was one of the most successful years for Ren’Py to date. That’s thanks to everyone that uses the game engine, from those who create with it, to those who develop and test, those who volunteer support, and the players without whom there would be no point in creating.
This year, we had the most games ever released with Ren’Py - 1060, as of the time of this writing. This is the first time we’ve had over a thousand releases in one year, and it’s 25% more than 2020. (This number is taken from VNDB, and counts any game that had at least one release in 2021, but only counts each game once.) According to VNDB, Ren’Py is the most-used visual novel engine.
Ren’Py is the fifth most popular game engine on Steam, used for 2.8% of Steam games.
Development of Ren’Py has also picked up in 2021. If we count Ren’Py 7.4.0 as having come out in 2020 (which varies a bit based on time zone), then there have been 11 releases of Ren’Py in 2021. I’ve worked on Ren’Py every day this year, and there have been a steady stream of pull requests from others, offering improvements to the engine.
Things weren’t entirely perfect in 2021, however. Ren’Py 7.4 really should have been multiple major released, but instead there were a number of patch releases that both fixed issues, and introduced new features. The result of this was that, until late in the year, Ren’Py 7.4 never felt fully stable.
I’ve been addressing that by becoming stricter about patch releases, which should only include fixes. In 2022, I plan to release major releases more frequently - I’m hoping for major releases every 3-4 months, with small patch releases as required to keep everything working well.
2021 was the year of Ren’Py 7.4. My hope is that 2022 will be the year of Ren’Py 7.5, 7.6, and 7.7 - and Ren’Py 8.0, 8.1, and 8.2 as well.
Ren’Py 8 and Python 3 plans
As part of the transition to Python 3, ideally starting with the next release of Ren’Py, my plan is to start releasing Python 2 and Python 3 versions of Ren’Py in parallel. So:
- Ren’Py 7.x
Will be a version of Ren’Py that is running on Python 2.7.
- Ren’Py 8.y
Will be a version of Ren’Py that is running on Python 3.x.
At least to start, Ren’Py itself will be feature identical between the Python 2 and the Python 3 versions, with the differences caused by Python itself. The second number won’t be the same - chances are that Ren’Py 7.5 will correspond to Ren’Py 8.0.
I’m not going to guarantee the first release of Ren’Py 8 will work on all platforms. I plan to drop support for 32-bit only Windows computers with Ren’Py 8, and it’s possible that the Android, iOS, and Web platforms will have to wait for Ren’Py 8.x or later to gain support.
For the first few releases of Ren’Py 8, Ren’Py 7 will be the suggested platform for people to keep creating games in. Ren’Py 8 will be intended for people who want to experiment with the latest version of Ren’Py.
Within a release or two, that will likely switch. Ren’Py 8 will be the version of Ren’Py that gets the most development effort, and we’ll start seeing features that only appear on Ren’Py 8. Ren’Py 7 will still exist, and still be supported on all current platforms, but it’ll be mainly intended for games that are far along in development, but with finite development horizons, so those games can be released without needing to port.
Eventually, the goal will be to reduce or stop releasing Ren’Py 7.x entirely, which will probably happen a couple years after Ren’Py 8 is the recommended new version for new games. I’d want to make sure that Ren’Py 7 games can always run on desktop (Linux and Windows, and maybe Mac) platforms for game preservation purposes, but I probably wouldn’t be spending time updating Ren’Py 7.x to make releases for iOS, Android, Web, and other future platforms.
December 2021 - Ren’Py 8 Nightlies Begin
The first thing I did this month was to release Ren’Py 7.4.11. It’s a release that I’m fairly happy with, and hopefully it will be good for the next few months of development.
This month, I focused on three main things.
The first was to get Ren’Py running under Python 3 again. It’d been several months since the last time that happened, and the changes in the 7.4 series had caused some problems. This was relatively easy to clean up, and soon Ren’Py was running in my Python 3 virtual environment.
The largest amount of work, however, was updating renpy-build to build versions of Python and supporting libraries that can run Ren’Py under Python 3 on the main desktop platforms - Windows, Linux, and macOS. This required moving things around so that the two versions of Ren’Py can be built in parallel.
Finally, I had to update the Ren’Py build process to be able to package up Ren’Py 8.x, and the nightly build process to make releases of 7.x and 8.x in parallel.
The good news is, it worked! There are now Ren’Py 8 nightlies being produced that can be downloaded from the usual nightly build site,
Should you use these? Maybe, but you’ll be very adventurous if you do. Right now, there are still important changes that will be required to support Python 3 that have yet to be added. There are also portions of the launcher (especially Android and iOS support) that haven’t been so much as tested.
But the good news is that there is a version of Ren’Py 8 that exists, and can be downloaded. It can run The Question, and the tutorial. There is a lot of work to do in finishing and testing Ren’Py 8 - but that work can now be done on something that exists.
What a way to close out the year.
Porting your game to Python 3
It’s probably not too early to start getting your game ready for Python 3. One good thing is that many of the big changes that are required were introduced in Ren’Py 7.4, so you can switch today, on a file-by-file basis, by writing
rpy python 3
At the top of a .rpy file.
The good news about these changes is that many of these changes are, if not esoteric, only really applicable to games that use a lot of Python code, especially to interface outside of Ren’Py. It was my experience that the pure visual novel “The Question” required no changes to run on Ren’Py 8, the tutorial required a few, and the launcher has been requiring a lot of changes to work.
Unicode Strings
One of the big changes that happened in Python 3 was a change into the default string type from byte strings (which represent data in UTF-8 or your computer’s encoding) to unicode strings (which can represent any language). Byte strings are basically used to read raw data from files, and to talk to the computer itself. Unicode strings are used for dialogue, filenames, labels, images, and nearly everything else.
For several versions of Ren’Py, unicode strings have been the default for Python found in Ren’Py script. If you do:
default mystring = "foo"
That will create a unicode string. The problem comes about because Python 3 is a lot stricter about this difference than Python 2, which would automatically convert between bytes and unicode strings. Generally, Ren’Py will keep these conversions working. However, if your game reads or writes external files, you’ll need to pay attention to if the files are opened in text or binary mode, and access the file appropriately.
I do expect to introduce a new function to help deal with this in the case of data files included with your game, so stay tuned for that.
Division
What is 1 divided by 2?
In most math classes, that’s taught to be 1/2, or 0.5. In Python 2, when you had 1 and 2 as integers, this is rounded down to an integer, so the answer is 0, an integer. In Python 3, this is 0.5, a floating point number.
This becomes a problem in Ren’Py, because Ren’Py uses the difference between integers and floating point numbers. Generally in Ren’Py, an integer represents pixels from the left/top, while floats represent a fractional size.
As a practical example, take
screen test():
text "Test" xpos (400 / 2)
In Python 2, this will be 200 pixels from the left side of the screen. In Python 3, this will be 200.0 screens from the left side - way away from anything that’s visible.
The good news is that Python 3 introduced a new operator, //, that does integer division just like Python 2. So if you change this to:
screen test():
text "Test" xpos (400 // 2)
Then your game will work as before. I’ve found it’s pretty easy to audit and find these problems, but if that’s not the case for you, let me know and I’ll try to make some tools.
Dicts and Range
Another Python 2 to Python 3 change is that there has been a change in how dictionaries are used. In Python 2, the .items, .keys, and .values methods returned lists. There were also corresponding .iteritems, .iterkeys, and .itervalues methods, that were faster, because they didn’t make a list.
In Python 3, things have been changed so the .iter versions go away, and the methods no longer make lists. That’s generally better, but does require you to change your game.
Take this example, using Python 2.
# A map from character to the year they were born (or created).
define characters = { 'Eileen' : 2003, 'Lucy' : 2004, 'Mary' : 2005, 'Shiro' : 2017 }
# Create a list of all the character names.
define character_names = characters.keys()
# Create a map from the character name to the age they'll turn in 2022.
init python:
age_in_2022 = { }
for name, year in characters.iteritems():
age_in_2022[name] = 2022 - year
In Python 3, characters.keys() won’t be a list, so if we want a list we have
to make it. Since there’s no need to make a list in the for loop, we can just
replace characters.iteritems() with characters.items(). In Python 3,
we’d write this as:
# A map from character to the year they were born (or created).
define characters = { 'Eileen' : 2003, 'Lucy' : 2004, 'Mary' : 2005, 'Shiro' : 2017 }
# Create a list of all the character names.
define character_names = list(characters.keys())
# Create a map from the character name to the age they'll turn in 2022.
init python:
age_in_2022 = { }
for name, year in characters.items():
age_in_2022[name] = 2022 - year
The Python range function underwent a similar change - it no longer returns
a list, so if you want one, you have to call list to make it. The old
xrange function went away, and can just be replaced by range.
Other
These are the problems I’ve encountered the most when porting Ren’Py to Python 3. I’d expect these to be less common in visual novels, and there might be other problems in simulation games. If you find yourself stuck, let me know and I’ll see what I can do to help.
Thank You
Thank you so much for your support in 2021. It made it easy for me to invest in all sorts of equipment to develop Ren’Py, from multiple cell phones to debug and fix Android issues, to new computers that makes building Ren’Py 8 faster, and will let me port Ren’Py to ARM (Apple Silicon) Macs. I’ve also been able to take time off to work on Ren’Py - so thank you, and I look forward to seeing what you create next year.
Title image based on one posted by Riva Celso (Winter Wolves).