Scripting Narrative Design

The complete C# surface of Narrative Design — IConvaiNarrativeDesign events, InvokeTrigger, InvokeSpeech with speak tags, async section fetching, and runtime trigger reconfiguration.

Controlling Narrative Design Programmatically

The Inspector workflow covers the majority of use cases. This page documents the full C# surface for situations where you need programmatic control — dynamic character switching, async data fetching at runtime, runtime-generated narrative flows, or deep integration with your own game systems.

All of the capabilities described here are available through IConvaiNarrativeDesign, which is exposed on every ConvaiCharacter via the NarrativeDesign property. ConvaiNarrativeDesignManager and ConvaiNarrativeDesignTrigger both delegate to this interface internally, so everything you configure in the Inspector is also reachable from code.

Accessing the Character API

Every ConvaiCharacter exposes a NarrativeDesign property that returns an IConvaiNarrativeDesign implementation:

ConvaiCharacter character = GetComponent<ConvaiCharacter>();
IConvaiNarrativeDesign narrative = character.NarrativeDesign;

Properties

Property
Type
Description

TemplateKeys

IReadOnlyDictionary<string, string>

Snapshot of all template keys currently tracked for this character.

CurrentSectionId

string

The section ID most recently received from the backend. Empty string if no section has been received yet.

CurrentSectionData

NarrativeSectionData

Full section payload. Contains SectionId, BehaviorTreeCode, and BehaviorTreeConstants. null until the first section change is received.

Listening to Section Changes

Subscribe to these events in OnEnable and unsubscribe in OnDisable to avoid stale listeners after a component is disabled or destroyed.

private void OnEnable()
{
    character.NarrativeDesign.OnSectionChanged     += HandleSectionChanged;
    character.NarrativeDesign.OnSectionDataReceived += HandleSectionData;
}

private void OnDisable()
{
    character.NarrativeDesign.OnSectionChanged     -= HandleSectionChanged;
    character.NarrativeDesign.OnSectionDataReceived -= HandleSectionData;
}

private void HandleSectionChanged(string previousId, string newId)
{
    Debug.Log($"Section: {previousId}{newId}");
}

private void HandleSectionData(NarrativeSectionData data)
{
    Debug.Log($"Section ID: {data.SectionId}");
    // data.BehaviorTreeCode and data.BehaviorTreeConstants available here
}

Events

Event
Signature
Description

OnSectionChanged

Action<string, string>

Fires on every section transition. Parameters: previousId, newId.

OnSectionDataReceived

Action<NarrativeSectionData>

Fires on every section transition with the full payload.

OnTriggerInvoked

Action<ConvaiNarrativeTriggerInvocation>

Fires after a trigger or speech request is accepted locally (before backend confirmation).

Invoking Triggers from Code

InvokeTrigger returns false if both triggerName and triggerMessage are empty, or if the trigger is rejected internally. Otherwise it returns true and queues the trigger if the session is not yet open.

Controlling What the Character Says

InvokeSpeech gives you direct control over the character's next utterance without advancing the narrative graph. It has two distinct modes depending on whether you wrap the message in <speak> tags.

Context Injection (plain text)

Pass a plain string to make the character aware of a piece of information. The character absorbs the context and responds in its own words — the exact phrasing is up to the AI.

Use this when you want the character to react to a game event in a natural, conversational way rather than reading from a script.

Verbatim Speech (speak tags)

Wrap the message in <speak> tags to make the character say that exact text word for word.

Use this for announcements, scripted lines, safety alerts, or any moment where the exact wording matters.

Comparison

Pattern
What the character does

InvokeSpeech("text")

Becomes aware of the context, responds in its own words

InvokeSpeech("<speak>text</speak>")

Says that exact text verbatim

InvokeSpeech does not advance the narrative graph regardless of which mode you use. To advance the graph at the same time as sending a message, use InvokeTrigger with a named trigger.

Listening to Trigger Invocations

ConvaiNarrativeTriggerInvocation fields:

Field
Type
Description

TriggerName

string

The trigger name that was sent (empty for speech).

TriggerMessage

string

The optional message payload.

Queued

bool

true if the trigger was deferred because the session was not yet open.

Template Keys via Code

Both methods send immediately if the session is open, or queue for the next connection if it is not.

The character-level API and ConvaiNarrativeDesignManager's methods converge on the same transport internally. Use the Manager's methods when you want the keys visible and editable in the Inspector; use the character API for purely code-driven flows where Inspector visibility is not needed.

Fetching Sections and Triggers Programmatically

Via the Character API

NarrativeSectionInfo fields: SectionId, SectionName.

NarrativeTriggerInfo fields: TriggerId, TriggerName, TriggerMessage, DestinationSection.

Via the Static Fetcher

NarrativeDesignFetcher provides the same data without needing a character component reference — useful in Editor tooling or loading screens:

FetchResult<T> fields:

Field
Type
Description

Success

bool

true if the request succeeded.

Data

T

The fetched data. default if Success is false.

Error

string

Error message. null if Success is true.

Resetting State

Reconfiguring ConvaiNarrativeDesignTrigger from Code

All Inspector-configurable settings have corresponding setter methods:

Architecture Overview

Conclusion

IConvaiNarrativeDesign exposes the full Narrative Design surface in code — trigger invocation, speech injection, template key control, async data fetching, and real-time section change events — so you can integrate it into any architecture without being tied to the Inspector components. For complete worked examples composing these APIs into real scenarios, see Usage Examples. For diagnosing problems, see Troubleshooting & Diagnostics.

Last updated

Was this helpful?