> 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/unity-plugin-beta-overview/features/narrative-design/usage-examples.md).

# 使用示例

## 构建完整的 Narrative Design 配置

以下示例展示如何组合 `ConvaiNarrativeDesignManager`, `ConvaiNarrativeDesignTrigger`，以及 `IConvaiNarrativeDesign` 组成完整、可运行的配置。它们按从简单到高级排序，并覆盖不同领域，以展示 Narrative Design 所支持的广度——从单按钮欢迎终端到完全自适应的多步骤场景。

每个示例都是独立的。请从最符合你当前复杂度的示例开始。如果某一步不熟悉，相关详情页已在文中链接。

***

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

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

**场景：** 一位访客来到接待台。界面中的“开始”按钮通过发送一个单一触发器启动体验，使角色从待机状态切换到活动欢迎环节。

### 设置

{% stepper %}
{% step %}
**准备场景**

Add `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 %}
**添加一个手动触发器**

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

{% step %}
**连接 UI 按钮**

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

{% step %}
**连接区段事件**

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

**运行时会发生什么：** 玩家点击开始 → `InvokeTrigger()` 将触发器发送到后端 → 图表切换到欢迎区段 → `OnSectionStart` 在进入欢迎区段时触发 → 角色开始欢迎。

***

## 示例 2 — 中级：分支对话

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

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

### 设置

在 Manager 中同步所有主题区段。不 `ConvaiNarrativeDesignTrigger` 需要组件——触发器直接通过 `IConvaiNarrativeDesign`.

```csharp
public class OrientationController : MonoBehaviour
{
    [SerializeField] private ConvaiCharacter _character;
    [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` 在所有触发器上设置为 false，这样用户就可以重新访问任意主题。发送模板键（`UserName`, `Department`）在会话开始前通过表单发送，使用 `UpdateTemplateKeys`.

**`InvokeSpeech` 有两种模式：** 纯文本会让角色用自己的话自然回应（适合自由文本追问）；将消息包裹在 `<speak>` 标签中会让角色逐字说出该文本（适合脚本化公告或精确提示）。这两种模式都不会推进图表。完整参考请参见“控制角色说什么”。

***

## 示例 3 — 中级：邻近触发的展品导览

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

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

### 设置

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

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

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

```csharp
[CreateAssetMenu(menuName = "展厅/站点数据")]
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" %}
如果站点的邻近半径重叠，两个触发器可能会在同一帧触发，在后端响应第一个之前就发送两次图表切换。请将站点间距拉开，确保邻近区域不相交，或者使用碰撞模式并让触发碰撞体物理分离。
{% endhint %}

***

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

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

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

### 会话生命周期

```mermaid
flowchart TD
    A[会话开始] --> B["UpdateAndSendTemplateKey\n(DifficultyLevel, Foundation)"]
    B --> C[角色进入对话\n→ FlushPending → 键已发送]
    C --> D[场景开始\n第一个区段激活]
    D --> E[学习者完成步骤\n→ 由 ScenarioController 评分]
    E --> F["UpdateAndSendTemplateKey\n(DifficultyLevel, score > 80 ? Advanced : Foundation)"]
    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", "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 字符串
        // 包含该区段的场景特定参数
        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 %}

***

## 结论

这四个示例涵盖了 Narrative Design 用例的完整范围——从完全在 Inspector 中连接的单触发欢迎流程，到一个角色行为会根据表现数据动态变化的完全自适应场景。每种模式都建立在相同的三个组件之上：Manager、Trigger，以及 `IConvaiNarrativeDesign` API。若要帮助诊断这些配置中的问题，请参见“故障排除与诊断”。


---

# 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/unity-plugin-beta-overview/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.
