Home > Root > Log book > A Visual Novel using a graph tool as a development tool?

A Visual Novel using a graph tool as a development tool?

Sunday 15 October 2023, by Mathieu Brèthes

All the versions of this article: [Deutsch] [English] [français]

I have not abandoned my Visual Novel (VN in this text) project, "Nine Suns." Actually I have started writing it using RenPy, an environment designed to develop VNs with Python as a support language, and it is going ahead.

Code as story, a risky strategy?

I do however get regularly frustrated by a limitation of RenPy, but I believe that it is actually a more general limitation of text-based development tools: when writing an interactive story using such a tool, there is really a strong incentive to write a linera story. This is caused by the nature of using text as a medium. Text gets read from top to bottom and (in our culture) left to right, paragraph after paragraph. When one writes a story, there is a beginning, an end, developments. Sidesteps and flashbacks appear when the author wants them to appear. A story, whether choral or chaotic, progresses page after page towards its conclusion.

Video games, as a medium, should not have that constraint. The programmer can easily, usually, choose to present narrative elements to the player and allow the player to choose the order of exploration by himself, he can even choose to make some of them optional. The programmer will add constraints that will determine the order in which the narrative elements are explored - for example, a narrative arc may be blocked behind a locked door, that the player can only open with a key he found in another story arc. The art of videogame story telling is the art of constraining the player without they being aware that there is such a constraint.

This is usually not hard to achieve because of the important difference between code and story. In a standard game, the code defines the logic of the game (this is what we call the game engine), and the story is designed on the side, with its rooms, objects and interactions. However, writing a VN with RenPy, one writes the story, cut into scenes, with dialogue that look like what you would find in a movie script - the exception being menus with choices that the player can interact with. Said choices are usually very constrained, wrong choices will quickly lead to a game over, right choices make the main intrigue go forward. This is why VNs often have poor replayability: when the player has gone through the story once, why doing it again?

This problem can be partly alleviated by creating different possible endings that will depend on the player’s choices during play. But still, the whole story looks like a tree with few big branches. And describing a story with few branches is easily done with a text programming language.

What about non-linear VMs?

In my game, Nine Suns, one of the specificities is that the story is built on a cyclical structure, not a linear structure. The main cycle is represented by the days of the week (Mon -> Tue -> Wed -> ... -> Sun -> Mon...), from which micro-stories can be accessed, each depending on events that have been performed in other micro-stories. For example, the player will meet an old lady on Saturday, but he can interact with her only if he helped her fend off a road offender on one of the week’s mornings. Some micro-stories will also develop depending on whether the player has already gone through them or not - a wrong choice ending the story for now, not bring to a game over, leaving the possibility to retry the story later on.

In this game, the most important aspect is the passage of time, hence the loop over the days, but I could also have imagined a structure where the player can freely go into different places, and live micro-stories from each place (which would be closer to a classical point and click / 2D adventure game, such as Dans Mon Quartier, the game developed by Nils).

This idea makes it complex to program this game using RenPy. It’s not only that the general story is not linear, but micro-stories interact with each other. How not to get lost in storing information relevant to the story, visualize the loops...

This game is not a tree, actually it looks much more like a graph, with returns to previous points.

An example in RenPy language

Here is a small example. In this game, the player and a character named Eileen (this is the character from the RenPy tutorial) go for a walk in a park and discuss some events. Eileen wants to know what the player thinks of the event, but the player does not want to reply. Eileen insists until she gets an answer. If the player gives in quickly, Eileen will be happy and they will hug, if he delays too much, she will get mad.

The RenPy code is 90 lines long, its menu contains a loop, like this:

init python:
    import random

define Eileen = Character('Eileen', color="#c8ffc8")
define m = Character('Me', color="#c8c8ff")

label start:
    scene park
    show eileen happy
    $ eileen_love = 0
    $ label1_num = 0
    $ label2_num = 0
    
    "Eileen and I walk in the park."
    "We are discussing the latest events."
    
    Eileen "What do you think of the latest events?"
        
label .label1:

    $label1_num += 1

    "What should I answer?"

    menu:
        
        "I don't know" if label1_num == 1:
        
            jump .label2
            
        "I really don't know" if label1_num == 2:
        
            jump .label2
            
        "I have no idea, honest" if label1_num > 2:
        
            jump .label2
            
        "I think the events brought us closer" if label1_num > 1:
        
            jump .label3
            
label .label2:

    $ eileen_love -= 1
    
    show eileen angry

    $ label2_num += 1

    if label2_num == 1:
    
        "Eileen frowns at me. Maybe I should find a better answer..."
        
    if label2_num == 2:
        "Eileen looks angry. Come on."
    
    if label2_num > 2:
    
        $ randval = random.randint(0,3)
        
        if randval == 0:    
            "Eileen sighs."
        elif randval == 1:
            "Eileen raises her eyes."
        else:
            "Eileen looks really pissed."
            
    jump .label1
    
label .label3:
    
    $ eileen_love += 2
    
    if eileen_love > 0:
    
        show eileen happy
        
        "Eileen gives me a hug."
        
    else:
    
        Eileen "Yeah, right."
        
    return
    

At first glance, even with a small program like this, it is not easy to follow the different branches of the program, specially the main menu (a loop). The winning condition (eileen_love > 0) and its implementation (a decrement of 1 every time the player declines to answer, and an increment of 2 when the player finally answers, which makes him "win" if he answers on the 2nd turn, and loose otherwise), is not easy to grasp.

Also, someting RenPy handles very poorly, is the generation of dialog "variants" used to make loops more lifelike - a similar dialog element can here have 3 variants.

Finally, if narrative elements require counting iterations of the player in the game’s loop, this requires manually introducing counters, which is impractical.

What if we represent the game as a graph instead?

Here is the exact same scene represented as a flowchart diagram :

An example of a VN

Isn’t it much more obvious to understand? Even without knowing the details of the syntax (meaning of the different boxes), we can observe how the arrows go and immediately see the loop and the rest.

A proposal for a visual syntax

I designed a syntax to handle the dialog elements that need to be displayed depending on the iteration count in a loop:

The automated dialog changes

Another shortcut is proposed for the randomly selected "dialog variants":

An example of syntax for random alternative dialog choice

Standard flowchart elements are used to represent the different elements of the game:

  • The narration (the player’s inner monologue)
The "storytelling" element
  • Dialogs (what the non-player characters or the player say out loud. I need to give more thought into this so that one can better visualize who says what: use colors? Logos?)
The Speech element
  • The menus (where the player manually selects an answer)
The choice menus
  • Automatic branches (game logic)
The programmatical branches
  • Storing and updating variables
An example of a variable element (data set/get)
  • Display and music information
Management of graphical elements

The arrows allow to represent the sequence of elements, jumps, and loops. When necessary, they can be augmented with text that will be interpreted as dialog elements (for example in a menu) or as code (for conditions of an automatic branch).

Finally, other standard flowchart elements can be used to define modules, with jumps and calls, etc.

This of course remains to be formalized.

What software should I use?

I do not have the capacity (lack of time, mostly) to develop my own graph-plotting software (even if I could theoretically make one, for example with Processing). To draw this program’s flowchart, I used a piece of free software called Dia. I have also given Yed a try. Yed is a proprietary software with a free version.

Those two pieces of software create real graphs, and generate a file format in XML that can be reinterpreted by a translator that will do the conversion in RenPy code (or something else altogether: why not direcly in Neo Geo Pocket code?). Dia is quite the old software, it is not very ergonomic (the way to add text to arrow elements is not practical), but it has two advantages over Yed: one, it is free software, and the second, as it is very old, its file format is very stable. If I develop a translator for Dia files, it will still work in 10 years.

I like this idea and I think I will work on it!