Tutorial: Defold Integration
This tutorial walks you through integrating plotknot into a Defold project from scratch. You’ll compile a story, load it in a Defold script, display text in a GUI, and present choices as buttons.
This tutorial assumes you’ve read the Programmer Tutorial and have a basic Defold project set up.
What you’ll build
A dialogue system: a GUI label shows story text, GUI buttons show choices, and pressing a button advances the story.
Step 1: Copy the runtime
Copy the plotknot runtime into your Defold project:
cp runtime/defold/plotknot.lua your_project/main/
The runtime is a single Lua file with no dependencies beyond Lua 5.1+ (which Defold includes).
Step 2: Write and compile a story
Create main/hello.plotknot:
# The Forest
You stand at the edge of a dark forest. The path ahead splits in two.
choice:
- Take the left path -> left_path
- Take the right path -> right_path
## Left Path
You follow the mossy trail deeper into the woods.
-> ending
## Right Path
The right path leads to a sunlit clearing.
-> ending
## Ending
Your journey through the forest ends here.
Compile it:
plotknot compile main/hello.plotknot --output main/
# compiled: main/hello.plotknot -> main/hello.lua
This produces main/hello.lua — a Lua table Defold can load as a module.
Step 3: Create a GUI for dialogue
In Defold, create a GUI scene (dialogue.gui) with:
- A text node named
story_text— displays the current narrative text - A box node named
choice_1with a text node — first choice button - A box node named
choice_2with a text node — second choice button - A box node named
choice_3with a text node — third choice button
Position them at the bottom of the screen. Hide the choice buttons by default.
Step 4: Create a script to drive the story
Create main/dialogue.script:
local plotknot = require("main.plotknot")
local story_data = require("main.hello")
function init(self)
self.story = plotknot.load(story_data)
self.story:start()
-- Hide choice buttons initially
gui.set_enabled(gui.get_node("choice_1"), false)
gui.set_enabled(gui.get_node("choice_2"), false)
gui.set_enabled(gui.get_node("choice_3"), false)
show_current(self)
end
function show_current(self)
local lines = self.story:current()
local text_parts = {}
local choices = nil
for _, line in ipairs(lines) do
if line.type == "text" then
table.insert(text_parts, line.text)
elseif line.type == "choice" then
choices = line.choices
end
end
-- Display text
gui.set_text(gui.get_node("story_text"), table.concat(text_parts, "\n\n"))
-- Display choices
local buttons = { "choice_1", "choice_2", "choice_3" }
for i, btn_name in ipairs(buttons) do
local btn = gui.get_node(btn_name)
if choices and choices[i] then
gui.set_enabled(btn, true)
gui.set_text(gui.get_node(btn_name .. "/text"), choices[i].text)
else
gui.set_enabled(btn, false)
end
end
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.pressed then
-- Check which choice button was pressed
for i = 1, 3 do
local btn = gui.get_node("choice_" .. i)
if gui.is_enabled(btn) and gui.pick_node(btn, action.x, action.y) then
self.story:advance(i)
show_current(self)
return true
end
end
end
return false
end
Step 5: Add the script to your collection
- Add the
dialogue.guiscene to your main collection - Attach
dialogue.scriptto the GUI scene - Make sure
hello.luaandplotknot.luaare in themain/directory
Step 6: Run it
Build and run your Defold project. You should see:
- “You stand at the edge of a dark forest. The path ahead splits in two.”
- Two choice buttons: “Take the left path” and “Take the right path”
- Pressing a button navigates to that stage and shows the next text
Step 7: Add game function calls
Register handlers for call statements in your story:
function init(self)
self.story = plotknot.load(story_data)
-- Register game functions
self.story:on_call("play_sound", function(name)
-- Play a Defold sound component
msg.post("#sound", "play_sound", { sound = name })
end)
self.story:on_call("shake_camera", function(intensity)
-- Trigger a camera shake
msg.post("/camera#script", "shake", { intensity = intensity })
end)
self.story:start()
show_current(self)
end
In your story:
## Enter
call play_sound("footsteps")
call shake_camera(2)
You descend into the darkness.
Step 8: React to stage changes
Use on_stage to trigger side effects when the story enters a stage:
self.story:on_stage("boss_room", function()
msg.post("/music#script", "play_track", { track = "boss_theme" })
end)
self.story:on_stage("ending", function()
msg.post("/ui#script", "show_credits")
end)
Step 9: Handle story end
Check story:stopped() to detect when the story has ended:
function show_current(self)
if self.story:stopped() then
gui.set_text(gui.get_node("story_text"), "The End")
-- Hide all choice buttons
for i = 1, 3 do
gui.set_enabled(gui.get_node("choice_" .. i), false)
end
return
end
-- ... rest of show_current
end
What you’ve built
A complete Defold dialogue system:
- Story text displayed in a GUI label
- Choices presented as tappable buttons
- Game function calls for sound and camera effects
- Section-entry hooks for music and UI triggers
- Story-end detection
What to read next
- Defold Integration Reference — project structure, saving/loading, common patterns
- Runtime API — full Lua runtime reference
- Extensibility — custom functions, call handlers, and line types