Guide:Ren'Py visual novels on Steam/Achievements

From VNDev Wiki
Revision as of 08:09, 19 February 2025 by Ironnori (talk | contribs) (text formatting)

Add Achievements

For each achievement you wish to have, you will need to add the appropriate code in your Ren’Py game, you will need to add the corresponding achievement in Steam, and you will need to create two images for it (one locked, one unlocked).

These steps can be done in any order.

For a simpler time adding achievements, Bob has a Ren'Py Achievements Framework which handles all of the in-game work. (You'll still need to do the work to add the corresponding achievement in Steam, including creating two images for the achievement.)

Create the Images

Create two 256x256px JPG images for the achievement, representing the locked and unlocked states.

Define the Achievement in Steam

Navigate to Technical Tools > Edit Steamworks Settings > Stats & Achievements > Achievements, and click the "New Achievement" button. You will see something like this:

For the API Name (the top left text field), enter a name for the achievement using letters, numbers, and underscores. For example, you can name the achievement "FIRST_STEPS" or "first_steps" or "firststeps", but you should not name it "first steps" or "First Steps!". This value will not be visible to users. Note this value down, as you’ll use it in Unlock the Achievement in Your Game (in Ren’Py).

For the Progress Stat, select None, unless you are using Achievement Stats.

The Display Name and Description are player-visible values, so you’ll want to enter friendly values here. For example, "First Steps" and "Start a new game" would be a good Display Name and Description.

Check the "Hidden?" checkbox if you want your achievement description to be hidden by default until unlocked. The name will still be visible.

Save the achievement before uploading icons or the upload will not work. After saving, edit the achievement again and upload the icons for achieved and unachieved, and save again.

Repeat these steps for each achievement.

Unlock the Achievement in Your Game (in Ren’Py)

If you're using Bob's Ren'Py Achievements Framework, you should follow the directions for that framework rather than this section.

For each API Name corresponding to an achievement in Steam, you will want to first register the achievement in an init block, and then grant it when the appropriate in-game event triggers. There is documentation here, but we’ll lay it out step-by-step below.

Note: You can do all of this before the above steps within Steam. Simply select an appropriate API name for your achievement and note it down for later reference when configuring Steam.

Register Your Achievement in an Init Block

You should register each achievement you want to grant. In any rpy file, add the following code:


init python:
	achievement.register("THE_API_NAME")


For example, for the achievement with an API name FIRST_STEPS, you would add:


init python:
	achievement.register("FIRST_STEPS")

It’s generally a good idea to register all of your achievements together at once, since this also provides an easy reference if you define the achievements in Steam later.


init python:
	achievement.register("FIRST_STEPS")
	achievement.register("NOTHING_BUT_LOVE")
	achievement.register("JOURNEYS_END")
	achievement.register("COMPLETIONIST")

Grant Your Achievement During Gameplay

When you wish to grant an achievement during gameplay, simply use the following code:


python:
	achievement.grant("THE_API_NAME")
	achievement.sync()

For example, to grant the FIRST_STEPS achievement when the game starts, you would simply use this code:


label start:
	python:
		achievement.grant("FIRST_STEPS")
		achievement.sync()
	"Welcome to the game!"
	e "My name is Eileen!"

You may also wish to add a "Sync Achievements" button to some screen, such as the Preferences screen. This will allow players who play your game build with achievements through other storefronts (such as itch.io) to synchronize their achievements with Steam after downloading the Steam version.


textbutton "Sync Achievements" action achievement.Sync()

Please note the capitalization of "achievement.Sync()", and note that this is a button to be added to a screen. (If you are not familiar with screen language, please reference the Ren’Py documentation or ask a friendly fellow developer for assistance.)

Display Achievements to Users (Optional)

Achievements granted through Steam will automatically show a popup notification when they are granted. Additionally, players can view the achievements they’ve unlocked through the game page in their library, and on their Steam profiles.

However, you may wish to add Achievements to all builds of your game, including ones distributed through other storefronts such as itch.io. All of the above code is still perfectly fine and applicable, but you will need to add a new screen to your game to view unlocked achievements. (This will also allow Steam players to view achievements within the game itself, which is always a plus.)

The following is an example of a basic screen that shows all unlocked achievements, with the same Display Name and Description as we used in Steam. If you are not familiar with screen language, please reference the Ren’Py documentation or ask a friendly fellow developer for assistance.


screen achievements():
	tag menu
	add "#aaaa"
	vbox:
		xcenter 0.5 ycenter 0.5
		if achievement.has("FIRST_STEPS"):
			text "First Steps - Start a new game" color "#000"
		else:
			text "First Steps - ???" color "#fff"
		if achievement.has("NOTHING_BUT_LOVE"):
			text "Nothing But Love - Come out to your parents" color "#000"
		else:
			text "Nothing But Love - ???" color "#fff"
		if achievement.has("JOURNEYS_END"):
			text "Journey's End - Finish the game" color "#000"
		else:
			text "Journey's End - ???" color "#fff"
		if achievement.has("COMPLETIONIST"):
			text "Completionist - Unlock all 333 collectables" color "#000"
		else:
			text "Completionist - ???" color "#fff"
		textbutton "Close" action Return()

After adding this screen to a rpy file (such as screens.rpy), you will need to add some way to show it. The easiest way is to add another button to show the screen in the navigation (screen navigation in screens.rpy).


if main_menu:
    textbutton _("Start") action Start()
else:
    textbutton _("History") action ShowMenu("history")
    textbutton _("Save") action ShowMenu("save")
textbutton _("Load") action ShowMenu("load")
textbutton _("Preferences") action ShowMenu("preferences")
textbutton _("Achievements") action ShowMenu("achievements")

Achievement Stats

Bob's achievement framework does not support achievement stats, so if you wish to use these, you will be unable to use the framework.

The achievement examples above are for boolean achievements: You either have it or you don’t. However, you may wish to add achievements that you can gradually build up to as you accomplish multiple goals in the game. (For example, you may wish to have an achievement that counts up from 0 to 50 for each secret item that you find, and grant the achievement when you find the 50th item.)

For this, you will need to use Stats, in conjunction with renpy’s achievements functionality.

(Bob has not personally done this, so thanks to SuperBiasedGary for confirming this works!)

Register Your Stat in Steam

Navigate to Technical Tools > Edit Steamworks Settings > Stats & Achievements > Stats, and click "New Stat". You should see something like this:


You probably want an INT stat. Give it an API name (with the same requirements as regular achievements), and set the other fields to reasonable values based on your use case. The Display Name, of course, is the name that players can see.

After you’ve done this, go back and create an achievement with your stat as the Progress Stat, and set a reasonable min and max for it.

Add Your Stat in Your Game (Ren’Py)

For this example, we’ll assume you have a stat with the API name COLLECTION with a min value of 0, a max value of 100, and a default value of 0. We’ll assume you also have an achievement with the API name ALL_COLLECTION using that stat, with the same min and max.

In any rpy file, you’ll need to register both your stat and achievement.

init python:
	achievement.register("COLLECTION", stat_max=100)
	achievement.register("ALL_COLLECTION")

Whenever a player finds a collection item, you’ll update the count. You may also want to grant the achievement if applicable, so that players not playing on Steam can obtain the achievement as well. (Steam should automatically grant the corresponding achievement when the stat reaches the max value.)

default persistent.found_items = Set()
label whatever:
	"You found a seashell!"
	python:
		persistent.found_items.add("seashell")
		achievement.progress("COLLECTION", len(persistent.found_items))
		if achievement.get_progress("COLLECTION") >= 100:
			achievement.grant("ALL_COLLECTION")
		achievement.sync()