DEV Community

loading...
Cover image for Making a Visual Novel with Unity (1/5) - Introduction to Ink

Making a Visual Novel in Unity Making a Visual Novel with Unity (1/5) - Introduction to Ink

Klaudia Bronowicka
Xamarin developer & Indie game developer interested in psychology and well-being
Originally published at klaudiabronowicka.com ・8 min read

Ever tried to develop a game with a deep, branching story and choice based narrative? Ever felt like managing those branches and choices can become overwhelming very quickly? Well, so did I. That's why I created this tutorial series, to show you some great tools and techniques I found when working on my own game. By the end of it, you should have a solid skeleton of your visual novel and will be able to focus on what matters the most - the story.

Ink

If you like narrative games you might have heard of Inkle Studios. They released Sorcery, 80 days and, most recently, Pendragon. You haven’t? I recommend you check them out.

What’s interesting is that their games are developed with ink, a scripting language those guys made to help them develop text games more easily. The best part is, it’s open source and free to use!

Ink allows you to write stories with branching narrative, choices, and dialogues very easily. It comes with Inky - an editor, where you can write stories using Ink, debug them and run in the same window to test your choices etc. There is also Inklewriter, which offers a simple interface to let you create those stories without needing to get deep into the scripting magic. To be honest, I haven’t used it. If your only goal is to write and share some interactive fiction, you might want to give it a go. However, if you’re planning to build an actual story driven game with Unity, read on.

Working with Ink

Inkle offers an extremely comprehensive documentation with examples of how to use Ink and all of its features. There is also an example game where they show those features in action. However, if you’re like me, you may find it a little bit daunting to go through all that text and try to make sense of it. That’s why I prepared this small example to show you the basics. Truth to be told, when making my own game, these basics covered 90% of what I needed.

If you want to follow along, download Inky, and let's get started!

Knots

In Ink, the story is divided into knots. Think of them as scenes within your story. Each scene can have a dialogue, a paragraph of text, some choices, etc. It’s up to you how you want to structure your story. What I found works best is to break into another knot when we get to a choice, a scene change, or when there is a context change (like the player needing to perform an action). How do knots look, you ask? Very good question, you mark them like so:

=== knot_title ===
Here goes knot content

Simple as that!

Now, I said that a story is built from multiple knots so you will need a way to move between them. You do it like so:

-> knot_title

This is also used to tell ink, which knot is the entry point for your story, so make sure you add this line at the top of your script (with the correct title). This is very important, as ink wouldn’t know where to start otherwise!

Now, let's see an example script. Please feel free to paste the following into your Inky to see it in action.

-> start_knot // this tells ink where to start the story

=== start_knot ===
Hello from the start knot!
Now we'll go to knot 2!
-> knot_2

=== knot_2 ===
Hello from knot 2!
-> END // this marks the end of the story
Enter fullscreen mode Exit fullscreen mode

You’ll see knot_2 ends with -> END line. This is to mark where the story ends. You want to make sure all of your knots have an ending, whether it's moving into the next knot or ending the story (Inky will remind you of that too).

Okay, so let’s get to that exciting bit now, the choices. In your game, you’re very likely to want to give a player a decision to make. Depending on what they choose, we want to branch off into a different knot. How is that done then?

*** Choice 1
[Continuation for choice 1]

*** Choice 2
[Continuation for choice 2]

Each choice is marked with *** and what follows will only be visible after the player chooses that option. Let’s add that into our example:

-> start_knot // this tells ink where to start the story

=== start_knot ===
Hello from the start knot!
Now we'll go to knot 2!

-> knot_2

=== knot_2 ===
Hello from knot 2!
Time for a personality test.
Red pill or blue pill?
*** Red pill
My god, how brave!
-> END // this marks the end of the story
*** Blue pill
Bold move, my friend
-> END // this marks the end of the story
Enter fullscreen mode Exit fullscreen mode

Now, you see each choice has its own continuation and we’ve performed our first branching! You can test your story in Inky, on the right hand side section of the window.

You can imagine it can get quite difficult to read and (and write) if you add a lot of text for each choice. This is why, as mentioned before, I like to split into further knots after each decision to make it easier to handle. Let’s change our example:

-> start_knot // this tells ink where to start the story

=== start_knot ===
Hello from the start knot!
Now we'll go to knot 2!
-> knot_2

=== knot_2 ===
Hello from knot 2!
Time for a personality test.
Red pill or blue pill?
*** Red pill
-> red_pill
*** Blue pill
-> blue_pill

=== red_pill ===
My god, how brave!
-> END

=== blue_pill
Bold move, my friend
-> END
Enter fullscreen mode Exit fullscreen mode

Now you see that each option has its own knot. From the player’s point of view, nothing has changed. However, doing things this way will prove very useful when localizing your game (you never know when you might need that!) and even just to make it more readable and easy to work with.

Choices matter

We’ve learned how to branch our story based on different choices, but in many cases, you’ll still want to share some same parts of the story afterward. Otherwise, it could get out of hand very quickly. If you want to do that, you just create a new knot and point into it from your choice specific knots.

-> start_knot

=== start_knot ===
Hello from the start knot!
Now we'll go to knot 2!
-> knot_2

=== knot_2 ===
Hello from knot 2!
Time for a personality test.
Red pill or blue pill?
*** Red pill
-> red_pill
*** Blue pill
-> blue_pill

=== red_pill ===
My god, how brave!
-> continue_conversation

=== blue_pill
Bold move, my friend
-> continue_conversation

=== continue_conversation
Alright. You have answered my question.
-> END
Enter fullscreen mode Exit fullscreen mode

Simple as that! These choices are meant to matter though. For example, what if we want to make a comment based on what the player has done? The most simple way is to check if we’ve visited a specific knot or not (hehe!). This is how to make that check:

{ knot_name: [further story content] }

Do you want to check if we’ve NOT visited a knot?

{ not knot_name: [further story content] }

By using this pattern, you can check for many different conditions and define the flow of the story accordingly. Let’s see it in action:

-> start_knot

=== start_knot ===
Hello from the start knot!
Now we'll go to knot 2!
-> knot_2

=== knot_2 ===
Hello from knot 2!
Time for a personality test.
Red pill or blue pill?
*** Red pill
-> red_pill
*** Blue pill
-> blue_pill

=== red_pill ===
My god, how brave!
-> continue_conversation

=== blue_pill
Bold move, my friend
-> continue_conversation

=== continue_conversation
Alright. You have answered my question.
{ red_pill:
You chose the red pill. But I'm still not sure I can trust you
}
{ not red_pill: -> no_red_pill_comment}
->END

=== no_red_pill_comment ===
You didn't choose the red pill. I'm not sure I can trust you.
-> END
Enter fullscreen mode Exit fullscreen mode

You'll notice I’ve used both approaches of typing the text directly after the check and of pointing to another knot. It's up to you how you want to structure your file and what's more convenient for you. Just bear in mind using knots can be a huge help later on.

Variables and conditions

If you’re familiar with programming, you might have thought the way to check if we’ve seen a knot is similar to ‘if-statements’. That’s exactly what it is and it can be used to check various different conditions! Before we do that though, let’s talk about variables.

In your story, you might want to keep track of some things like player name, health points, relationship levels between people, experience points, etc. You can do that in ink by using variables. Some example ones can look like so:

VAR PlayerName = “John”

VAR ChoseRedPill = false

VAR HealthPoints = 50

As you can see, we can have various types of variables. They can be numbers, text, or booleans (true/false values). After defining a variable, you can access it anywhere in your story, whether it's to display it, update it or use it to make another decision. Let’s add those variables into our game.

// Global variables
VAR PlayerName = "John"
VAR ChoseRedPill = false
VAR HealthPoints = 50

-> start_knot

=== start_knot ===
Hello, {PlayerName}! This is the starting knot! // display variable value
Now, we'll go to knot 2!
-> knot_2

=== knot_2 ===
Hello from knot 2!
Time for a personality test.
Red pill or blue pill?
*** Red pill
~ChoseRedPill = true // update variable value
-> red_pill
*** Blue pill
~HealthPoints -= 20 // update variable value
-> blue_pill

=== red_pill ===
My god, how brave!
-> continue_conversation

=== blue_pill
Bold move, my friend
-> continue_conversation

=== continue_conversation
{ HealthPoints < 50: You seem quite weak. I wonder why...}
Alright. You have answered my question.
{ ChoseRedPill:
You chose the red pill. But I'm still not sure I can trust you
}
{not red_pill: -> no_red_pill_comment}
->END

=== no_red_pill_comment ===
You didn't choose the red pill. I'm not sure I can trust you.
-> END
Enter fullscreen mode Exit fullscreen mode

As you can see, to display a variable within the text, use ~VariableName

To update a variable, use ~VariableName = [new value]. In case of numbers you can use += or -= to increase or decrease the value by a certain amount (e.g. ~HealthPoints -= 30).

To check for a condition using variables, follow the same pattern as before, { condition : result }. For example:

{ ChoseRedPill:
You chose the red pill. But I'm still not sure I can trust you
}
Enter fullscreen mode Exit fullscreen mode

Or

{ HealthPoints < 50: You seem quite weak. I wonder why...}
Enter fullscreen mode Exit fullscreen mode

This will make the line appear only when HealthPoints value is lower than 50.

Wrapping up

That’s it for the basics! There is a lot more to cover in regards to Ink, but I’m confident you can start writing great interactive stories or prototypes of your games with just the methods we’ve covered here. However, if you’re interested in learning more Ink magic, check out the official documentation. It’s amazing how much you can do with it!

Next time, we’ll see how to connect Ink to our Unity project.

Happy coding!

More in this series

Making a Visual Novel with Unity 1/5 - Introduction to ink
Making a Visual Novel with Unity 2/5 - Integration with ink (Coming out 1st Dec)
Making a Visual Novel with Unity 3/5 - Characters and emotions (Coming out 8th Dec)
Making a Visual Novel with Unity 4/5 - State handling (Coming out 15th Dec)
Making a Visual Novel with Unity 5/5 - Localisation (Coming out 22nd Dec)

Discussion (10)

Collapse
zimlearn profile image
Dr Abstract

Cool - always wanted to try - sort of faked it here altura.mobi/adventure/list/by/danzen - let me know what you think of the story if you read one - I like YogaTales, Nanora and Detective Fastbrush.

Collapse
k_bronowicka profile image
Klaudia Bronowicka Author

That's awesome, I hope you like it!

I tried YogaTales, pretty much all of my choices were different to the majority's and I kept getting confused haha

Collapse
zimlearn profile image
Dr Abstract

Was the confusion the story or the app - or... both - sigh. lol - probably would be more confused with Nanora... just doing the last story for that now.

Thread Thread
k_bronowicka profile image
Klaudia Bronowicka Author

I think the story, because it kept giving me story continuation I didn't choose and I got lost in what was actually happening haha Still, I like its sense of humour :D

Collapse
patricktingen profile image
Patrick Tingen

Did not know Ink but it looks fun. A long time ago I tried to write a text adventure (in turbo pascal, verrrrrry long time ago). I still have the source somewhere, but I might try to do it again with ink. At that time I got stuck in the complexity of all possible branches due to the choices.

Is it possible to have some sort of map with Ink, other than defining it with the variables you already introduced? In my TP adventure I had a map where the player could move north, east, west and south and move up and down

Collapse
k_bronowicka profile image
Klaudia Bronowicka Author

I don't think there is a way to introduce a map other than the variables, as you mentioned. Another, more interactive, way to do it would be to create a map and its visual representation in C# and Unity. There are ways to call C# functions from ink and to use ink API to go directly to specific knots 'from the code'. I think that integration could work nicely for what you're trying to do. I will be covering some of it in my next articles.

Collapse
patricktingen profile image
Patrick Tingen

Thanks, looking forward to it

Collapse
eelstork profile image
Tea

Cool. Been following Inkle for a while now but didn't notice they had a tool for Unity released.

Collapse
k_bronowicka profile image
Klaudia Bronowicka Author

Yes, they do and it all works very nicely together! I will be covering the integration in the future parts if you're interested to see more.

Collapse
eelstork profile image
Tea

Absolutely. Looking forward to it.