This plugin imports Arcweave projects into Unity. It supports data exported from Arcweave Project > Export > Engine > Export for Unity (available to all users) or fetched via Arcweave's web API (available to Team workspaces only).
- Installation
- Getting Data from Arcweave
- Creating an ArcweaveProjectAsset
- Using the Demo Scene
- How the Demo Works
- Plugin Documentation
- Important Notes
- Troubleshooting
Choose one of the following methods:
The bundled demo uses the Unity UI package. If your project does not already include it, install Unity UI (com.unity.ugui, version 2.0.0 or a compatible production release) from Window > Package Manager before importing the plugin. New Unity 6000.5 projects do not include this package by default, and the demo scripts cannot compile without it.
- Download
ArcweaveForUnity.unitypackagefrom the latest GitHub release - In Unity, go to Assets > Import Package > Custom Package and select the downloaded file
- Click Import. You can untick the
Demofolder if you don't need the demo scene
Alternatively, download ArcweaveForUnity.zip, extract it, and copy the Arcweave folder into your Unity project's Assets folder.
- Get the asset from the Unity Asset Store
- Open Unity and go to Window > Package Manager
- Select My Assets from the dropdown
- Find Arcweave for Unity and click Download, then Import
Note: This repository is not structured as a UPM (Unity Package Manager) package and cannot be installed via Git URL.
In Arcweave:
- Open your project
- Click the Export icon in the top bar
- Go to the Engine tab
- Click Export for Unity
- Check Include Assets if your project uses images or audio
You will get a .zip file containing:
project.json: all data of your Arcweave project- (Optional)
assets/Images/: project image assets (if "Include Assets" was checked) - (Optional)
assets/Audio/: project audio assets (if "Include Assets" was checked) - (Optional)
cover/cover.jpg: the project cover image
File placement:
- Place
project.jsonanywhere in your Unity project'sAssetsfolder. - For this demo project, images must go in a
Resourcesfolder (create one if it doesn't exist) or they won't load at runtime.
Available to Team workspaces.
You will need:
- Your Team workspace API key (found in your Workspace's API section).
- Your project hash (found in Project Properties).
Importing from web can also download the images and audio your project uses (see Media Downloaded from Web), so you don't need to copy them into a Resources folder.
See the Arcweave API Documentation for details.
To import your data into Unity:
- Right-click in the Unity Project Window.
- Navigate to Create > Arcweave > Project Asset.
- Name the new
.assetfile as you prefer. - Select it to open the Inspector.
Choose your import source:
- From Json: Drag your
project.jsonfile into the "Project Json File" field. - From Web: Enter your API Key and Project Hash. Optionally enable Download Media to download the project's images and audio.
Click the Import Project button to begin the import.
Download Media is disabled by default, so your project only relies on media downloaded from Arcweave if you choose to. When it is enabled, importing from web in the Unity Editor downloads every image used as an element or component cover and every audio file attached to an element through Arcweave's web API. The first import saves the files in a <Project Asset Name> Media folder next to the project asset, named after their Arcweave asset ids, and links them to the imported project, so they are included in builds without a Resources folder.
- Each import refreshes the media: changed files are updated in place and files the project no longer uses are deleted. The importer only overwrites or deletes files it downloaded for that project asset, so you can move or rename the folder and its files. A duplicated project asset downloads its own copy of the media.
- If a file fails to download, the import continues, keeps any previously downloaded copy of that file, and logs a warning.
- Unity cannot import some formats Arcweave accepts (
.webpimages and.webmaudio). These are skipped with a warning. - Media downloads count towards your workspace's API rate limit. When the limit is reached, the importer waits up to a minute and tries each media download again, up to five attempts in total, so importing projects with lots of media can take a while. Click Cancel on the progress bar to stop downloading; media that was not downloaded keeps its previous copy.
- Media is only downloaded in the Unity Editor, including when
ArcweavePlayerimports the project on Play. Reimporting in a built player keeps the media downloaded in the editor for the assets the project still uses.
Leave Download Media disabled to provide images and audio yourself in a Resources folder, as described below. Disabling it later doesn't delete downloaded files, but the next import no longer uses them.
After successful import, the Inspector displays:
- The project name
- All global variables and their values
- An "Open Project Viewer" button for viewing the project flowchart
If you press Play before importing, ArcweavePlayer imports the project automatically. You can also import from code with await aw.ImportProjectAsync(), or await aw.EnsureImportedAsync() to import only when the asset has no playable project yet.
Warning: The API key is saved in the asset and ships with your build. Treat From Web as a development workflow and import the project in the editor before building, so your game doesn't need the API key or a network connection to start.
The plugin includes a Demo folder with a complete example scene.
- Open ArcweaveDemoScene from the Demo folder.
- Select the ArcweavePlayer GameObject in the hierarchy.
- Assign your imported project asset to the
AWfield. - Press Play.
If you're using Unity 6 or newer, you need to update the Input System module:
- In the Hierarchy, select the EventSystem GameObject
- In the Inspector, find the Standalone Input Module component
- Click the Replace with InputSystemUIInputModule button
- This upgrades the legacy Input Manager to the new Input System
Without this step, UI interactions (buttons, clicks) will not work in Unity 6+.
Images are downloaded when importing from web with Download Media enabled. Otherwise:
- Create a
Resourcesfolder in your Unity project (if it doesn't exist). - Copy your image files into the
Resourcesfolder. - Ensure image filenames match those in Arcweave (without extension).
- Set each image's Texture Type to Default in Import Settings.
The plugin loads images dynamically using Resources.Load().
Audio is downloaded when importing from web with Download Media enabled. Otherwise:
- Create a
Resourcesfolder in your Unity project (if it doesn't exist). - Copy your audio files into the
Resourcesfolder. - Ensure audio filenames match those in Arcweave (including extension, e.g.
sfx.mp3).
The plugin resolves audio clips dynamically using AudioAsset.TryGetAudioClip(), which calls Resources.Load<AudioClip>() with the filename (without extension).
The demo consists of two main components that work together:
┌─────────────────────────────────────────────────────────────────┐
│ ArcweavePlayer │
│ (Handles narrative logic, traversal, and state management) │
├─────────────────────────────────────────────────────────────────┤
│ Events: │
│ ├── onProjectStart → Fired when narrative begins │
│ ├── onElementEnter → Fired when entering any element │
│ ├── onElementOptions → Fired when multiple choices available │
│ ├── onWaitInputNext → Fired when single path (continue) │
│ └── onProjectFinish → Fired when narrative ends │
└─────────────────────────────────────────────────────────────────┘
│
│ subscribes to events
▼
┌─────────────────────────────────────────────────────────────────┐
│ ArcweavePlayerUI │
│ (Handles all visual display and user interaction) │
├─────────────────────────────────────────────────────────────────┤
│ Responsibilities: │
│ ├── Display dialogue text (RuntimeContent) │
│ ├── Display cover images and character portraits │
│ ├── Create choice buttons dynamically │
│ ├── Handle button clicks → invoke callbacks │
│ └── Manage Save/Load UI │
└─────────────────────────────────────────────────────────────────┘
PlayProject()
│
▼
┌─────────────────┐
│ Initialize │ ← Resets all variables and visits
│ Project │
└────────┬────────┘
│
▼
┌─────────────────┐
│ onProjectStart │ ← UI can show "Game Started" message
└────────┬────────┘
│
▼
┌─────────────────┐
│ Enter Element │ ← element.Visits++ (incremented)
│ │
└────────┬────────┘
│
▼
┌─────────────────┐
│ onElementEnter │ ← UI displays content and images
└────────┬────────┘
│
▼
┌─────────────────┐
│ GetOptions() │ ← Evaluates all branch conditions
└────────┬────────┘
│
┌───────┴───────┐
│ │
▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Multiple │ │ Single │ │ No Paths │
│ Paths │ │ Path │ │ (End) │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│onElement- │ │onWaitInput│ │onProject- │
│ Options │ │ Next │ │ Finish │
└─────┬─────┘ └─────┬─────┘ └───────────┘
│ │
│ callback(i) │ callback()
│ │
└───────┬───────┘
│
▼
┌─────────────────┐
│ Execute Path │ ← Runs Arcscript in connection labels
│ Connection │
│ Labels │
└────────┬────────┘
│
▼
Enter Next
Element
(loop)
| Method | Class | Purpose |
|---|---|---|
Project.Initialize() |
Project | Reset variables and visits before starting |
element.Visits++ |
Element | Track how many times element was visited |
element.GetOptions() |
Element | Get available paths from current element |
element.RuntimeContent |
Element | Get evaluated dialogue text |
element.RunContentScript() |
Element | Execute Arcscript and update RuntimeContent |
element.GetCoverOrFirstComponentImage() |
Element | Get element's cover or first component image |
element.GetAudioAssets() |
Element | Get audio assets attached to the element |
path.ExecuteAppendedConnectionLabels() |
Path | Run Arcscript in connection labels |
path.text |
Path | Get label text or target element title |
options.Paths |
Options | List of valid paths to choose from |
options.hasPaths |
Options | Check if any paths exist |
options.hasOptions |
Options | Check if multiple choices available |
The root container for an imported Arcweave project. Access via ArcweaveProjectAsset.Project.
| Property | Type | Description |
|---|---|---|
name |
string | Project name |
boards |
List<Board> | All boards in the project |
components / Components |
List<Component> | All components (characters/entities) |
GlobalVariables |
List<Variable> | All global/project-scoped variables |
Variables |
List<Variable> | Deprecated alias for GlobalVariables |
StartingElement |
Element | Entry point for the narrative |
| Method | Description |
|---|---|
void Initialize() |
Must call before use. Resets all variables and visit counts. |
Board BoardWithID(string id) |
Get board by ID |
Board BoardWithName(string name) |
Get board by name |
Element ElementWithId(string id) |
Get element by ID |
T GetNodeWithID<T>(string id) |
Get any node by ID and type |
Variable GetVariable(string name, string scope = null) |
Get a variable by name. Pass a board or component CustomId for scoped variables |
IEnumerable<Variable> GetAllVariables() |
Enumerate global, board-scoped, and component-scoped variables |
bool SetVariable(string name, object value) |
Set a global/project-scoped variable by name |
bool SetVariable(string name, string scope, object value) |
Set a scoped variable by name and owner CustomId |
void ResetVariablesToDefaultValues() |
Reset all variables to defaults |
string SaveVariables() |
Serialize current global and scoped variable state to JSON |
void LoadVariables(string json) |
Restore current global and scoped variable state from JSON |
int Visits(string elementId) |
Get visit count for an element |
void ResetVisits() |
Reset all visit counts to 0 |
Boards and components can define scoped variables in addition to project/global variables. In current exports, scoped variables are backed by boolean, integer, float, or plain-string attributes with a custom ID.
Project.GlobalVariablescontains only project/global variables.Board.VariablesandComponent.Variablescontain their scoped variables.Project.GetAllVariables()enumerates variables from all three scopes.- Scoped variables use the attribute's
CustomIdas their name and the owning container'sCustomIdas their scope. - The attribute ID remains the variable ID used by runtime changes and save data.
- In Arcscript, scoped variables use
containerCustomId.variableName.
Variable globalHealth = project.GetVariable("health");
Variable boardHealth = project.GetVariable("health", "intro_board");
Variable componentHealth = project.GetVariable("health", "hero");
project.SetVariable("health", 10);
project.SetVariable("health", 2, "intro_board");
project.SetVariable("health", 5, "hero");Both the source attribute and its board/component must have a CustomId for the attribute to become a scoped variable. Plain-string attributes with null data import as an empty string.
Use Project.GlobalVariables when accessing the global-variable list. Existing Project.Variables code continues to work through a deprecated alias to the same list. IProject.Variables remains available so existing interpreter callers and custom implementations remain compatible.
Previously imported Unity assets retain their global variables automatically; no reimport is required. The Arcweave JSON export format and variable save-state format are unchanged.
Container for narrative nodes.
| Property | Type | Description |
|---|---|---|
Id |
string | Unique identifier |
CustomId |
string | Optional Arcweave custom id used as the board-variable scope |
Name |
string | Board name |
Nodes |
List<INode> | All nodes (Elements, Branches, Jumpers) |
Notes |
List<Note> | Annotation notes |
Attributes |
List<Attribute> | Board attributes |
Variables |
List<Variable> | Variables scoped to this board |
| Method | Description |
|---|---|
T NodeWithID<T>(string id) |
Get node by ID and type |
Element ElementWithID(string id) |
Get element by ID |
A narrative node containing dialogue content.
| Property | Type | Description |
|---|---|---|
Id |
string | Unique identifier |
Title |
string | Element title (may contain Arcscript) |
RawContent |
string | Original content with Arcscript markup |
RuntimeContent |
string | Evaluated content (call RunContentScript() first!) |
Visits |
int | Visit counter (get/set) |
Components |
List<Component> | Attached characters/entities |
Attributes |
List<Attribute> | Custom metadata |
Outputs |
List<Connection> | Outgoing connections |
cover |
Cover | Cover image reference |
AudioAssets |
AudioAsset[] | Audio assets attached to this element |
| Method | Description |
|---|---|
void RunContentScript() |
Evaluate Arcscript in content → updates RuntimeContent |
Options GetOptions() |
Get available paths (evaluates branch conditions) |
bool HasContent() |
Check if element has content |
bool HasComponent(string name) |
Check if element has component by name |
bool TryGetComponent(string name, out Component c) |
Try to get component by name |
Texture2D GetCoverImage() |
Get the downloaded cover image or load it from Resources |
Texture2D GetFirstComponentCoverImage() |
Get first component's cover image |
Texture2D GetCoverOrFirstComponentImage() |
Get cover or fallback to component image |
AudioAsset[] GetAudioAssets() |
Get all audio assets attached to this element |
// WRONG - RuntimeContent will be empty/outdated
string text = element.RuntimeContent; // ❌ Don't do this!
// CORRECT - Always call RunContentScript() first
element.RunContentScript(); // ✅ Evaluates Arcscript
string text = element.RuntimeContent; // ✅ Now it's updated
// Get available choices
Options options = element.GetOptions();
if (options.hasPaths) {
foreach (Path path in options.Paths) {
Debug.Log("Choice: " + path.text);
}
}Link between nodes with optional label.
| Property | Type | Description |
|---|---|---|
Id |
string | Unique identifier |
RawLabel |
string | Original label with Arcscript |
RuntimeLabel |
string | Evaluated label (call RunLabelScript() first!) |
Source |
INode | Source node |
Target |
INode | Target node |
isValid |
bool | True if connection has valid ID |
| Method | Description |
|---|---|
void RunLabelScript() |
Evaluate Arcscript → updates RuntimeLabel |
Conditional branching node with if/elseif/else logic.
| Property | Type | Description |
|---|---|---|
Id |
string | Unique identifier |
Conditions |
List<Condition> | Ordered conditions (first true wins) |
colorTheme |
string | Visual theme color |
Single condition in a branch.
| Property | Type | Description |
|---|---|---|
Id |
string | Unique identifier |
Script |
string | Arcscript condition expression |
Output |
Connection | Output connection if true |
| Method | Description |
|---|---|
bool Evaluate() |
Evaluate condition (empty scripts return true) |
Navigation shortcut to another element.
| Property | Type | Description |
|---|---|---|
Id |
string | Unique identifier |
Target |
Element | Target element to jump to |
Character or entity definition. Not a Unity MonoBehaviour.
| Property | Type | Description |
|---|---|---|
Id |
string | Unique identifier |
CustomId |
string | Arcweave custom ID used as a variable scope |
Name |
string | Component name |
Attributes |
List<Attribute> | Custom attributes |
Variables |
List<Variable> | Variables scoped to this component |
cover |
Cover | Cover image reference |
| Method | Description |
|---|---|
Texture2D GetCoverImage() |
Get the downloaded cover image or load it from Resources |
Custom metadata on elements, components, or boards.
| Property | Type | Description |
|---|---|---|
Id |
string | Arcweave attribute identifier |
CustomId |
string | Optional Arcscript variable name for eligible scoped attributes |
Name |
string | Attribute name |
Type |
DataType | Plain/rich string, component list, boolean, integer, or float |
data |
object | Attribute data (lazy-evaluated for RichText) |
containerType |
ContainerType | Element, Component, or Board |
containerId |
string | ID of the container |
Project/global, board-scoped, or component-scoped state variable. Supports: int, double, bool, string.
| Property | Type | Description |
|---|---|---|
Id |
string | Arcweave variable identifier used for runtime updates and save/load |
Name |
string | Variable name |
Value |
object | Current value |
DefaultValue |
object | Initial value |
Parent |
IHasVariables | Owning scope (null for globals, otherwise a Board or Component) |
Type |
Type | System.Type of the value |
| Method | Description |
|---|---|
void ResetToDefaultValue() |
Reset to default value |
Image or video reference.
| Property | Type | Description |
|---|---|---|
type |
Cover.Type | Image, Youtube, or Undefined |
filePath |
string | Asset filename |
assetId |
string | Arcweave asset id |
downloadedImage |
Texture2D | Image downloaded when importing from web |
| Method | Description |
|---|---|
Texture2D ResolveImage() |
Return the downloaded image, otherwise load it from a Resources folder |
Available paths from an element. Created by Element.GetOptions().
| Property | Type | Description |
|---|---|---|
Element |
Element | Source element |
Paths |
List<Path> | Valid paths to next elements |
hasPaths |
bool | True if any paths exist |
hasOptions |
bool | True if multiple paths OR path has label |
Resolved path to a target element.
| Property | Type | Description |
|---|---|---|
label |
string | Connection label (if any) |
text |
string | Label or target element's title |
TargetElement |
Element | Destination element |
| Method | Description |
|---|---|
void ExecuteAppendedConnectionLabels() |
Run Arcscript in all connection labels along path |
Event-driven narrative player (demo helper class).
| Field | Type | Description |
|---|---|---|
aw |
ArcweaveProjectAsset | The project to play |
autoStart |
bool | Auto-start on scene load |
| Event | Signature | Description |
|---|---|---|
onProjectStart |
(Project) |
Narrative started |
onProjectFinish |
(Project) |
Narrative ended |
onElementEnter |
(Element) |
Entered new element |
onElementOptions |
(Options, Action<int>) |
Multiple choices available |
onWaitInputNext |
(Action) |
Single path, waiting for continue |
| Method | Description |
|---|---|
void PlayProject() |
Start or restart the narrative |
void Save() |
Save current state to PlayerPrefs |
void Load() |
Load and restore saved state |
-
Unity 6+ requires Input System update: If using Unity 6 or newer, the EventSystem must use the new Input System module. Select EventSystem in Hierarchy, find Standalone Input Module in Inspector, and click "Replace with InputSystemUIInputModule". Without this, UI buttons won't respond to clicks. See Unity 6+ Input System Fix.
-
RuntimeContent requires RunContentScript(): The
RuntimeContentproperty is not automatically evaluated. You must callRunContentScript()before reading it. -
GetOptions() has side effects: This method internally saves and restores variable state while evaluating branch conditions.
-
Initialize() resets everything: Calling
Project.Initialize()resets all global and scoped variables to defaults and all visit counts to 0. -
Scoped variables require scope:
Project.GetVariable("health")only searches project/global variables. Pass the owning board or componentCustomIdto access a scoped variable. -
Images and Audio clips come from web imports or a Resources folder: Importing from web with Download Media enabled downloads cover images and audio clips. Otherwise they are loaded via
Resources.Load<T>(), so place them in anyResourcesfolder and ensure filenames match (without extension). -
Components are not MonoBehaviours: Arcweave Components represent characters/entities in your narrative, not Unity components.
-
Visit tracking is manual: The
Visitsproperty must be incremented manually (or use ArcweavePlayer which does it automatically).
Problem: UI buttons don't respond to clicks in Unity 6 or newer versions.
Solution:
- Select EventSystem in the Hierarchy
- In the Inspector, locate Standalone Input Module
- Click Replace with InputSystemUIInputModule
- This migrates from the legacy Input Manager to the new Input System
Unity 6 requires the new Input System for UI interactions.
Problem: TryGetAudioClip() returns null.
Solution:
- If you import from web, check the Console for download warnings and confirm Download Media is enabled
- Ensure audio files are in a folder named exactly
Resources(case-sensitive) - Verify the filename in your Arcweave project matches the file on disk (the plugin strips the extension automatically)
- Confirm the audio format is supported by Unity (e.g.
.wav,.mp3,.ogg)
Problem: GetCoverImage() returns null.
Solution:
- If you import from web, check the Console for download warnings and confirm Download Media is enabled
- Ensure images are in a folder named exactly
Resources(case-sensitive) - Verify the filename matches (without extension)
- Check the image's Texture Type is set to Default in Import Settings
Problem: Seeing {variable_name} or if/endif blocks in text.
Solution:
// Always call this before reading RuntimeContent
element.RunContentScript();Problem: Variables are null or throw errors.
Solution:
// Call Initialize() once before using the project
project.Initialize();Problem: GetOptions() returns no paths or wrong paths.
Solution:
- Check your branch conditions in Arcweave - they might all be evaluating to false
- Verify variable values are correct:
Debug.Log(project.GetVariable("varName").Value); - Use the Project Viewer in Unity to visually inspect connections
Problem: Load() doesn't restore the correct state.
Solution:
- Ensure you called
Save()beforeLoad() - PlayerPrefs persists between sessions - use
PlayerPrefs.DeleteAll()to clear if testing - Check the save keys exist:
PlayerPrefs.HasKey("arcweave_save_currentElement")
Problem: Can't click "Import Project" button.
Solution:
- From Json mode: Drag a TextAsset (.json file) into the "Project Json File" field
- From Web mode: Fill in both API Key AND Project Hash fields
- Ensure the JSON file is valid (open it in a text editor to verify it's not corrupted)
See the LICENSE file for details.