Developer Perspective - Skipping through Content
Added 2023-11-20 23:00:01 +0000 UTCA few years ago I watched an interesting presentation that was about the idea of the "skip" function as an important game mechanic for visual novels and how developers should consider the experience of skipping through VNs as part of their game design.
I thought it was interesting because, so often, "skip" is an overlooked feature. It's there, but we don't lend a lot of thought to how it integrates with the experience of unlocking the story or moving through the content during replays.
Though I thought it was an interesting presentation, I have learned so much more about game design since then and have come to a much greater understanding of how important skipping is to VN gameplay and how it can impact the experience when consideration isn't given to this function.
One of the reasons I've had some "skip" related epiphanies is due to working on When stars Collide.
This game handles conditional/unconditional content in a way different from any other game I've worked on in the past. Now this is a common set up - many other games I've played use this structure. But I have never personally written a game structured this way.
And this really impacts how integral 'skip' will be to the player.
So how does skip even work?
So how does skip work? Now this part gets a little technical in terms of explaining how Ren'py handles "seen text" - it's important to explain this because it's necessary to understand why poor design can be so detrimental to player experience.
In Ren'py "skip" has a few different functions but we're going to talk about "seen text" and "unseen text".
Generally, on replay, a player is going to want to skip over text they've seen before and stop when the game reaches something they've never seen before (the "unseen text").
To understand how skip handles text, you have to understand a little about how visual novels look behind the scenes.
The narrative part of a Ren'py game (the story itself) is made up of something called strings. In code, a string is just something set inside a pair of quotation marks.
Strings occupy lines that are numbered. Below, we have seven strings.

Remember that before, I said you can think of content in a VN as if it exists in a container. And there are different types of containers. Like "blocks" and "labels".
For instance, conditional content often appears in in a new block or label. A 'block' is just a group of strings that are all indented the same way. (Indentation is really important in code).

All of this is just so that when I say line, string, or block - you know what I'm talking about.
Let's get back to the skip function.
Because the lines of code are numbered and indented ins specific ways, it's easy to track what line you're look at and to record what lines you've looked at in the past.

So what happens if you have two lines of code with identical strings?

In this case, even though we have the same content, it exists on different lines of code. So Ren'py won't skip line 2 because you've seen line 1. Even if you've seen line 1, line 2 is still "unseen."
It doesn't matter that the strings are identical - the lines themselves are different lines of code.
Ren'py does not make judgement calls about content that is identical to other content. It only cares about if you've seen that specific line of code before.
This means you are presented with identical content occupying different lines of code within a game...it is treated mechanically by the skip function as completely unique content. Even though practically from a player perspective, it's the same.
So "unseen" and "seen" have both a mechanical and practical functionality that sometimes is in conflict.
Take the below example:

Have you ever been playing a game and skipping through the "seen text" only to have the skip suddenly stop...but in the place where it stops, you know you've seen that text before even if the game doesn't let you skip past it.
You may have thought the skip function was broken or buggy - especially if it's a game where this happens frequently.
But what you've actually encountered is what I just described above. You hit a section of text that has been copied and pasted multiples in the script.
So while you considered the text to have been 'seen' before, Ren'py was treating it as 'unseen' because it was - in the mechanical sense.
And when I talk about how the function of "skip" might impact player experience, this is one major example of what I'm talking about.
When and Where and Why this happens
This typically happens when content that should be set up as unconditional or shared across routes is, instead set up as conditional.
Take this example:

All the content under the "romance_target" conditions is identical. There's no reason to repeat it; it's exactly the same except the character name.
I believe this happens most often when the developer is a novice coder who is uncomfortable with or unfamiliar with variables. I did this exact thing in one place in Changeling for that exact reason.
Variables are just a construct used to allow us to quickly substitute in something that may not be the same across playthroughs - such as the player character's name.
So instead of dialogue saying

we would code it like this:

Even though the value of the variable [MC] can change, the variable itself stays the same. So this means the player can skip over it no matter what is being substituted there.
When you are a newbie coder, you sometimes learn very specific use cases for variables and this can result in rigid application of what you've learned to similar use cases. If you learn how to use a variable to substitute the MC's name, you may not necessarily think of doing the same thing for the love interest's name - but you absolutely can.
So the above example becomes this:

In this set up, you would always be able to skip through this section on additional playthroughs, no matter who you're romancing.
When I first started coding, it never would have occurred to me to set it up this way even though it's way better for the player.
You can't always avoid repeated content
One thing I've learned through writing WSC is that you can't always avoid repeated or, at least, very similar content.
Sometimes, for example, you are writing a character scenario and you need something specific to happen in all the scenarios but it doesn't make sense narratively to funnel everything into a shared moment because you need 10 different micro-variations based on who you're talking to.
I have a few moments like this in WSC.
It can sometimes be difficult to have these moments be shared, for instance, when there are different characters showing on the screen at one time and you're having to show and hide characters who may be different depending who you're romancing.
My strategy in these moments is to just try to avoid having the text be 100% identical, focusing on rewording things or editing for characterisation and the like.
But I try to avoid these moments as much as possible.
If I have to have a conditional section that I know will interrupt skipping through the content, I try to make sure it's worth it.
Fortunately there are also some new tools out there that really help with this sort of thing, which overall makes it even easier to ensure that conditional variations are allowing for a smooth playthrough as much as possible.
In short...
I still think that considering the skip mechanic as integral to the design of your game is really interesting.
We all know this feature exists and we know people will use it when playing our games. But actually stopping to think about what the experience is when players inevitably skip through content isn't always something we do.
It's one thing to try to make sure any existing narrative variations have enough 'meat' to be worth the stop. I think this makes sense (and was the topic of the original presentation I saw).
But it was a whole other level for me to stop and think about the more mechanical side of things and how conditional statements, variables, and repeated content can all negatively or positively impact a player's experience when going through a game that relies heavily on the skip feature to get through all the game content.