> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/narrative-design/scripting-narrative-design.md).

# 叙事设计脚本参考

Inspector 工作流涵盖了大多数使用场景。本页记录了完整的 C# 接口，适用于你需要程序化控制的情况——动态角色切换、运行时异步数据获取、运行时生成的叙事流程，或与自己的游戏系统深度集成。

这里描述的所有功能都可通过 `IConvaiNarrativeDesign`访问，并在每个 `ConvaiCharacter` 的 `NarrativeDesign` 属性上暴露。 `ConvaiNarrativeDesignManager` 和 `ConvaiNarrativeDesignTrigger` 内部都会委托给该接口，因此你在 Inspector 中配置的所有内容也都可以从代码中访问。

### 访问角色 API

每个 `ConvaiCharacter` 公开了一个 `NarrativeDesign` 属性，它返回一个 `IConvaiNarrativeDesign` 实现：

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

#### 属性

| 属性                   | 类型                                    | 描述                                                                                           |
| -------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------- |
| `模板键`                | `IReadOnlyDictionary<string, string>` | 当前为该角色跟踪的所有模板键的快照。                                                                           |
| `CurrentSectionId`   | `string`                              | 最近从后端接收到的章节 ID。如果尚未收到任何章节，则为空字符串。                                                            |
| `CurrentSectionData` | `NarrativeSectionData`                | 完整的章节载荷。包含 `SectionId`, `BehaviorTreeCode`，和 `BehaviorTreeConstants`. `null` ，直到收到第一次章节切换为止。 |

### 监听章节变化

在 `OnEnable` 中订阅，并在 `OnDisable` 中订阅这些事件，以避免组件被禁用或销毁后留下过期监听器。

```csharp
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 和 data.BehaviorTreeConstants
}
```

这些事件通过 SDK 内部的 `EventHub`传递。如果你的处理程序会访问 Unity API（例如 `GameObject.SetActive`），请在场景中使用 `ConvaiNarrativeDesignManager` ——它会自动在主线程上派发。对 `IConvaiNarrativeDesign` 事件的直接订阅可能会根据配置在后台线程上到达。

#### 事件

| 事件                      | 签名                                         | 描述                                    |
| ----------------------- | ------------------------------------------ | ------------------------------------- |
| `OnSectionChanged`      | `Action<string, string>`                   | 在每次章节切换时触发。参数： `previousId`, `newId`. |
| `OnSectionDataReceived` | `Action<NarrativeSectionData>`             | 在每次章节切换时触发，并携带完整载荷。                   |
| `OnTriggerInvoked`      | `Action<ConvaiNarrativeTriggerInvocation>` | 在触发器或语音请求被本地接受后触发（在后端确认之前）。           |

### 从代码中调用触发器

```csharp
// 命名触发器——沿着特定边推进图
bool accepted = character.NarrativeDesign.InvokeTrigger("CheckpointReached");

// 带可选消息载荷的命名触发器
character.NarrativeDesign.InvokeTrigger("ItemInspected", "The fire extinguisher is missing its pin.");
```

`InvokeTrigger` 返回 `false` 如果 `triggerName` 和 `triggerMessage` 和 `true` 都为空，或者触发器在内部被拒绝，则返回 false。否则返回

### 控制角色语音

`InvokeSpeech` ，并在会话尚未打开时将触发器入队。 `<speak>` 标签。

**上下文注入（纯文本）：** 传入纯字符串，让角色知晓一条信息。角色会吸收上下文，并用自己的话作出回应。

```csharp
// 角色会知晓这一事实并自然作出回应
character.NarrativeDesign.InvokeSpeech("The trainee just completed the evacuation drill.");
```

**字面语音（`<speak>` 标签）：** 将消息包裹在 `<speak>` 标签中，使角色逐字逐句说出该文本。

```csharp
// 角色会逐字说出这句话
character.NarrativeDesign.InvokeSpeech("<speak>Attention: the fire exit on level two is now unlocked.</speak>");
```

| 模式                                    | 角色会做什么         |
| ------------------------------------- | -------------- |
| `InvokeSpeech("text")`                | 知晓上下文，并用自己的话回应 |
| `InvokeSpeech("<speak>text</speak>")` | 逐字逐句说出该精确文本    |

{% hint style="info" %}
`InvokeSpeech` 无论你使用哪种模式，它都不会推进叙事图。若要在发送消息的同时推进叙事图，请使用 `InvokeTrigger` 并配合命名触发器。
{% endhint %}

#### 监听触发器调用

```csharp
character.NarrativeDesign.OnTriggerInvoked += invocation =>
{
    Debug.Log($"Trigger: {invocation.TriggerName}, Queued: {invocation.Queued}");
};
```

`ConvaiNarrativeTriggerInvocation` 字段：

| 字段               | 类型       | 描述                       |
| ---------------- | -------- | ------------------------ |
| `TriggerName`    | `string` | 发送的触发器名称（语音时为空）。         |
| `TriggerMessage` | `string` | 可选的消息载荷。                 |
| `Queued`         | `bool`   | `true` 如果触发器因会话尚未打开而被延后。 |

### 通过代码设置模板键

```csharp
// 设置单个键
character.NarrativeDesign.SetTemplateKey("PlayerName", "Alex");

// 设置多个键
character.NarrativeDesign.SetTemplateKeys(new Dictionary<string, string>
{
    { "PlayerName",  "Alex" },
    { "ScoreLevel",  "Intermediate" }
});
```

如果会话已打开，这两种方法会立即发送；如果未打开，则会排队等待下一次连接。

角色级 API 和 `ConvaiNarrativeDesignManager`的这些方法在内部汇聚到同一传输层。當你希望这些键在 Inspector 中可见且可编辑时，请使用 Manager 的方法；当你只需要纯代码驱动的流程、无需 Inspector 可见性时，请使用角色 API。

### 获取章节和触发器

#### 通过角色 API

```csharp
NarrativeFetchResult<List<NarrativeSectionInfo>> result =
    await character.NarrativeDesign.FetchSectionsAsync();

if (result.Success)
{
    foreach (NarrativeSectionInfo section in result.Data)
        Debug.Log($"{section.SectionId}: {section.SectionName}");
}
else
{
    Debug.LogError(result.Error);
}
```

```csharp
NarrativeFetchResult<List<NarrativeTriggerInfo>> result =
    await character.NarrativeDesign.FetchTriggersAsync();

foreach (NarrativeTriggerInfo trigger in result.Data)
    Debug.Log($"{trigger.TriggerName} → {trigger.DestinationSection}");
```

`NarrativeSectionInfo` 字段： `SectionId`, `SectionName`.

`NarrativeTriggerInfo` 字段： `TriggerId`, `TriggerName`, `TriggerMessage`, `DestinationSection`.

#### 通过静态获取器

`NarrativeDesignFetcher` 提供相同的数据，而无需角色组件引用——在编辑器工具或加载界面中很有用：

```csharp
// 获取章节
FetchResult<List<SectionData>> sections =
    await NarrativeDesignFetcher.FetchSectionsAsync(characterId);

// 获取触发器
FetchResult<List<TriggerData>> triggers =
    await NarrativeDesignFetcher.FetchTriggersAsync(characterId);

// 并行获取二者
var (sectionsResult, triggersResult) =
    await NarrativeDesignFetcher.FetchAllAsync(characterId);
```

`FetchResult<T>` 字段：

| 字段      | 类型       | 描述                               |
| ------- | -------- | -------------------------------- |
| `成功`    | `bool`   | `true` 如果请求成功。                   |
| `数据`    | `T`      | 获取到的数据。 `默认值` 如果 `成功` 是 `false`. |
| `Error` | `string` | 错误消息。 `null` 如果 `成功` 是 `true`.   |

### 高级运行时控制

#### 重置控制器状态

```csharp
// 仅重置控制器状态（清除 CurrentSectionID 和 CurrentSectionData）
// 不会影响章节配置列表或 Unity Event 绑定
narrativeManager.ResetController();
```

#### 从代码中重新配置 ConvaiNarrativeDesignTrigger

所有可在 Inspector 中配置的设置，都有对应的 setter 方法：

```csharp
ConvaiNarrativeDesignTrigger trigger = GetComponent<ConvaiNarrativeDesignTrigger>();

// 覆盖触发器选择
trigger.SetTrigger("trigger-uuid", "CheckpointA", "Player reached checkpoint A");

// 在运行时更改激活模式
trigger.SetActivationMode(TriggerActivationMode.Proximity);
trigger.SetProximityRadius(5f);

// 提供一个已知的玩家 Transform（当自动查找不足时很有用）
trigger.SetPlayerTransform(playerController.transform);

// 切换目标角色
trigger.SetCharacter(otherCharacter.GetComponent<IConvaiCharacterAgent>());

// 在关键触发前验证
if (!trigger.ValidateConfiguration())
{
    foreach (string warning in trigger.ValidationWarnings)
        Debug.LogWarning(warning);
}
```

{% hint style="warning" %}
`ClearAllSectionConfigs()` 会移除所有 `UnitySectionEventConfig` 条目以及所有 Unity Event 绑定。这一操作在运行时无法撤销。只有在你已确认要切换到另一个角色且不再需要现有章节事件绑定时才调用它。
{% endhint %}

```csharp
// 永久清除所有章节配置（移除所有 UnitySectionEventConfig 条目）
// 仅在切换到完全不同的角色时使用
narrativeManager.ClearAllSectionConfigs();
```

### 组件关系

```mermaid
classDiagram
    class ConvaiNarrativeDesignManager {
        +UpdateTemplateKey(key, value)
        +FetchAndSyncFromBackend()
        +OnAnySectionChanged UnityEvent
    }
    class ConvaiNarrativeDesignTrigger {
        +InvokeTrigger() bool
        +ResetTrigger()
        +ValidateConfiguration() bool
    }
    class IConvaiNarrativeDesign {
        +SetTemplateKey(key, value) bool
        +InvokeTrigger(name, msg) bool
        +InvokeSpeech(msg) bool
        +FetchSectionsAsync() Task
        +OnSectionChanged Action
    }
    class CharacterNarrativeDesignFacade {
        -_templateKeys Dictionary
        -_pendingTriggers Queue
        +FlushPending()
    }
    class ConnectionService {
        +UpdateTemplateKeys(keys)
        +SendTrigger(name, msg)
    }

    ConvaiNarrativeDesignManager ..> IConvaiNarrativeDesign : 委托给
    ConvaiNarrativeDesignTrigger ..> IConvaiNarrativeDesign : 调用 InvokeTrigger
    IConvaiNarrativeDesign <|.. CharacterNarrativeDesignFacade
    CharacterNarrativeDesignFacade --> ConnectionService : 通过 RTVI 发送
```

`ConvaiNarrativeDesignManager` 和 `ConvaiNarrativeDesignTrigger` 二者都委托给 `IConvaiNarrativeDesign`。 `CharacterNarrativeDesignFacade` 实现该接口并管理待处理队列； `ConnectionService` 负责实际的 RTVI 传输。

### 下一步

{% content-ref url="/pages/5a4803abb898897932fbc643bd96c9e58ef4d96c" %}
[叙事设计使用示例](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/narrative-design/usage-examples.md)
{% endcontent-ref %}

{% content-ref url="/pages/bd435d6564aabc01fda5fe6b7c130cf0ffeb1c36" %}
[排查叙事设计问题](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/narrative-design/troubleshooting-and-diagnostics.md)
{% endcontent-ref %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/narrative-design/scripting-narrative-design.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
