- https://github.com/inkle/ink/blob/master/Documentation/WritingWithInk.md taking notes to actually read through everything…
basic
- can comment //
tags
- can tag via # - following string content can be read by the game.
- use case: trigger different art based on emotion, e.g. # surly
- delimit multi tags with more #
- can mark ink with audio filenames
- tag can be written above or at end of the line, and all tags are in
_inkStory.currentTags
choices
*
- w/o further flow instr, choice flows into next line of text
- put choice text in square brackets if you don’t want it to be printed
*
[hello]
knots
- fundamental structural unit of content
- 2+ equals signs, like
=== top_knot ===
- name must be single word
- knots don’t run automatically, call them
-> top_knot
- need
->END
to signal end of flow
diverts
->
direct to a knot name to tell story where to go- seamless, can happen mid-sentence too
glue
- default - line-breaks before every new line of content
- change this with <>
branching + joining
are done with knots and diverts
- can use diverts to loop content
includes, stitches
- knots can be subdivided into stitches
- single equals sign =
=== the_orient_express ===
= in_first_class
...
= in_third_class
...
= in_the_guards_van
...
= missed_the_train
...
- knot can represent a scene, and stitches for events within the scene
- stitches have unique names
* [Travel in third class]
-> the_orient_express.in_third_class
-
first stitch is default when diverting to a knot with stitches
-
can also have non-stitch content in a knot w stitches, but need to manually divert into a stitch after that
-
combine script files via INCLUDE
varying choices
- default - choices can only be chosen once, so if you loop, chosen options will disappear
- fallback choice - choice without text
sticky choices
-
- bullet for choices that don’t disappear
- fallbacks can be sticky
conditional choices
* { not visit_paris } [Go to Paris] -> visit_paris
+ { visit_paris } [Return to Paris] -> visit_paris
* { visit_paris.met_estelle } [ Telephone Mme Estelle ] -> phone_estelle
- can check if a piece of content has been seen
- note that knot_name test is true if any stitch inside has been seen
- conditionals don’t override once-only option behavior - need sticky
- can use multiple conditionals
- ink supports && and || syntax too
- recommended to use not tho instead of !
- these are read counts not booleans - can check count
variable text - alternatives
sequence
- set of alternatives tracking how many times its been seen - each time shows next element; when it runs out of content, shows final
{I bought a coffee with my five-pound note.|I bought a second coffee for my friend.|I didn't have enough money to buy any more coffee.}
cycles
- loop, don’t end
It was {&Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday} today.
can use ~ for shuffle to randomize output
conditional text
{met_person: i remember seeing them here}
functions***
TURNS_SINCE(->knot)
- number of player inputs since a given knot/stitch was seen useful function
=== function came_from(-> x)
~ returns TURNS_SINCE(x) == 0
weave
- simplify story flows with always-forward direction
gather
-
- all options gather at this same ending
- flow is guaranteed to start at top and ‘fall’ to the bottom
nested flow
- add sub choices
- "Well, Poirot? Murder or suicide?"
* "Murder!"
"And who did it?"
* * "Detective-Inspector Japp!"
* * "Captain Hastings!"
* * "Myself!"
* "Suicide!"
- Mrs. Christie lowered her manuscript a moment. The rest of the writing group sat, open-mouthed.
- gather points can be nested too
- options separate paths, gathers bring them back together
- unlimited nesting levels
weave tracking
- lines in a weave dont have addr, can’t be diverted to
- to remember what a player has seen, we can add labels with
(label_name)
to anything- important for options later that are only unlocked based on the player having seen specific content
=== meet_guard ===
The guard frowns at you.
* (greet) [Greet him]
'Greetings.'
* (get_out) 'Get out of my way[.'],' you tell the guard.
- 'Hmm,' replies the guard.
* {greet} 'Having a nice day?' // only if you greeted him
* 'Hmm?'[] you reply.
* {get_out} [Shove him aside] // only if you threatened him
You shove him sharply. He stares in reply, and draws his sword!
-> fight_guard // this route diverts out of the weave
- 'Mff,' the guard replies, and then offers you a paper bag. 'Toffee?'
=== fight_guard ===
...
= throw_something
* (rock) [Throw rock at guard] -> throw
* (sand) [Throw sand at guard] -> throw
= throw
You hurl {throw_something.rock:a rock|a handful of sand} at the guard.
vars and logic
- global variables can be set and read from anywhere
VAR
- initial value determines type of variable- can store diverts as variables
- global vars can be touched from runtime too
- ink layer is good for storing gameplay vars - no save/load issues + the story can react to current values
~
says a line will do logic
=== set_some_variables ===
~ knows_about_wager = true
~ x = (x * x) - (y * y) + c
~ y = 2 * x * y
- a string defined in ink will always be a string, even if it has ink?
knots do logic but they aren’t quite functions, which have call stack and ret vals
- CONST
function
- cannot have stitches, diverts, choices
- can call functions, include printed content, return val of any type, recurse
- declare a knot to be one
== function hi ==
- can be called inline
external functions
- call C# functions in game
visual novel strategies
- plan out high-level story flow via diagram or twine first
- track key decisions with variables
- branch endings with conditions
running ink with Unity
https://github.com/inkle/ink/blob/master/Documentation/RunningYourInk.md
- create a wrapper MonoBehavior for ink Story
using Ink.Runtime;
public class Script : MonoBehaviour {
// Set this file to your compiled json asset
public TextAsset inkAsset;
// The ink story that we're wrapping
Story _inkStory;
void Awake()
{
_inkStory = new Story(inkAsset.text);
}
while (_inkStory.canContinue) {
Debug.Log (_inkStory.Continue ());
// if no more content, check for choices
if( _inkStory.currentChoices.Count > 0 )
{
for (int i = 0; i < _inkStory.currentChoices.Count; ++i) {
Choice choice = _inkStory.currentChoices [i];
Debug.Log("Choice " + (i + 1) + ". " + choice.text);
}
}
//...and when the player provides input:
_inkStory.ChooseChoiceIndex (index);
//And now you're ready to return to step 1, and present content again.
}
-
get ink tags to trigger other behavior
-
can make knot tags at the very top of a knot
-
tags can have vars like
{character}: Hello there! #{character}_greeting.jpg
-
can save and reload story state via json
-
use unity error handling to avoid ink errors throwing exceptions
_inkStory = new Story(inkAsset.text);
_inkStory.onError += (msg, type) => {
if( type == Ink.ErrorType.Warning )
Debug.LogWarning(msg);
else
Debug.LogError(msg);
};
other useful runtime funcs
_inkStory.ChoosePathString("myKnotName");
_inkStory.ChoosePathString("myKnotName.theStitchWithin");
- note, can’t reference labels like this, only knots and stitches
_inkStory.variablesState["player_health"] = 100
int health = (int) _inkStory.variablesState["player_health"]
_inkStory.state.VisitCountAtPathString("...");
variable observers
- register function to be called whenever a story variable changes
- enables state of ink vars to be reflected directly in UI
_inkStory.ObserveVariable ("health", (string varName, object newValue) => {
SetHealthInUI((int)newValue);
});
external functions
- define game functions callable from ink
EXTERNAL playSound(soundName)
_inkStory.BindExternalFunction ("playSound", (string name) => {
_audioController.Play(name);
});
besides this, can also just use text to write instructions to the game and define game-side custom parser
tunnels??
threads??
lists
- list of states for state tracking
- like an in/out nameboard
- names in parentheses are in the initial state of a list
LIST here = Jane, (Pat), Joe
- lists provide
- flags: each entry is event, use += to mark event as occured, test with ?
- state machine: each entry is state, use = to set, ++ and — to step forwrd or back, test with == or >
- properties: each list reps a property