How to use Twine to create believable game characters

Three easy ways to turn your text adventure characters into personalities

Using Twine to create believable game characters

If you want your text adventure game to be unforgettable, one of the best ways to do so is to populate it with characters that seem truly alive. Your players should not only read about your characters or observe them in action. It is important that they should be able to interact with them — and even more important that your characters are able to interact with your players.

How can you achieve this using simple game engines such as Twine?
In short, you cannot. To create truly profound character behavior, you have to use tools and engines that can take years to learn. (In fact, we’re developing Dark Forest: Reborn using our own powerful engine, which enables the creation of complex and immersive dialogues.) But even with simple engines, there are some techniques you can employ that will help you to make your characters seem more alive.

Today, we’ll briefly discuss three such techniques. To give the impression that they’re living beings, your characters must:


  • Exhibit varied behaviors

  • Play an active role in a conversation

  • Demonstrate a personal attitude


Let me elaborate.

1. Variability of behavior

Variability of behavior is key to creating characters that seem to be alive. When a text adventure player cannot predict your character’s next action, when the character is actually doing something rather than merely reacting to the environment or the player’s phrases or actions, an illusion will be created that there is something behind that moving figure of pixels or set of words; something that is alive. If that illusion is sophisticated enough, it will be indistinguishable from reality.

The simplest method of achieving variability of behavior is randomization. Despite its simplicity, it can give good results. The worst thing, which completely destroys the illusion of a living character, is the same response to the same input, always. Even if the input is not precisely the same.

Player: Hello.
Character: Hello. How are you?

For example, your dialogue with a character may start with the simplest possible interaction:

In 99.9% of all games, if you initiate the same dialogue again (for example, when replaying the game), if you input “Hello” you will receive the identical response: “Hello. How are you?”
But imagine if the NPC instead answered, “Hi. Long time, no see. How’s it going?” or “Salut. What’s new, PlayerName?”

It’s a simple touch, but most players wouldn’t expect it and it will add some extra points to your game. If there are even a few such unexpected twists in your game, the general curiosity level increases — who knows what the character may say, the next time the player asks something?

We can achieve randomization of phrases in Twine by using arrays.

----------------

(set: $helloPhrase to (a: "Hello!", "Hi!", "Salut!", "Welcome!"))

(set: $hello = $helloPhrase's random)
'Olivia: $hello'

----------------

2. Active behavior

As mentioned above, a typical text adventure character interaction goes like this: a player does something, and a character reacts. In the worst case, the same action always has the same reaction. This is not how people behave in real life. A character without active behavior looks like a request–response automate. This is something to be avoided, if you wish your game to be good.

To achieve some sort of active behavior when using Twine, you can use timers:

----------------

(set: $helloPhrase to (a: "Hello.", "Hi there.", "Hey."))
(set: $hello = $helloPhrase's random)
Olivia: $hello
(live:5000)[Olivia: A stranger...]
(live:6000)[Olivia: Interesting.]

----------------

(live:number_of_milliseconds) means the text inside [these brackets] will be displayed after exactly X milliseconds. In our example, the line “A stranger” will be displayed after five seconds, and “Interesting” after six seconds.

To add even more variety, randomize the timer value:

----------------

(set: $delay to (random: 10000, 15000))
(live:$delay)[Olivia: Why are you silent?]

----------------

With timers, you can not only make visual differences but also change the outcome of an encounter completely. For example, if a player says nothing for more than 30 seconds, the character could leave the location, forcing the player to seek a new encounter.

3. Personal attitude

To become unforgettable, your characters should appear to care about and think about the player. Of course, they should also have their own lives and activities. But if they are too indifferent or distant or passive, your players will never become fully immersed. They might start to think they’re actually watching a movie or a cartoon — instead of participating in the gift that only a videogame can offer.

Here’s how to easily demonstrate a character’s personal attitude.

1. The character should comment on the player’s actions, providing an emotional response.
2. The character should remember information about the player, and use it in their interactions.

Examples:

– If Olivia doesn’t know the player’s name, she asks the player to introduce herself. The player responds with “Ann”. Or if Ann refuses to introduce herself, Olivia can call her “stranger” instead.

– Olivia asks the player to deliver a stack of resources to the castle keep. The player fails and is forced to return to Olivia. Olivia doesn’t simply give the player another stack of resources to restart the quest, but also says something like “Stay strong, Ann. You will succeed this time.”

– The player defeats the game boss, and then receives congratulations from Olivia, who is impressed but continues to behave discreetly.

 

Example: Using Twine to get the player’s name:

----------------

Passage ‘Tile 1’:

(set: $helloPhrase to (a: "Hello", "Hi there", "Salut"))
(set: $hello = $helloPhrase's random)
(live:1000)[Olivia: $hello.] 
(live:3000)[Olivia: My name is Olivia, and I am the guardian of this sacred place.]
(live:7000)
[
 (set: $nameRequests to (a: "How should I call you?", "What's your name, stranger?", 
   "How may I address you?"))
 	(set: $nameRequest = $nameRequests's random)
 	(set: $name to (prompt: $nameRequest, "", "Refuse", "Answer"))
 	(goto: "Tile 2")
]

Passage ‘Tile 2‘:

(if: $name's length is 0)
[
 	(set: $name to "stranger")
 	Olivia: You have the right to distrust me, $name. For now…
] \
(else:)[Olivia: I am glad to meet you, $name.]

----------------

In this short article, we described three simple but efficient ways to make your characters seem more alive. There are many more ways, of course, and there are different approaches to achieving the behaviors described when using game engines such as Twine. We will describe some of those techniques in the next series of articles.

To learn more about character-centered text adventure game development techniques, follow our team:

By Steel S.