> 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/usage-examples.md).

# 叙事设计使用示例

以下示例展示了如何组合 `ConvaiNarrativeDesignManager`, `ConvaiNarrativeDesignTrigger`，和 `IConvaiNarrativeDesign` 成完整、可运行的设置。它们按从简单到高级排序，并涵盖不同领域，以展示 Narrative Design 支持的广度。每个示例都是独立的——从与你当前复杂度级别最匹配的那个开始。

### 示例 1：脚本化欢迎序列

**复杂度：** 初级 | **激活模式：** 手动 | **使用的功能：** 管理器、触发器（手动）、一个模板键

**场景：** 一位访客来到接待台。UI 中的“Start”按钮通过发送一个单独的触发器来启动体验，使角色从空闲状态进入主动欢迎部分。

#### 设置

{% stepper %}
{% step %}

#### 准备场景

添加 `ConvaiNarrativeDesignManager` 到角色 GameObject，并从仪表板同步各章节。你至少需要两个章节：一个空闲章节（角色在此等待）和一个欢迎章节（角色在此开始体验）。
{% endstep %}

{% step %}

#### 在会话开始前设置访客姓名

在开始会话之前，发送一个模板键，以便角色可以按姓名称呼访客：

```csharp
public class ReceptionController : MonoBehaviour
{
    [SerializeField] private ConvaiNarrativeDesignManager _narrativeManager;
    [SerializeField] private string _visitorName;

    private void Start()
    {
        _narrativeManager.UpdateAndSendTemplateKey("VisitorName", _visitorName);
    }
}
```

{% endstep %}

{% step %}

#### 添加一个手动触发器

添加 `ConvaiNarrativeDesignTrigger` 到任意 GameObject（它不会出现在世界中——由 UI 驱动）。设置 **激活模式** 设为 **手动** 并从仪表板中获取/选择欢迎触发器。
{% endstep %}

{% step %}

#### 连接 UI 按钮

在 Button 组件的 **On Click ()** 事件中，指定 `ConvaiNarrativeDesignTrigger` 并选择 `ConvaiNarrativeDesignTrigger.InvokeTrigger`.
{% endstep %}

{% step %}

#### 连接章节事件

在管理器的 **叙事章节** 列表中，找到欢迎章节条目并添加一个 `OnSectionStart` 监听器。将其指向欢迎开始时场景中应发生变化的内容——例如，启用姓名牌 UI 或启动环境动画。
{% endstep %}
{% endstepper %}

**运行时会发生什么：** 玩家点击 Start → `InvokeTrigger()` 将触发器发送到后端 → 图表移动到欢迎章节 → `OnSectionStart` 在欢迎章节入口处触发 → 角色开始欢迎。

### 示例 2：分支对话

**复杂度：** 中级 | **激活模式：** 手动（代码驱动） | **使用的功能：** 管理器、 `IConvaiNarrativeDesign`, `InvokeSpeech`、多个模板键

**场景：** 一个导览助手可以引导用户浏览三个独立的主题区域（设施、系统访问、政策）。主题选择由 UI 按钮驱动，用户可以在主题之间自由切换。每个主题之后都支持开放式后续提问。

#### 设置

在管理器中同步所有主题章节。无需 `ConvaiNarrativeDesignTrigger` 组件——触发器直接通过 `IConvaiNarrativeDesign`.

```csharp
public class OrientationController : MonoBehaviour
{
    [SerializeField] private ConvaiCharacter _character;
    [SerializeField] private ConvaiNarrativeDesignManager _narrativeManager;
    [SerializeField] private TextMeshProUGUI _activeTopicLabel;

    private void OnEnable()
    {
        _character.NarrativeDesign.OnSectionChanged += OnSectionChanged;
    }

    private void OnDisable()
    {
        _character.NarrativeDesign.OnSectionChanged -= OnSectionChanged;
    }

    // 由 UI 按钮调用
    public void SelectTopic(string triggerName)
    {
        _character.NarrativeDesign.InvokeTrigger(triggerName);
    }

    // 由自由文本输入框的提交事件调用——纯文本上下文注入
    // 角色会用自己的话自然回应
    public void AskFollowUp(string userQuestion)
    {
        _character.NarrativeDesign.InvokeSpeech(userQuestion);
    }

    // 当需要脚本化公告时调用——角色会逐字说出这段文本
    public void AnnounceToUser(string announcement)
    {
        _character.NarrativeDesign.InvokeSpeech($"<speak>{announcement}</speak>");
    }

    private void OnSectionChanged(string previous, string next)
    {
        // 更新面包屑 UI——从管理器中查找可读名称
        if (_narrativeManager.FindSectionConfig(next) is { } cfg)
            _activeTopicLabel.text = cfg.SectionName;
    }
}
```

在 Inspector 中为按钮分配触发器名称： `"TopicFacilities"`, `"TopicSystemsAccess"`, `"TopicPolicies"`.

将 `所有触发器上的 TriggerOnce = false` 设置为 false，以便用户可以重新访问任意主题。会话打开前从表单发送模板键（`UserName`, `Department`）使用 `UpdateTemplateKeys`.

`InvokeSpeech` 有两种模式：纯文本会让角色用自己的话自然回应（适用于自由文本后续提问）；将消息包裹在 `<speak>` 标签中会让角色逐字说出该文本（适用于脚本化公告或精确提示）。这两种模式都不会推进图表。请参见 [控制角色语音](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/narrative-design/scripting-narrative-design.md#control-character-speech) 以获取完整参考。

### 示例 3：基于接近触发的展厅导览

**复杂度：** 中级 | **激活模式：** 接近 | **使用的功能：** 管理器、触发器（接近）、区域事件、批量模板键

**场景：** 一个产品展厅有五个展示站点。访客走近每个站点时，主持角色就开始讲解该产品。每个站点都是独立的，可以按任意顺序参观。

#### 设置

为每个站点创建一个 `ConvaiNarrativeDesignTrigger` 。对于每个站点：

* **激活模式：** `接近`
* **接近半径：** 根据站点大小调整（在 Scene 视图中显示为绿色球体 gizmo）
* **仅触发一次：** `true`
* **场景加载时重置：** `true` （这样每次参观时导览都会重置）

通过模板键连接站点特定上下文。从一个 `ScriptableObject` 在 `Start()`:

```csharp
[CreateAssetMenu(menuName = "Showroom/Station Data")]
public class StationData : ScriptableObject
{
    public string ProductName;
    public string LaunchYear;
    public string KeyFeature;
}

public class StationController : MonoBehaviour
{
    [SerializeField] private ConvaiNarrativeDesignManager _narrativeManager;
    [SerializeField] private StationData _data;

    private void Start()
    {
        _narrativeManager.UpdateTemplateKeys(new Dictionary<string, string>
        {
            { "ProductName", _data.ProductName },
            { "LaunchYear",  _data.LaunchYear  },
            { "KeyFeature",  _data.KeyFeature  }
        });
    }
}
```

使用 `OnPlayerEnterZone` 用于高亮产品模型（例如启用轮廓着色器）。使用 `OnPlayerExitZone` 来移除高亮。

{% hint style="warning" %}
如果站点接近半径重叠，两个触发器可能会在同一帧触发，从而在后端响应第一个之前发送两次图表转换。请让站点之间保持足够间距，使接近区域不相交，或者使用 Collision 模式并让触发器碰撞器在物理上分离。
{% endhint %}

### 示例 4：带动态反馈的自适应场景

**复杂度：** 高级 | **激活模式：** 代码驱动 | **使用的功能：** 管理器、 `IConvaiNarrativeDesign`, `OnSectionDataReceived`, `BehaviorTreeConstants`、动态模板键、重做流程

**场景：** 一个技术技能评估器运行一个多步骤场景。每完成一步，学习者的表现都会被评分。角色的引导程度和场景的难度会根据当前累计得分动态调整。该场景可以重新尝试，并重置所有状态。

#### 会话生命周期

```mermaid
flowchart TD
    A[会话开始] --> B["UpdateAndSendTemplateKey\n(DifficultyLevel, 基础)"]
    B --> C[角色进入对话\n→ FlushPending → 键已发送]
    C --> D[场景开始\n第一章节激活]
    D --> E[学习者完成步骤\n→ 由 ScenarioController 评分]
    E --> F["UpdateAndSendTemplateKey\n(DifficultyLevel, score > 80 ? 高级 : 基础)"]
    F --> G[InvokeTrigger StepCompleted]
    G --> H[章节变更\n→ OnSectionDataReceived]
    H --> I[反序列化 BehaviorTreeConstants\n应用场景参数]
    I --> E
    E --> J{最后一步？}
    J -- 是 --> K[OnSectionStart 评估完成\n→ 显示分数摘要]
    K --> L{重新尝试？}
    L -- 是 --> M[ResetController + FetchAndSyncFromBackend\n→ 重新开始]
    L -- 否 --> N[结束会话]
```

#### 实现

```csharp
public class ScenarioController : MonoBehaviour
{
    [SerializeField] private ConvaiNarrativeDesignManager _narrativeManager;
    [SerializeField] private ConvaiCharacter _character;

    private int _totalScore;
    private int _stepCount;

    private void OnEnable()
    {
        _character.NarrativeDesign.OnSectionDataReceived += OnSectionDataReceived;
    }

    private void OnDisable()
    {
        _character.NarrativeDesign.OnSectionDataReceived -= OnSectionDataReceived;
    }

    public void StartScenario(string learnerName)
    {
        _totalScore = 0;
        _stepCount  = 0;
        _narrativeManager.UpdateTemplateKeys(new Dictionary<string, string>
        {
            { "LearnerName",    learnerName   },
            { "DifficultyLevel", "基础" }
        });
        _narrativeManager.SendTemplateKeysUpdate();
    }

    public void OnStepCompleted(int stepScore)
    {
        _totalScore += stepScore;
        _stepCount++;

        string level = (_totalScore / _stepCount) > 80 ? "高级" : "基础";
        _narrativeManager.UpdateAndSendTemplateKey("DifficultyLevel", level);
        _character.NarrativeDesign.InvokeTrigger("StepCompleted");
    }

    private void OnSectionDataReceived(NarrativeSectionData data)
    {
        if (string.IsNullOrEmpty(data.BehaviorTreeConstants)) return;

        // BehaviorTreeConstants 是在 Convai 仪表板中编写的 JSON 字符串
        // 它包含此章节的场景特定参数
        var constants = JsonUtility.FromJson<ScenarioConstants>(data.BehaviorTreeConstants);
        ApplyScenarioParameters(constants);
    }

    public async void RetakeScenario()
    {
        _narrativeManager.ResetController();
        await _narrativeManager.FetchAndSyncFromBackendAsync();
        StartScenario("LearnerName");
    }

    private void ApplyScenarioParameters(ScenarioConstants constants)
    {
        // 将仪表板中编写的常量值应用到你的场景
    }

    [Serializable]
    private class ScenarioConstants
    {
        public float TimeLimit;
        public int   RequiredScore;
        public bool  ShowHints;
    }
}
```

`BehaviorTreeConstants` 是你在 Convai 仪表板中按章节编写的 JSON 字符串。它携带你希望从图表注入到 Unity 的任何数据——时间限制、评分阈值、提示标志或其他场景参数。SDK 会在每个 `NarrativeSectionData` 载荷中传递它。

{% hint style="info" %}
`BehaviorTreeCode` 和 `BehaviorTreeConstants` 在 SDK 侧为只读。它们携带在 Convai 仪表板中编写的数据，无法在 Unity 运行时修改。
{% endhint %}

### 下一步

{% content-ref url="/pages/e2aeb655628e9664d3a8c2993eb219a88fcfeef0" %}
[叙事设计脚本参考](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/narrative-design/scripting-narrative-design.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/usage-examples.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.
