Interesting. It will be simplier for other people who wants to make a Ren'Py VN as well. Thanks for sharing ;)
Self-Pruning Menu for Ren'Py VNs
Today’s development log is about a technical topic that will only be of interest to people making visual novels in ren'py. It’s not exactly an exciting, engaging thing, but we figured it could be useful to other people.
In our 0.1 build there is a moment where the player is shown a list of questions he can ask to a character, Asterion. After he picks an option he gets his answer and is then brought back to the menu where he can select the other questions. Making a menu is easy on ren’py, but for this occasion we wanted the game to exclude the option the player picked previously. We thought it would be jarring if you could repeat the dialogue again and again — it breaks the immersion.
You'd expect Ren'Py to have a obvious way to do it but it doesn't, at least none a writer who doesn't know how to code would figure out. So nanoff, our programmer, prepared this code. I've seen other visual novels who struggled with this so we chose to share it. For the sake of clarity let's call those "self-pruning menus."
$myChoices = [ ("Choice 1 text", "id1"), ("Choice 2 text", "id2"), ("Choice 3 text", "id3")] label before_choice: $narrator("Pick an option", interact=False) $result = renpy.display_menu(myChoices) if result == "id1": "Here's the dialogue if choice 1 is picked" "Anything could go here, for the record, like choices, variable changes, etc." $ myChoices.remove(("Choice 1 text", "id1")) if result == "id2": "Here's the dialogue if choice 2 is picked" $ myChoices.remove(("Choice 2 text", "id2")) if result == "id3": "Here's the dialogue if choice 3 is picked" $ myChoices.remove(("Choice 3 text", "id3")) $ long = len(myChoices) if long > 0: jump before_choice “The story goes on from this line.”
- Replace “Pick an option” with the message you want to display below your choices.
- Replace "Choice 1 text” for the options you want to display in the menu. Do the same for "Choice 2 text”, "Choice 3 text” and so on.
- Replace "Here's the dialogue if choice 1 is picked” for the dialogue you’re using. Keep in mind anything can go there, like more menus, jumps to other labels, variable changes, etc.
- After all the options have been explored it goes on to “The story goes on from this line.”
- If you plan on pasting this more than once, rename the before_choice label AND the jump at the end. Labels tell the program where to go, so if there's two before_choice labels, the program won't know where to jump to and will crash.
- You can have as many options as can fit the screen. The template we offered only has three options, to add more just copy and paste the if blocks for every dialogue choice.
We tried to make the instructions as easy as we could. If, despite that, you have a hard time visualizing everything we made an example below with some proper dialogue.
$myChoices = [ ("What's your name?", "id1"), ("How are you doing today?", "id2"), ("What's your favorite color", "id3")] label before_choice: $narrator("Ask him a few questions!", interact=False) $result = renpy.display_menu(myChoices) if result == "id1": "My name is Asterion and I come from Crete." "It's nice to meet you." $ myChoices.remove(("Choice 1 text", "id1")) if result == "id2": "I'm doing well, thanks to you, of course!" "Thank you for asking." $ myChoices.remove(("Choice 2 text", "id2")) if result == "id3": "I'd say my favorite color is orange..." "But light blue really is pleasing to me. It's a soothing color." $ myChoices.remove(("Choice 3 text", "id3")) $ long = len(myChoices) if long > 0: jump before_choice "Are those all your questions? Do you want to go out for lunch now?" jump next_chapter
Now, how does this all work? Nanoff prepared an explanation if you are curious but don’t worry, you don’t have to understand it.
Ren'py is built on python, so despite it having easier ways to do most of the basic things you'd want to do in a VN, there's always a python analogue to every action you do. In the case of choices, ren'py generates a list of tuples, each tuple being a pair of text and identifier.
What the $result = renpy.display_menu(myChoices) line does, is it displays the text for each element in the list, and if that text is selected, it stores the corresponding identifier on the result variable.
So after making your choices, if you picked "Choice 2 text", the result variable will contain “id2”. The following blocks show actions that happen depending on the value stored in result.
On the first block, since result's value is not id1, it will skip the dialogue.`
On the second block, since result's value is id2, the contents of the if block will happen. Once that portion of dialogue (or whatever you want to put in there) happens, it will remove the choice you just took from the list of available choices.
After skipping the other block, we set the "long" variable to the length of the myChoices list. This basically stores the number of available choices in the long variable.
Now, if the length is bigger than 0, the flow will jump to the before_choice variable, and will ask the player to choose another option.
If length is not bigger than 0 (therefore, it is 0 or less), the flow will not jump, and will resume below.
That's it for today. We hope this little dev log is useful for other developers.