> 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/emotion/scripting-api.md).

# 情绪脚本 API

Emotion 系统在运行时提供两条不同的路径来响应和控制情绪状态。The **检视器路径** 使用 `ConvaiCharacterEventRelay` —— 一个将情绪变化回调作为标准 Unity Event 暴露出来的组件，无需编写代码。The **脚本路径** 使用 `ConvaiEmotionController` 直接使用，提供完整的 C# API，用于读取实时状态、注入覆盖以及锁定表情。两条路径可以同时使用。

### 检视器路径 — ConvaiCharacterEventRelay

`ConvaiCharacterEventRelay` 是一个 MonoBehaviour，它将角色回调桥接到 Unity Event，允许设计师完全在检视器中配置情绪反应，无需编写任何代码。

**添加组件：** **Convai → Events → Convai Character Event Relay**

将其放置在场景中的任意 GameObject 上。它会自动找到 `ConvaiCharacter` ，位于同一个 GameObject 上，或者你也可以通过 **Character** 字段从另一个 GameObject 分配角色。

#### 检视器字段

| 字段          | 默认     | 描述                                                      |
| ----------- | ------ | ------------------------------------------------------- |
| `Character` | *（无）*  | 对一个 `ConvaiCharacter`的可选显式引用。留空则使用自动解析。                 |
| `自动解析角色`    | `true` | 启用后，继电器会自动找到一个 `ConvaiCharacter` 位于同一个 GameObject 上的对象。 |

#### OnEmotionChanged 事件

该继电器公开一个 **On Emotion Changed** Unity Event，它会在角色的情绪信号发生变化时触发。该事件会传递一个 `CharacterEmotionRelayData` 载荷：

| 属性              | 类型       | 描述                             |
| --------------- | -------- | ------------------------------ |
| `CharacterId`   | `string` | 角色的唯一标识符。                      |
| `CharacterName` | `string` | 角色的显示名称（若无则回退到 GameObject 名称）。 |
| `Emotion`       | `string` | 原始服务器标签（例如 `"happy"`).         |
| `强度`            | `int`    | 后端发送的 1–3 整数刻度。                |

**示例连线：** 添加一个 `ConvaiCharacterEventRelay` 到你的 NPC 的 GameObject。 在 **On Emotion Changed** 列表中，单击 **+**，将一个 UI Text 组件拖入对象字段，并选择 `Text.text` —— 标签将在每次情绪变化时自动更新。

{% hint style="info" %}
`ConvaiCharacterEventRelay` 在进行分类法解析或平滑处理之前，会在原始服务器标签上触发。可用于 UI 显示、音频提示或简单的分支逻辑。对于带有分数和保持时间的平滑、解析后状态，请改用 `ConvaiEmotionController.Current` ，从脚本中访问。
{% endhint %}

### 从脚本访问控制器

该 `ConvaiEmotionController` 组件可以通过其具体类型获取，也可以通过 `IEmotionStateSource` 接口获取。当你希望将代码与特定组件实现解耦时，请使用接口：

```csharp
using Convai.Modules.Emotion.Components;
using Convai.Domain.Embodiment.Interfaces;

// 具体类型——完整 API 访问，包括覆盖和锁定
var controller = npcGameObject.GetComponent<ConvaiEmotionController>();

// 接口——对当前情绪读取的解耦只读访问
var source = npcGameObject.GetComponent<IEmotionStateSource>();
EmotionReading reading = source.Current;
```

### 读取当前情绪状态

`ConvaiEmotionController.Current` 返回一个 `EmotionReading` —— 每帧根据累加器输出重建的不可变快照。可在 `Update` 中轮询，或通过任意事件对其做出反应。

```csharp
using Convai.Domain.Embodiment.Readings;
using Convai.Modules.Emotion.Components;
using UnityEngine;

public sealed class EmotionLogger : MonoBehaviour
{
    [SerializeField] private ConvaiEmotionController emotionController;

    private void Update()
    {
        EmotionReading reading = emotionController.Current;

        if (!reading.IsNeutral)
            Debug.Log($"主导情绪：{reading.DominantLabel} ({reading.DominantScore:F2})");
    }
}
```

#### EmotionReading 属性和方法

| 成员                                                     | 类型                                   | 描述                                                |
| ------------------------------------------------------ | ------------------------------------ | ------------------------------------------------- |
| `DominantLabel`                                        | `string`                             | 得分最高情绪的规范化标签（例如 `"joy"`, `"anger"`).              |
| `DominantScore`                                        | `float`                              | 平滑和突发处理后，主导情绪的归一化分数 \[0–1]。                       |
| `AllScores`                                            | `IReadOnlyDictionary<string, float>` | 按规范化标签键控的完整分数表。分类法中的每种情绪都有一个条目。本帧没有贡献的情绪得分为 `0`.  |
| `嘴部影响`                                                 | `float`                              | 在非说话帧期间被 LipSync 合成器用于混合口型的 \[0–1] 提示值。           |
| `主导标签已持续保持的实际秒数。`                                      | `float`                              | 当前主导标签连续保持的实时时钟秒数。                                |
| `IsNeutral`                                            | `bool`                               | `true` 当主导标签为 `"neutral"` 或当 `DominantScore ≤ 0`. |
| `NeutralLabel`                                         | `const string`                       | 字符串常量 `"neutral"`.                                |
| `GetScore(string canonicalLabel)`                      | `float`                              | 返回给定规范化标签的平滑分数，若不存在则返回 `0` 。                      |
| `CopyScoresTo(IDictionary<string, float> destination)` | `void`                               | 将完整分数表复制到调用方拥有的字典中。复制前会清空目标。                      |

### 作者期锁定

控制器有三个序列化字段，可直接在检视器中将表情固定为某种特定情绪——这在制作和调试时很有用，或者可在不进入 Play Mode 的情况下，在 Scene 视图中预览 blendshape 轨道结果。

| 字段                   | 类型       | 默认          | 描述                                 |
| -------------------- | -------- | ----------- | ---------------------------------- |
| `lockEmotion`        | `bool`   | `false`     | 启用后，所有传入的服务器情绪事件都会被忽略，角色会保持锁定的情绪。  |
| `lockedEmotionLabel` | `string` | `"neutral"` | 在 `lockEmotion` 处于激活状态时保持的规范化分类标签。 |
| `lockedIntensity`    | `float`  | `1.0`       | 锁定情绪的强度 \[0–1]。                    |

{% hint style="danger" %}
`lockEmotion` 是一个 **序列化字段** —— 它的值会随场景或预制体一起保存。如果你保持启用却忘记重置，角色将在生产构建中悄无声息地忽略所有实时情绪信号，不会有运行时错误或警告。构建前务必将其禁用。
{% endhint %}

{% hint style="info" %}
`ConvaiEmotionController` 继承自 `[ExecuteAlways]` 其基类。设置 `lockEmotion = true` 在检视器中会立即更新 Scene 视图里的 blendshape，而无需进入 Play Mode。这使它成为验证插槽映射是否在角色网格上产生正确视觉结果的实用工具。
{% endhint %}

### SetEmotionOverride 和 ClearEmotionOverride

`SetEmotionOverride` 会在服务器发送内容之上向累加器注入额外分数。该覆盖仍然会经过平滑处理——它会以 `lerpSpeed`的速度混入，而不是瞬间生效。当你的应用逻辑需要响应场景内事件来增强或引导情绪时，请使用它。

```csharp
using Convai.Modules.Emotion.Components;
using UnityEngine;

public sealed class HazardZoneTrigger : MonoBehaviour
{
    [SerializeField] private ConvaiEmotionController emotionController;

    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Trainee"))
            emotionController.SetEmotionOverride("fear", 0.9f);
    }

    private void OnTriggerExit(Collider other)
    {
        if (other.CompareTag("Trainee"))
            emotionController.ClearEmotionOverride();
    }
}
```

`ClearEmotionOverride` 会移除覆盖并将累加器恢复为由服务器驱动的状态。返回过程是平滑的。

### LockEmotion 和 UnlockEmotion

`LockEmotion` 会完全绕过累加器，将角色立即切换到某个特定表情并保持在那里，无论服务器发送什么都不受影响。当你需要在脚本化序列中获得可保证、稳定的表情时，请使用它。

```csharp
using Convai.Modules.Emotion.Components;
using UnityEngine;

public sealed class WelcomeSequenceController : MonoBehaviour
{
    [SerializeField] private ConvaiEmotionController emotionController;

    public void BeginWelcome()
    {
        emotionController.LockEmotion("joy", 0.75f);
    }

    public void EndWelcome()
    {
        emotionController.UnlockEmotion();
    }
}
```

`UnlockEmotion` 会释放锁定。累加器将从中性状态恢复，并再次开始响应服务器事件。

**API 签名：**

```csharp
void LockEmotion(string label, float intensity = 1f);
void UnlockEmotion();
void SetEmotionOverride(string label, float score);
void ClearEmotionOverride();
```

### 订阅情绪变化事件

若要对后端发送的每一种新情绪作出反应——用于日志记录、分析或自适应场景逻辑——请订阅 `OnCharacterEmotionChanged` 在 `ConvaiManager.Events`上。这是一个标准的 C# 事件；在 `OnEnable` 中订阅，并在 `OnDisable`.

```csharp
using Convai.Domain.DomainEvents.Runtime;
using Convai.Runtime.Components;
using UnityEngine;

public sealed class EmotionEventListener : MonoBehaviour
{
    [SerializeField] private ConvaiManager convaiManager;

    private void OnEnable()
    {
        convaiManager.Events.OnCharacterEmotionChanged += HandleEmotionChanged;
    }

    private void OnDisable()
    {
        convaiManager.Events.OnCharacterEmotionChanged -= HandleEmotionChanged;
    }

    private void HandleEmotionChanged(CharacterEmotionChanged e)
    {
        Debug.Log($"[{e.CharacterId}] {e.Emotion} — 强度 {e.Intensity} ({e.NormalizedIntensity:F2})");
    }
}
```

#### CharacterEmotionChanged 属性

| 属性                    | 类型         | 描述                                             |
| --------------------- | ---------- | ---------------------------------------------- |
| `CharacterId`         | `string`   | 情绪发生变化的角色的唯一标识符。                               |
| `Emotion`             | `string`   | 原始服务器标签（例如 `"happy"`，而不是规范化的 `"joy"`).         |
| `强度`                  | `int`      | 后端发送的 1–3 整数刻度（已截断）。                           |
| `NormalizedIntensity` | `float`    | `(Intensity - 1) / 2f` —— 将 1–3 范围映射到 \[0, 1]。 |
| `IsNeutral`           | `bool`     | `true` 如果情绪字符串为 `"neutral"`.                   |
| `IsHighIntensity`     | `bool`     | `true` 如果 `Intensity >= 3`.                    |
| `IsLowIntensity`      | `bool`     | `true` 如果 `Intensity <= 1`.                    |
| `Timestamp`           | `DateTime` | 事件创建时的 UTC 时间戳。                                |

{% hint style="warning" %}
`CharacterEmotionChanged.Emotion` 包含 **原始服务器标签** （例如 `"happy"`），而不是规范化的分类法标签（`"joy"`）。如果你需要规范化标签——例如，要在 `AllScores` 中查找分数——请通过分类法解析它： `taxonomy.TryResolve(e.Emotion, out EmotionDescriptor descriptor)`.
{% endhint %}

### IEmotionStateSource 接口

仅需要读取情绪状态、而不需要控制覆盖或锁定的代码，应依赖于 `IEmotionStateSource` ，而不是具体的 `ConvaiEmotionController`。这可以保持依赖最小，并使消费类更容易测试。

```csharp
using Convai.Domain.Embodiment.Interfaces;
using Convai.Domain.Embodiment.Readings;
using UnityEngine;

public sealed class EmotionDrivenUI : MonoBehaviour
{
    // 在检视器中分配一个 ConvaiEmotionController——它实现了 IEmotionStateSource
    [SerializeField] private MonoBehaviour emotionSource;

    private IEmotionStateSource _source;

    private void Awake()
    {
        _source = emotionSource as IEmotionStateSource;
    }

    private void Update()
    {
        EmotionReading reading = _source.Current;
        // 根据 reading.DominantLabel 和 reading.DominantScore 更新 UI
    }
}
```

### 下一步

有关将配置文件配置与这些 API 调用结合起来的完整示例，请参阅 [情绪示例](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/emotion/usage-examples.md)。如果运行时表现不符合预期，请参阅 [排查情绪问题](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/emotion/troubleshooting-and-diagnostics.md).

{% content-ref url="/pages/6c46f45b25431c91dffb50ae4e5dd2ea5cc7ef50" %}
[情绪示例](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/emotion/usage-examples.md)
{% endcontent-ref %}

{% content-ref url="/pages/65ec9decd2473b72bc06bf3c946f3008c8528dda" %}
[排查情绪问题](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/emotion/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/emotion/scripting-api.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.
