Skip to main content

Exporting Your Game

Your project runs from imported files inside res://, not from the StoryFlow Editor, so a shipped game needs those files packed with it. This page covers how the runtime finds the project at startup, what has to be included in the export and how media is resolved once the raw files are no longer readable.

Requires v1.2.2

Exported games load a synced project reliably from plugin v1.2.2 onward. Earlier versions could import and play a project perfectly in the editor while the exported build started with no project and no media. If you are shipping, update the plugin first.

How the Runtime Finds Your Project

The StoryFlowRuntime autoload loads the project on its own at startup. There is nothing to call and nothing to wire up:

  1. It reads res://storyflow/storyflow_import_meta.json, the metadata file written by the last sync or manual import.
  2. It takes the output_dir field out of that metadata.
  3. It loads the project from that directory. It looks for project.storyflow first and falls back to project.json. In a build only project.json is readable, so that is the one that loads.

If the metadata file is missing, unparseable or points at a directory with no project file, the autoload simply ends up with no project and every start_dialogue() call fails with No StoryFlow project loaded.

GDScript
# Verify the project actually loaded in a build
func _ready() -> void:
    if not StoryFlowRuntime.has_project():
        push_error("No StoryFlow project in this build")
        return
    print("Loaded: ", StoryFlowRuntime.get_project().title)

The Output Directory Path Is Fixed

Auto-discovery reads exactly res://storyflow/storyflow_import_meta.json. The dock, on the other hand, writes the metadata into whatever Output directory you configure, which defaults to res://storyflow. Point the output somewhere else and the import still succeeds, but the runtime looks in the fixed location, finds nothing and never loads the project. Unless you set the project on the manager yourself with set_project(), leave the output directory at res://storyflow.

Why the Sync Publishes project.json

Added in v1.2.2. Each sync and manual import publishes the project data it parsed into the output directory as project.json, and that is the file the runtime reads in a shipped game.

The .storyflow file from your build cannot do that job. Left unimported it is not packed into the export at all, and once Godot imports it the plugin's import plugin replaces it with a small marker resource that carries no project data. Either way there is nothing readable at runtime, which is why an exported game built with an older plugin version started up with no project even though the editor played it fine.

Media in an Exported Game

Godot packs the imported version of a media file - CompressedTexture2D for images, AudioStreamWAV, AudioStreamMP3 and so on for audio - and not the raw bytes you dropped into the project. The importer normally reads media through file buffers, which works in the editor and in play mode but finds nothing in a build.

Since v1.2.2 the importer falls back to ResourceLoader whenever the raw file is not readable, reaching the imported resource through Godot's path remap. Portraits, dialogue images and dialogue audio resolve in an exported game without any change on your side.

A Surviving Warning Means Something Real

If StoryFlow: Source media file not found still appears after the fallback, the asset is genuinely absent: not in the build and not imported into the Godot project either. Run a full sync rather than a data-only one so the file is copied in, then export again.

What Has to Ship

Two things must be present in the exported build. Neither is optional.

  • The output directory (res://storyflow by default) - it holds project.json, your script JSON files, the media copies and storyflow_import_meta.json. This is the project.
  • addons/storyflow/editor/ - despite the name, this folder is not editor-only at runtime. The autoload preloads storyflow_importer.gd from it and instantiates the importer to read the project at startup. Exclude the folder from your export preset and the autoload breaks, taking the whole dialogue system with it.

Do Not Filter Out the editor Folder

It is a natural instinct to strip addons/*/editor/ from a release build. Do not do it for this plugin. The dock, the import plugin and Live Sync only ever run inside the Godot editor, because only the plugin's EditorPlugin creates them, but the importer that sits beside them is loaded by the runtime autoload in every build.

The Read-Only res:// Filesystem

Loading the project at runtime runs the same importer code the editor uses, including its copy steps, and res:// is read-only in an exported game. Since v1.2.2 every copy first compares content (size, then MD5) and skips the write when the destination already matches. In a build the destination always matches, because source and destination are the same directory, so loading a project at runtime is a no-op instead of a stream of failed writes and "could not create directory" errors.

Checklist

Before you ship a build:

  • Plugin is v1.2.2 or newer.
  • A full sync or manual import has been run, so project.json and the media copies are current in the output directory.
  • The last sync reported no errors. A result line ending in N errors. Check Output log. means something on disk is not what it should be - see Import and Sync Errors.
  • The output directory is res://storyflow, or you call set_project() yourself.
  • addons/storyflow/editor/ is not excluded from the export preset.
  • The build starts with StoryFlowRuntime.has_project() returning true.

Test the Export, Not Just the Editor

An imported project behaves differently in a build than in the editor: the raw media files are gone, res:// is read-only and only what the export packed exists. Run an actual export once early in the project rather than at the end, and check the Output log of the running build for StoryFlow messages.

Need Help?

Join our Discord community to ask questions, share your projects, report bugs, and get support from the team and other users.

Join Discord