> 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/scripting-reference/character-events.md).

# 角色事件

角色级事件参考，涵盖语音、情绪、转录、轮次变化和动作，可通过 Inspector 中继或脚本连接。

角色事件让你可以根据 AI 角色说了什么、感受到了什么以及做了什么，来驱动 UI、动画、游戏玩法和评估逻辑。SDK 提供了两个 Inspector 中继组件用于无代码接线，以及一个带类型的 C# 事件中心用于脚本化响应。两种方式观察的是同一底层事件。

{% hint style="info" %}
**中继 vs. C# 订阅：** 中继组件会触发一组精选事件。对于 `OnRemoteAudioEnabledChanged` 和 `OnSessionStateChanged` （按角色区分），请在 C# 中直接订阅 `ConvaiCharacter` ——这些不会在中继上暴露。
{% endhint %}

***

### `ConvaiCharacterEventRelay`

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

放在与 `ConvaiCharacter`相同的 GameObject 上，或者放在任何具有 **Auto Resolve Character** 已启用的 GameObject 上，以获取 `ConvaiCharacter` 在运行时同一对象上的组件。

{% tabs %}
{% tab title="Inspector" %}
添加该组件，并在 Inspector 中将回调连接到六个 UnityEvent 中的任意一个。启用 **Auto Resolve Character** 或分配 `ConvaiCharacter` 字段显式设置。
{% endtab %}

{% tab title="脚本" %}

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

public class CharacterReactionHandler : MonoBehaviour
{
    private void OnEnable()
    {
        var events = ConvaiManager.ActiveManager?.Events;
        if (events == null) return;

        events.OnCharacterSpeechStateChanged += HandleSpeech;
        events.OnCharacterEmotionChanged     += HandleEmotion;
    }

    private void OnDisable()
    {
        var events = ConvaiManager.ActiveManager?.Events;
        if (events == null) return;

        events.OnCharacterSpeechStateChanged -= HandleSpeech;
        events.OnCharacterEmotionChanged     -= HandleEmotion;
    }

    private void HandleSpeech(CharacterSpeechStateChanged e)
    {
        if (e.IsStartOfSpeech) Debug.Log($"{e.CharacterId} started speaking.");
        if (e.IsEndOfSpeech)   Debug.Log($"{e.CharacterId} stopped speaking.");
    }

    private void HandleEmotion(CharacterEmotionChanged e) =>
        Debug.Log($"{e.CharacterId}: {e.Emotion} ({e.NormalizedIntensity:P0})");
}
```

{% endtab %}
{% endtabs %}

#### 事件

| 事件                     | 参数                                | 触发时机            |
| ---------------------- | --------------------------------- | --------------- |
| `OnTranscriptReceived` | `CharacterTranscriptRelayData`    | 转录文本到达（中间稿或最终稿） |
| `OnSpeechStarted`      | —                                 | 角色开始说话          |
| `OnSpeechStopped`      | —                                 | 角色停止说话          |
| `OnTurnCompleted`      | `CharacterTurnCompletedRelayData` | 角色的完整对话回合结束     |
| `OnCharacterReady`     | —                                 | 角色已初始化并可进行对话    |
| `OnEmotionChanged`     | `CharacterEmotionRelayData`       | 角色检测到的情绪发生变化    |

#### `CharacterTranscriptRelayData` 字段

| 字段              | 类型    | 说明                  |
| --------------- | ----- | ------------------- |
| `角色 ID`         | `字符串` | 角色的标识符              |
| `CharacterName` | `字符串` | 角色的显示名称             |
| `文本`            | `字符串` | 当前转录文本（可能是中间稿）      |
| `IsFinal`       | `布尔值` | 当此语句不会再有后续更新时为 True |

#### `CharacterEmotionRelayData` 字段

| 字段              | 类型    | 说明                                   |
| --------------- | ----- | ------------------------------------ |
| `角色 ID`         | `字符串` | 角色的标识符                               |
| `CharacterName` | `字符串` | 角色的显示名称                              |
| `情绪`            | `字符串` | 情绪标签，例如 `“Joy”`, `“Sadness”`         |
| `强度`            | `整数`  | 原始强度值；范围 **1–3** （1 = 低，2 = 中，3 = 高） |

中继暴露原始 `强度` 整数（1–3）。若要归一化到 0.0–1.0，请计算 `(Intensity - 1) / 2f`。对于归一化值和布尔辅助属性（`IsNeutral`, `IsHighIntensity`），请订阅 `ConvaiEvents.OnCharacterEmotionChanged` 在 C# 中——域事件载荷包含这些字段。

#### `CharacterTurnCompletedRelayData` 字段

| 字段               | 类型    | 说明                   |
| ---------------- | ----- | -------------------- |
| `角色 ID`          | `字符串` | 角色的标识符               |
| `CharacterName`  | `字符串` | 角色的显示名称              |
| `WasInterrupted` | `布尔值` | 当回合因玩家打断角色而结束时为 True |

***

### `ConvaiTranscriptEventRelay`

**添加组件路径：** Convai → Events → Convai Transcript Event Relay

当你需要同时响应角色和玩家的转录流时，请使用此中继——例如字幕显示、自定义聊天 UI 或转录记录。

#### 过滤属性

| 属性                     | 默认   | 说明                                |
| ---------------------- | ---- | --------------------------------- |
| `FinalOnly`            | `否`  | 当 `是`，只有最终转录会到达回调。非最终更新会被完全丢弃。    |
| `IgnoreInterimUpdates` | `是`  | 当 `是`，中间更新会被过滤掉。非最终、非中间的稳定更新仍会通过。 |
| `CharacterIdFilter`    | `""` | 当不为空时，只有匹配此 ID 的角色转录会到达角色回调。      |

{% hint style="info" %}
`FinalOnly` 和 `IgnoreInterimUpdates` 是彼此独立的过滤器。 `FinalOnly = true` 是最严格的——除已确认的最终转录外，其余全部丢弃。 `IgnoreInterimUpdates = true` （默认）会丢弃进行中的部分词，但允许稳定的中间更新通过，从而使字幕渲染更平滑。
{% endhint %}

#### 事件

| 事件                                   | 参数                             | 触发时机                          |
| ------------------------------------ | ------------------------------ | ----------------------------- |
| `OnTranscriptReceived`               | `TranscriptUpdateRelayData`    | 任何转录更新到达，角色或玩家，都会先于下面按角色划分的事件 |
| `OnCharacterTranscriptReceived`      | `CharacterTranscriptRelayData` | 角色转录到达（遵循过滤器）                 |
| `OnPlayerTranscriptReceived`         | `PlayerTranscriptRelayData`    | 玩家转录到达（遵循过滤器）                 |
| `OnFinalCharacterTranscriptReceived` | `CharacterTranscriptRelayData` | 角色转录已定稿                       |
| `OnFinalPlayerTranscriptReceived`    | `PlayerTranscriptRelayData`    | 玩家转录已定稿                       |

#### `PlayerTranscriptRelayData` 字段

| 字段              | 类型    | 说明                 |
| --------------- | ----- | ------------------ |
| `PlayerId`      | `字符串` | 本地玩家标识符            |
| `PlayerName`    | `字符串` | 玩家的显示名称            |
| `SpeakerId`     | `字符串` | 服务器分配的说话者 ID       |
| `SpeakerName`   | `字符串` | 服务器分配的说话者显示名称      |
| `ParticipantId` | `字符串` | 房间参与者标识符           |
| `TurnId`        | `字符串` | 此对话回合的标识符          |
| `MessageId`     | `字符串` | 此转录消息的标识符          |
| `文本`            | `字符串` | 转录文本（可能是中间稿）       |
| `IsFinal`       | `布尔值` | 当这是该回合的最终转录时为 True |

***

### C# 事件中心 — 按角色范围划分的事件

通过以下方式访问： `ConvaiManager.ActiveManager.Events`. 这些事件会在整个房间范围内触发——当存在多个角色时，请按 `角色 ID` 进行过滤，以将响应限定到特定角色。

#### 角色事件

| 事件                              | 参数类型                          | 触发时机                   |
| ------------------------------- | ----------------------------- | ---------------------- |
| `OnCharacterTranscriptReceived` | `CharacterTranscriptReceived` | 角色转录到达                 |
| `OnCharacterSpeechStateChanged` | `CharacterSpeechStateChanged` | 角色开始或停止说话              |
| `OnCharacterEmotionChanged`     | `CharacterEmotionChanged`     | 角色情绪发生变化               |
| `OnCharacterReady`              | `CharacterReady`              | 角色已准备好进行对话             |
| `OnCharacterTurnCompleted`      | `CharacterTurnCompleted`      | 角色回合结束                 |
| `OnCharacterActionReceived`     | `CharacterActionReceived`     | Convai 为此角色发送结构化的场景内动作 |
| `OnLlmNoResponseReceived`       | `LlmNoResponseReceived`       | Convai 已处理输入，但未生成口头回应  |

#### 玩家事件

| 事件                                 | 参数类型                             | 触发时机                 |
| ---------------------------------- | -------------------------------- | -------------------- |
| `OnPlayerTranscriptReceived`       | `PlayerTranscriptReceived`       | 玩家转录到达               |
| `OnPlayerSpeakingStateChanged`     | `PlayerSpeakingStateChanged`     | 玩家开始或停止说话            |
| `OnFinalUserTranscriptionReceived` | `FinalUserTranscriptionReceived` | 玩家的转录已定稿             |
| `OnVadSttStateChanged`             | `VadSttStateChanged`             | 语音活动检测 / 语音转文本管线状态变化 |

#### 跨功能事件

| 事件                          | 参数类型                      | 备注                               |
| --------------------------- | ------------------------- | -------------------------------- |
| `OnNarrativeSectionChanged` | `NarrativeSectionChanged` | 某个角色上的叙事设计部分发生了变化。完整细节请参见叙事设计部分。 |

#### 内部 / 高级事件

| 事件                              | 注意                                                                           |
| ------------------------------- | ---------------------------------------------------------------------------- |
| `OnModerationResponseReceived`  | 当 Convai 返回审核决定时触发。适用于在安全关键型训练模拟中对被标记内容做出响应。通过以下方式访问原始字段 `ConvaiEvents.Raw`. |
| `OnBlendshapeTurnStatsReceived` | 内部口型同步性能统计。不适合一般用途。                                                          |

***

### 直接 `ConvaiCharacter` C# 事件

这些事件位于 `ConvaiCharacter` 组件本身上——而不是位于 `ConvaiEvents`上。请直接在角色实例上订阅。将它们用于中继或事件中心未暴露的、按角色划分的音频和按角色划分的会话状态。

| 事件                            | 签名                     | 触发时机                     |
| ----------------------------- | ---------------------- | ------------------------ |
| `OnRemoteAudioEnabledChanged` | `Action<bool>`         | 角色的远程音频输出已启用或禁用          |
| `OnSessionStateChanged`       | `Action<SessionState>` | 此单个角色的会话状态发生变化（与房间级状态不同） |

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

public class CharacterAudioIndicator : MonoBehaviour
{
    [SerializeField] private ConvaiCharacter _character;
    [SerializeField] private GameObject     _muteIcon;

    private void OnEnable()
    {
        if (_character == null) return;
        _character.OnRemoteAudioEnabledChanged += OnAudioChanged;
    }

    private void OnDisable()
    {
        if (_character == null) return;
        _character.OnRemoteAudioEnabledChanged -= OnAudioChanged;
    }

    private void OnAudioChanged(bool isEnabled) =>
        _muteIcon.SetActive(!isEnabled);
}
```

`OnSessionStateChanged` 时 `ConvaiCharacter` 反映的是该角色各自的会话，而不是房间级状态。在多角色场景中，每个角色都有自己的会话状态。请使用 `ConvaiManager.ActiveManager.Events.OnSessionStateChanged` 来获取房间级状态。

***

### 域事件载荷类型

#### `CharacterTranscriptReceived`

| 字段              | 类型                  | 说明               |
| --------------- | ------------------- | ---------------- |
| `角色 ID`         | `字符串`               | 角色标识符            |
| `CharacterName` | `字符串`               | 角色显示名称           |
| `文本`            | `字符串`               | 转录文本             |
| `IsFinal`       | `布尔值`               | 当不会再有后续更新时为 True |
| `IsInterim`     | `布尔值`               | 对进行中的部分转录为 True  |
| `时间戳`           | `DateTime`          | 事件发生的 UTC 时间     |
| `消息`            | `TranscriptMessage` | 包含额外元数据的完整消息对象   |

#### `CharacterSpeechStateChanged`

| 字段                | 类型         | 说明                  |
| ----------------- | ---------- | ------------------- |
| `角色 ID`           | `字符串`      | 角色标识符               |
| `IsSpeaking`      | `布尔值`      | 当角色正在发言时为 True      |
| `IsStartOfSpeech` | `布尔值`      | 在语音片段的第一个事件上为 True  |
| `IsEndOfSpeech`   | `布尔值`      | 在语音片段的最后一个事件上为 True |
| `IsSilent`        | `布尔值`      | 当未在说话时为 True        |
| `UtteranceId`     | `字符串`      | 此语音片段的标识符           |
| `时间戳`             | `DateTime` | 事件发生的 UTC 时间        |

#### `CharacterEmotionChanged`

| 字段                    | 类型         | 说明                            |
| --------------------- | ---------- | ----------------------------- |
| `角色 ID`               | `字符串`      | 角色标识符                         |
| `情绪`                  | `字符串`      | 来自 Convai 分类法的情绪标签，例如 `“Joy”` |
| `强度`                  | `整数`       | 原始强度值；范围 1–3                  |
| `NormalizedIntensity` | `float`    | 归一化到 0.0–1.0 的强度              |
| `IsNeutral`           | `布尔值`      | 当角色回到中性状态时为 True              |
| `IsHighIntensity`     | `布尔值`      | 对于高强度情绪为 True                 |
| `IsLowIntensity`      | `布尔值`      | 对于低强度情绪为 True                 |
| `时间戳`                 | `DateTime` | 事件发生的 UTC 时间                  |

#### `CharacterReady`

| 字段              | 类型         | 说明                                 |
| --------------- | ---------- | ---------------------------------- |
| `角色 ID`         | `字符串`      | 角色标识符                              |
| `ParticipantId` | `字符串`      | 该角色的房间参与者标识符                       |
| `成员 ID`         | `字符串`      | 在多角色房间中，此就绪信号所属的成员关系。单角色会话中为空字符串。  |
| `角色会话 ID`       | `字符串`      | 在多角色房间中，此成员关系的角色会话标识符。单角色会话中为空字符串。 |
| `参与者标识`         | `字符串`      | 在多角色房间中，此成员关系的传输参与者身份。单角色会话中为空字符串。 |
| `时间戳`           | `DateTime` | 角色变为就绪的 UTC 时间                     |

参见 [角色身份与寻址](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/multi-character-sessions/character-identity.md) 了解这些字段如何在共享房间中定位某个成员关系。

#### `CharacterTurnCompleted`

| 字段               | 类型         | 说明                 |
| ---------------- | ---------- | ------------------ |
| `角色 ID`          | `字符串`      | 角色标识符              |
| `ParticipantId`  | `字符串`      | 房间参与者标识符           |
| `WasInterrupted` | `布尔值`      | 当回合因玩家打断而结束时为 True |
| `时间戳`            | `DateTime` | 回合完成的 UTC 时间       |

#### `CharacterActionReceived`

| 字段      | 类型                                   | 说明            |
| ------- | ------------------------------------ | ------------- |
| `角色 ID` | `字符串`                                | 角色标识符         |
| `动作`    | `IReadOnlyList<ConvaiActionCommand>` | 此回合的场景内动作有序列表 |
| `时间戳`   | `DateTime`                           | 事件发生的 UTC 时间  |

#### `LlmNoResponseReceived`

| 字段              | 类型         | 说明                |
| --------------- | ---------- | ----------------- |
| `角色 ID`         | `字符串`      | 角色标识符             |
| `ParticipantId` | `字符串`      | 房间参与者标识符          |
| `原因`            | `字符串`      | Convai 未生成口头回应的原因 |
| `时间戳`           | `DateTime` | 事件发生的 UTC 时间      |

#### `PlayerSpeakingStateChanged`

| 字段                | 类型         | 说明                    |
| ----------------- | ---------- | --------------------- |
| `SessionId`       | `字符串`      | Session identifier    |
| `IsSpeaking`      | `布尔值`      | 当玩家正在主动说话时为 True      |
| `IsStartOfSpeech` | `布尔值`      | 在玩家语音片段的第一个事件上为 True  |
| `IsEndOfSpeech`   | `布尔值`      | 在玩家语音片段的最后一个事件上为 True |
| `IsSilent`        | `布尔值`      | 当玩家未在说话时为 True        |
| `时间戳`             | `DateTime` | 事件发生的 UTC 时间          |

#### `FinalUserTranscriptionReceived`

| 字段              | 类型            | 说明           |
| --------------- | ------------- | ------------ |
| `文本`            | `字符串`         | 来自玩家的最终转录文本  |
| `SpeakerId`     | `字符串`         | 服务器分配的说话者 ID |
| `SpeakerName`   | `字符串`         | 说话者显示名称      |
| `ParticipantId` | `字符串`         | 房间参与者标识符     |
| `MessageId`     | `字符串`         | 消息标识符        |
| `时间戳`           | `DateTime`    | 事件发生的 UTC 时间 |
| `SpeakerInfo`   | `SpeakerInfo` | 结构化的说话者身份    |

#### `VadSttStateChanged`

| 字段         | 类型         | 说明                              |
| ---------- | ---------- | ------------------------------- |
| `IsActive` | `布尔值`      | 当语音活动检测 / STT 管线正在主动处理音频时为 True |
| `时间戳`      | `DateTime` | 事件发生的 UTC 时间                    |

***

### 支持类型

#### `SpeakerInfo` 结构体

| 字段                | 类型            | 说明                 |
| ----------------- | ------------- | ------------------ |
| `SpeakerId`       | `字符串`         | 服务器分配的说话者标识符       |
| `SpeakerName`     | `字符串`         | 显示名称               |
| `ParticipantId`   | `字符串`         | 房间参与者标识符           |
| `SpeakerType`     | `SpeakerType` | 该说话者的角色            |
| `IsValid`         | `布尔值`         | 当所有身份字段都已填充时为 True |
| `IsDefaultPlayer` | `布尔值`         | 对默认本地玩家身份为 True    |

#### `SpeakerType` 枚举

| 值        | 说明        |
| -------- | --------- |
| `未知` (0) | 尚未确定说话者类型 |
| `角色` (1) | 一个 AI 角色  |
| `玩家` (2) | 人类玩家      |
| `系统` (3) | 系统生成的消息   |

***

### Inspector 中继 vs. C# 事件——何时选择

使用 **Inspector 中继组件** 适用于以下情况：

* 在 Inspector 中连接到 Animator 参数、UI 组件或 Audio source
* 逻辑简单、基于组件且没有分支
* 零代码的拖放式接线是优先事项

使用 **C# 订阅** 通过 `ConvaiEvents` 适用于以下情况：

* 按 `角色 ID` 在运行时动态
* 处理程序包含条件逻辑或调用 async / coroutine 方法
* 用于场景中所有角色事件的单一处理程序

使用 **直接 `ConvaiCharacter` 订阅** 适用于以下情况：

* 响应 `OnRemoteAudioEnabledChanged` 或按角色 `OnSessionStateChanged`
* 这些事件在中继组件上不可用

***

### 使用示例

#### 示例 1 — 实时转录字幕显示

一个军事训练模拟在屏幕底部显示字幕条，随着 AI 教官的讲话流式传入而显示内容，并在每次中间转录时更新。

```csharp
using Convai.Domain.DomainEvents.Transcript;
using Convai.Runtime.Facades;
using TMPro;
using UnityEngine;

public class SubtitleDisplay : MonoBehaviour
{
    [SerializeField] private TMP_Text _label;
    [SerializeField] private string   _targetCharacterId;

    private void OnEnable()  => ConvaiManager.ActiveManager?.Events.OnCharacterTranscriptReceived += OnTranscript;
    private void OnDisable() => ConvaiManager.ActiveManager?.Events.OnCharacterTranscriptReceived -= OnTranscript;

    private void OnTranscript(CharacterTranscriptReceived e)
    {
        if (e.CharacterId != _targetCharacterId) return;
        _label.text = e.IsFinal ? string.Empty : e.Text;
    }
}
```

#### 示例 2 — 基于情绪的材质替换

一个交互体验会根据检测到的情绪强度改变角色的自发光材质颜色——高强度情绪使用更暖的色调，低强度则更冷。

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

public class EmotionMaterialDriver : MonoBehaviour
{
    [SerializeField] private Renderer _characterRenderer;
    [SerializeField] private string   _targetCharacterId;
    [SerializeField] private Color    _highIntensityColor = Color.red;
    [SerializeField] private Color    _lowIntensityColor  = Color.blue;
    [SerializeField] private Color    _neutralColor       = Color.white;

    private static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor");

    private void OnEnable()  => ConvaiManager.ActiveManager?.Events.OnCharacterEmotionChanged += OnEmotion;
    private void OnDisable() => ConvaiManager.ActiveManager?.Events.OnCharacterEmotionChanged -= OnEmotion;

    private void OnEmotion(CharacterEmotionChanged e)
    {
        if (e.CharacterId != _targetCharacterId) return;

        Color target = e.IsNeutral
            ? _neutralColor
            : Color.Lerp(_lowIntensityColor, _highIntensityColor, e.NormalizedIntensity);

        _characterRenderer.material.SetColor(EmissionColor, target);
    }
}
```

#### 示例 3 — 无回应时显示“思考中”旋转指示器

一个企业入职培训模拟在 AI 角色收到输入但尚未生成口头回应时显示旋转指示器，避免学习者误以为系统卡住了。

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

public class ThinkingSpinner : MonoBehaviour
{
    [SerializeField] private GameObject _spinnerRoot;
    [SerializeField] private string     _targetCharacterId;

    private void OnEnable()
    {
        var events = ConvaiManager.ActiveManager?.Events;
        if (events == null) return;
        events.OnLlmNoResponseReceived       += ShowSpinner;
        events.OnCharacterSpeechStateChanged += HideSpinnerOnSpeech;
    }

    private void OnDisable()
    {
        var events = ConvaiManager.ActiveManager?.Events;
        if (events == null) return;
        events.OnLlmNoResponseReceived       -= ShowSpinner;
        events.OnCharacterSpeechStateChanged -= HideSpinnerOnSpeech;
    }

    private void ShowSpinner(LlmNoResponseReceived e)
    {
        if (e.CharacterId != _targetCharacterId) return;
        _spinnerRoot.SetActive(true);
    }

    private void HideSpinnerOnSpeech(CharacterSpeechStateChanged e)
    {
        if (e.CharacterId != _targetCharacterId) return;
        if (e.IsStartOfSpeech) _spinnerRoot.SetActive(false);
    }
}
```

***

### 故障排查

| 症状                                                 | 可能原因                                                          | 修复方法                                                    |
| -------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------- |
| `ConvaiCharacterEventRelay` 回调从不触发                 | `AutoResolveCharacter` 已关闭且未分配角色                              | 启用 **Auto Resolve Character** 或分配 `ConvaiCharacter` 字段  |
| 转录 UI 显示重复内容                                       | 同时订阅了中继和 `ConvaiEvents.OnCharacterTranscriptReceived` 以处理同一事件 | 每个功能只使用一种方式——中继或 C# 订阅，不要两者都用                           |
| `CharacterIdFilter` 没有效果                           | 过滤器包含多余空格或大小写错误                                               | 比较是不区分大小写的；检查是否有前导/尾随空格                                 |
| `OnFinalCharacterTranscriptReceived` 始终不触发         | `FinalOnly = false` 且转写从不标记 `IsFinal = true`                  | 检查 `ConvaiCharacter.EnableRemoteAudioOnStart`; 角色必须完全连接 |
| `OnEmotionChanged` 触发但 `NormalizedIntensity` 始终为 0 | Convai 仪表板中该角色未启用情感功能                                         | 在角色的 Convai 配置中启用情感输出                                   |
| `OnRemoteAudioEnabledChanged` 始终不触发                | 订阅了中继，而不是订阅 C# 事件，位于 `ConvaiCharacter` 直接                     | 订阅 `character.OnRemoteAudioEnabledChanged` ——此事件不在中继上   |

***

### 下一步

连接好角色事件后，探索 [转录 API](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/scripting-reference/transcript-api.md) 用于基于拉取的时间线访问，或者 [角色与玩家 API](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/scripting-reference/character-and-player-api.md) 用于编写角色会话控制、音频和注意力的脚本。


---

# 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/scripting-reference/character-events.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.
