Using InkPlayer
InkPlayer is a custom node that greatly simplifies the use of ink in Godot.
story.gd
is a direct port of Story.cs
, to use it in any engine, a bit
of boilerplate code is necessary. InkPlayer takes care of that boilerplate
so you can focus on building your game.
While it’s possible to instantiate story.gd
directly, it’s highly
recommended that you use InkPlayer instead.
In addition to reading this document, feel free to glance at story_player.tscn and story_player.gd, which use InkPlayer to run different stories.
Tip
InkPlayer’s API documentation is available here.
Main differences with story.gd
InkPlayer takes a resource as its input, rather than a string containing the JSON bytecode.
It unifies Ink’s original handlers and inkgd custom signals under the same set of consistent signals.
It adds convenient methods to save and load the story state.
It simplifies certain APIs, such as evaluate_function or remove_variable_observer.
Loop-based vs. signal-based flow
InkPlayer can be used in two different ways.
Warning
The examples below are not complete. For a working example, refer to story_player.gd.
Loop-based
This is the traditional way to use Ink.
onready var _ink_player = $InkPlayer
func _ready():
_ink_player.connect("loaded", self, "_story_loaded")
_ink_player.create_story()
func _story_loaded(successfully: bool):
if !successfully:
return
_continue_story()
func _continue_story():
while _ink_player.can_continue:
var text = _ink_player.continue_story()
# This text is a line of text from the ink story.
# Set the text of a Label to this value to display it in your game.
print(text)
if _ink_player.has_choices:
# 'current_choices' contains a list of the choices, as strings.
for choice in _ink_player.current_choices:
print(choice)
# '_select_choice' is a function that will take the index of
# your selection and continue the story by calling again
# `_continue_story()`.
_select_choice(0)
else:
# This code runs when the story reaches it's end.
print("The End")
Signal-based
Using signals makes the code a little bit more idiomatic for Godot. It’s also more flexible.
onready var _ink_player = $InkPlayer
func _ready():
_ink_player.connect("loaded", self, "_story_loaded")
_ink_player.connect("continued", self, "_continued")
_ink_player.connect("prompt_choices", self, "_prompt_choices")
_ink_player.connect("ended", self, "_ended")
_ink_player.create_story()
func _story_loaded(successfully: bool):
if !successfully:
return
_ink_player.continue_story()
func _continued(text, tags):
print(text)
_ink_player.continue_story()
func _prompt_choices(choices):
if !choices.empty():
print(choices)
# In a real-world scenario, _select_choice' could be
# connected to a signal, like 'Button.pressed'.
_select_choice(0)
func _ended():
print("The End")
func _select_choice(index):
_ink_player.choose_choice_index(index)
_continue_story()
Loading the story from a background thread
For bigger stories, loading the compiled story into the runtime can take a long time (more than a second). To avoid blocking the main thread, you may want to load the story from a background thread and display a loading indicator.
Fortunately, InkPlayer
supports loading the story in a thread out of the
box. Either tick Loads In Background in the inspector or set
loads_in_background to true
in code.
On platforms that don’t support threads, the feature is automatically disabled regardless of the value of loads_in_background.