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

# 角色事件

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

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

***

### `ConvaiCharacterEventRelay`

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

放置在与 `ConvaiCharacter`相同的 GameObject 上，或者放在任何带有 **自动解析角色** 已启用的 GameObject 上，以便在 `ConvaiCharacter` 在运行时拾取同一对象上的角色。

{% tabs %}
{% tab title="检查器" %}
添加该组件，并在检查器中将回调连接到六个 UnityEvent 中的任意一个。启用 **自动解析角色** 或显式分配 `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} 开始说话。");
        if (e.IsEndOfSpeech)   Debug.Log($"{e.CharacterId} 停止说话。");
    }

    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` 字段

| 字段              | 类型       | 说明                 |
| --------------- | -------- | ------------------ |
| `CharacterId`   | `string` | 角色的标识符             |
| `CharacterName` | `string` | 角色的显示名称            |
| `文本`            | `string` | 当前转录文本（可能是中间结果）    |
| `IsFinal`       | `bool`   | 当此话语不再有后续更新时为 True |

#### `CharacterEmotionRelayData` 字段

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

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

#### `CharacterTurnCompletedRelayData` 字段

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

***

### `ConvaiTranscriptEventRelay`

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

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

#### 过滤属性

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

{% hint style="info" %}
`FinalOnly` 是位于 `IgnoreInterimUpdates` 是不同的过滤条件。 `FinalOnly = true` 是最严格的——除已确认的最终转录外，其余全部丢弃。 `IgnoreInterimUpdates = true` （默认）会丢弃进行中的部分词语，但允许稳定的中间更新通过，从而实现更平滑的字幕渲染。
{% endhint %}

#### 事件

| 事件                                   | 参数                             | 触发时            |
| ------------------------------------ | ------------------------------ | -------------- |
| `OnCharacterTranscriptReceived`      | `CharacterTranscriptRelayData` | 角色转录到达（遵循过滤条件） |
| `OnPlayerTranscriptReceived`         | `PlayerTranscriptRelayData`    | 玩家转录到达（遵循过滤条件） |
| `OnFinalCharacterTranscriptReceived` | `CharacterTranscriptRelayData` | 角色转录已定稿        |
| `OnFinalPlayerTranscriptReceived`    | `PlayerTranscriptRelayData`    | 玩家转录已定稿        |

#### `PlayerTranscriptRelayData` 字段

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

***

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

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

#### 角色事件

| 事件                              | 参数类型                          | 触发时                    |
| ------------------------------- | ----------------------------- | ---------------------- |
| `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` | 角色上的 Narrative Design 部分已更改。完整详情请参见 Narrative Design 部分。 |

#### 内部 / 高级事件

| 事件                              | 注意                                                                             |
| ------------------------------- | ------------------------------------------------------------------------------ |
| `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);
}
```

{% hint style="info" %}
`OnSessionStateChanged` on `ConvaiCharacter` 反映的是此角色的独立会话，而非房间级状态。在多角色场景中，每个角色都有自己的会话状态。请使用 `ConvaiManager.ActiveManager.Events.OnSessionStateChanged` 以获取房间级状态。
{% endhint %}

***

### 域事件负载类型

#### `CharacterTranscriptReceived`

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

#### `CharacterSpeechStateChanged`

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

#### `CharacterEmotionChanged`

| 字段                    | 类型         | 说明                             |
| --------------------- | ---------- | ------------------------------ |
| `CharacterId`         | `string`   | 角色标识符                          |
| `情绪`                  | `string`   | 来自 Convai 分类体系的情绪标签，例如 `"Joy"` |
| `强度`                  | `int`      | 原始强度值；范围 1–3                   |
| `NormalizedIntensity` | `float`    | 归一化到 0.0–1.0 的强度               |
| `IsNeutral`           | `bool`     | 当角色回到中性状态时为 True               |
| `IsHighIntensity`     | `bool`     | 高强度情绪时为 True                   |
| `IsLowIntensity`      | `bool`     | 低强度情绪时为 True                   |
| `时间戳`                 | `DateTime` | 事件发生的 UTC 时间                   |

#### `CharacterReady`

| 字段              | 类型         | 说明              |
| --------------- | ---------- | --------------- |
| `CharacterId`   | `string`   | 角色标识符           |
| `ParticipantId` | `string`   | 此角色在房间中的参与者标识符  |
| `时间戳`           | `DateTime` | 角色变为就绪时的 UTC 时间 |

#### `CharacterTurnCompleted`

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

#### `CharacterActionReceived`

| 字段            | 类型                                   | 说明                |
| ------------- | ------------------------------------ | ----------------- |
| `CharacterId` | `string`                             | 角色标识符             |
| `动作`          | `IReadOnlyList<ConvaiActionCommand>` | 该回合中按顺序排列的场景内动作列表 |
| `时间戳`         | `DateTime`                           | 事件发生的 UTC 时间      |

#### `LlmNoResponseReceived`

| 字段              | 类型         | 说明                |
| --------------- | ---------- | ----------------- |
| `CharacterId`   | `string`   | 角色标识符             |
| `ParticipantId` | `string`   | 房间参与者标识符          |
| `原因`            | `string`   | Convai 未生成语音回复的原因 |
| `时间戳`           | `DateTime` | 事件发生的 UTC 时间      |

#### `PlayerSpeakingStateChanged`

| 字段                | 类型         | 说明                    |
| ----------------- | ---------- | --------------------- |
| `SessionId`       | `string`   | 会话标识符                 |
| `IsSpeaking`      | `bool`     | 当玩家正在说话时为 True        |
| `IsStartOfSpeech` | `bool`     | 在玩家语音片段的第一个事件上为 True  |
| `IsEndOfSpeech`   | `bool`     | 在玩家语音片段的最后一个事件上为 True |
| `IsSilent`        | `bool`     | 当玩家未在说话时为 True        |
| `时间戳`             | `DateTime` | 事件发生的 UTC 时间          |

#### `FinalUserTranscriptionReceived`

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

#### `VadSttStateChanged`

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

***

### 支持类型

#### `SpeakerInfo` struct

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

#### `SpeakerType` enum

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

***

### 检查器中继 vs. C# 事件——何时选择

使用 **检查器中继组件** 当：

* 在检查器中连接到 Animator 参数、UI 组件或 Audio 源
* 逻辑简单、基于组件且没有分支
* 零代码的拖放式连接是首要目标

使用 **C# 订阅** 通过 `ConvaiEvents` 当：

* 按 `CharacterId` 在运行时动态地
* 处理器具有条件逻辑或调用异步 / 协程方法
* 用于场景中所有角色事件的单一处理器

使用 **直接 `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` 处于关闭状态且未分配角色                             | 启用 **自动解析角色** 或显式分配 `ConvaiCharacter` 字段                   |
| 转录 UI 显示重复内容                                         | 同时订阅了 relay 和 `ConvaiEvents.OnCharacterTranscriptReceived` 同一事件 | 每个功能只使用一种方式——relay 或 C# 订阅，不要两者都用                          |
| `CharacterIdFilter` 没有效果                             | 过滤器包含多余空白或大小写错误                                                 | 比较不区分大小写；检查前导/尾随空格                                         |
| `OnFinalCharacterTranscriptReceived` 从不触发            | `FinalOnly = false` 并且转录从未标记 `IsFinal = true`                   | 检查 `ConvaiCharacter.EnableRemoteAudioOnStart`; 角色必须完全连接    |
| `OnEmotionChanged` 会触发，但 `NormalizedIntensity` 始终为 0 | Convai 仪表板中该角色未启用情绪功能                                           | 在角色的 Convai 配置中启用情绪输出                                      |
| `OnRemoteAudioEnabledChanged` 从不触发                   | 订阅了 relay，而不是 `ConvaiCharacter` 直接                              | 订阅 `character.OnRemoteAudioEnabledChanged` ——此事件不在 relay 上 |

***

### 下一步

接好角色事件后，了解 [Transcript API](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/scripting-reference/transcript-api.md) 用于基于拉取的时间线访问，或 [Character & Player 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:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/scripting-reference/character-events.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.
