> 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).

# 叙事设计使用示例

四个完整的 Narrative Design 示例，从单触发欢迎序列到带动态反馈的自适应多区段场景。

以下示例展示如何将 `ConvaiNarrativeDesignManager`, `ConvaiNarrativeDesignTrigger`，以及 `IConvaiNarrativeDesign` 组合成完整可运行的配置。它们按从简单到高级排序，并覆盖不同领域，以展示 Narrative Design 所支持的广度。每个示例都是自包含的——从最符合你当前复杂度水平的那个开始。

### 示例 1：脚本化欢迎流程

**复杂度：** 初级 | **激活模式：** 手动 | **使用的功能：** Manager、Trigger（手动）、一个模板键

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

#### 设置

{% stepper %}
{% step %}

#### 准备场景

添加 `ConvaiNarrativeDesignManager` 到角色 GameObject，并从仪表板同步各个 section。你至少需要两个 section：一个空闲 section（角色在此等待）和一个欢迎 section（角色在此开始体验）。
{% 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 驱动）。设置 **激活模式** 到 **Manual** 并从仪表板获取/选择欢迎触发器。
{% endstep %}

{% step %}

#### 将 UI 按钮连接起来

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

{% step %}

#### 连接 section 事件

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

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

### 示例 2：分支对话

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

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

#### 设置

在 Manager 中同步所有主题 section。无需 `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——从 Manager 中查找可读名称
        if (_narrativeManager.FindSectionConfig(next) is { } cfg)
            _activeTopicLabel.text = cfg.SectionName;
    }
}
```

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

将 `所有触发器都将 TriggerOnce = 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：近距离触发的展品导览

**复杂度：** 中级 | **激活模式：** 近距离 | **使用的功能：** Manager、Trigger（近距离）、区域事件、批量模板键

**场景：** 一个产品展厅有五个展示站点。访客走向每个站点时，一位主持角色会开始讲解该产品。每个站点彼此独立，并且可以按任意顺序参观。

#### 设置

为每种警报类型创建一个 `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：带动态反馈的自适应场景

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

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

#### 会话生命周期

```mermaid
flowchart TD
    A[会话开始] --> B["UpdateAndSendTemplateKey\n(DifficultyLevel, Foundation)"]
    B --> C[角色进入对话\n→ FlushPending → keys sent]
    C --> D[场景开始\n第一个 section 激活]
    D --> E[学习者完成步骤\n→ 由 ScenarioController 评分]
    E --> F["UpdateAndSendTemplateKey\n(DifficultyLevel, score > 80 ? Advanced : Foundation)"]
    F --> G[InvokeTrigger StepCompleted]
    G --> H[section 变化\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", "Foundation" }
        });
        _narrativeManager.SendTemplateKeysUpdate();
    }

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

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

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

        // BehaviorTreeConstants 是你在 Convai 仪表板中编写的 JSON 字符串
        // 包含此 section 的场景特定参数
        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` 是你按 section 在 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, and the optional `goal` 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>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
