Defold
← Bring content into your game
Defold · Lua · GUI
Start with one visible sentence. This small example reads the real Harbour export directly; it is not a complete dialogue player. Then connect the data to your game UI.
1 · Prepare files and scene
Copy story.json to /story/. Add /story to project → custom_resources in game.project. Create a GUI with a text node dialogue, choose a font resource, and add the GUI to your game object. Assign first_text.gui_script to the GUI and run the project.
story.json · Export files explained
2 · Show the first text
-- first_text.gui_script: attach to a GUI containing a text node named dialogue.
function init(self)
local raw, error = sys.load_resource("/story/story.json")
if not raw then
gui.set_text(gui.get_node("dialogue"), "Cannot load story.json")
return
end
local ok, story = pcall(json.decode, raw)
if not ok then
gui.set_text(gui.get_node("dialogue"), "Invalid JSON")
return
end
local locale = "en"
local id = "fd7b61a3-8233-4ba7-8f77-b88b119a91bf"
local content = story.content
local translation = (content.translations[locale] or {})[id]
local message = translation and translation.message or content.texts[id].sourceMessage
local parts = {}
for _, segment in ipairs(message.segments) do
assert(segment.kind == "text", "Text-only example")
parts[#parts + 1] = segment.text
end
gui.set_text(gui.get_node("dialogue"), table.concat(parts))
endExpected: “Welcome to the harbour. I am Mira.” With locale = "de" or the language parameter de: “Willkommen im Hafen. Ich bin Mira.” A missing language uses source text. This sample supports text segments; it deliberately does not silently discard placeholders.
3 · From one sentence to a conversation
The fixed text ID above is for the first test. In your game choose flowRef/entryRef from story-index.json, read execution.entries[entryRef].nodeRef, then execution.nodes[nodeRef]. The current dialogue supplies text.textRef and speakerRef. Continuations depend on node kind.
Name/dialogue: gui.set_text. Answers: GUI nodes, input focus and gui.pick_node in on_input; retain the option ID separately. End: disable the common GUI parent with gui.set_enabled(node, false) and release input focus.
- Continue and End: follow connections, close the text box
- Choices: answer ID, order and selected continuation
- Conditions and variables: ten or two gold
- Language: the same IDs, newly resolved content
- Command: give an item or open a shop
- Call/Return: visit a substory and return
Inspect answers and the gold check in this language
Read the data for a choice
Insert this fragment where story is available after loading (for RPG Maker: const story = $gameTemp.vnleExample.story;). It prints IDs for inspection. Replace the output with your answer buttons. It covers the two Harbour answers and their following gold check, not a general interpreter.
-- Inside a function where the decoded story table is available.
local choice = story.execution.nodes["42edfff8-b8fe-46bf-ae0d-9e2347b6e726"]
for _, option_id in ipairs(choice.optionOrder) do
local option = choice.options[option_id]
print(option_id, option.text.textRef)
-- Resolve the text; retain option_id for the corresponding GUI button.
end
local branch = story.execution.nodes["c1c3c372-0a73-4cd0-851d-ede59d5709bb"]
local gold = 10 -- Repeat with 2.
local next_step = branch.whenFalse
if gold >= branch.condition.right.value.value then next_step = branch.whenTrue end
print(next_step.nodeRef)With 10 gold, next refers to “Pay five gold”; with 2 it refers to “Not enough gold”. In the actual game, run the check only when the selected path reaches this node. Each selected answer supplies its own continuation.
4 · Map your own images
This supplementary excerpt expects a current dialogue object named node and the UI elements named in its comments. For C++ and MonoGame, speakerRef has already been read as SpeakerId or speakerId. The key is Mira’s actual character ID; the image name and location belong to your game. Insert the lines at the indicated locations.
-- Add mira.png to an atlas and register that atlas as portraits in the GUI Textures.
local portraits = { ["807b2957-7bab-4a28-83a4-1f3d1589bbc2"] = "mira" }
local portrait = gui.get_node("portrait") -- GUI Box node using the portraits texture
local animation = portraits[node.speakerRef]
gui.set_enabled(portrait, animation ~= nil)
if animation then gui.play_flipbook(portrait, hash(animation)) endResolve speaker names through content.characters[speakerRef].nameTextRef just like dialogue text. Missing mappings should hide the previous portrait or show a placeholder. Images are not selected by language.
Use image references from the complete export
5 · Ship files with the game
custom_resources includes the raw JSON in the bundle. Lua arrays start at 1: iterate optionOrder with ipairs. Read JSON ID maps with table[id]. Add images to the GUI through atlas resources.
Check your result
- English and German greetings display correctly; an unavailable language falls back to English.
- When adding flow: try both answers, gold 10/2, the return from the substory and End.
- Run the built game outside your project folder. JSON, fonts and portraits must be available there too.
Official documentation
API and setup references for this approach: