> 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-and-player-api.md).

# 角色和玩家 API

角色与玩家脚本组件参考，涵盖会话控制、语音、音频和注意力目标定位。

`ConvaiCharacter` 控制单个 AI 角色的会话、语音、远程音频、动态上下文和注意力目标。 `ConvaiPlayer` 表示本地人类参与者，并提供文本消息发送和身份配置。这两个组件由……拥有并跟踪。 `ConvaiManager`.

***

### 访问组件

```csharp
// 通过 Inspector 字段（推荐）
[SerializeField] private ConvaiCharacter _character;

// 通过管理器所有权列表
var character = ConvaiManager.ActiveManager?.Characters[0];

// 通过管理器的活动对话目标
var active = ConvaiManager.ActiveManager?.ActiveConversationCharacter;

// 玩家
var player = ConvaiManager.ActiveManager?.Player;
```

***

### `ConvaiCharacter`

#### 属性

| 属性                                | 类型                       | 访问  | 说明                                   |
| --------------------------------- | ------------------------ | --- | ------------------------------------ |
| `CharacterId`                     | `string`                 | 读取  | Convai 角色标识符                         |
| `CharacterName`                   | `string`                 | 读取  | 角色的显示名称                              |
| `OwnerId`                         | `string`                 | 读取  | 所有者账户标识符                             |
| `SessionState`                    | `SessionState`           | 读取  | 此单个角色的当前会话状态                         |
| `IsCharacterReady`                | `bool`                   | 读取  | 当角色完成就绪握手时为 True                     |
| `IsSessionConnected`              | `bool`                   | 读取  | 当此角色的会话处于……时为 True `已连接` 状态          |
| `IsInConversation`                | `bool`                   | 读取  | 当此角色是当前活动对话目标时为 True                 |
| `IsSpeaking`                      | `bool`                   | 读取  | 当角色正在积极生成音频输出时为 True                 |
| `IsRemoteAudioEnabled`            | `bool`                   | 读取  | 当此角色的远程音频输出已启用时为 True                |
| `CurrentEmotion`                  | `string`                 | 读取  | 从 Convai 接收到的最新情绪标签                  |
| `CurrentEmotionIntensity`         | `int`                    | 读取  | 最新情绪强度（1–3）                          |
| `ConfigurationSource`             | `ConvaiConfigSourceMode` | 读取  | 配置是来自 Inspector 字段还是配置文件资源           |
| `CharacterConfigAsset`            | `ConvaiCharacterProfile` | 读取  | 配置文件资源，当 `ConfigurationSource` 基于资源时 |
| `NameTagColor`                    | `Color`                  | 读取  | 用于该角色 UI 名称标签的颜色                     |
| `EnableRemoteAudioOnStart`        | `bool`                   | 读取  | 远程音频输出是否在启动时启用                       |
| `EnableSessionResume`             | `bool`                   | 读取  | 会话在重新连接后是否尝试恢复                       |
| `CharacterReadyTimeoutSeconds`    | `float`                  | 读/写 | 等待角色就绪握手超时前的秒数                       |
| `InitialDynamicInfoText`          | `string`                 | 读取  | 会话开始时发送的动态上下文文本                      |
| `InitialDynamicInfoKeepInContext` | `bool`                   | 读取  | 初始动态上下文是否在多轮中保留                      |
| `ActionConfig`                    | `ConvaiActionConfig`     | 读取  | 此角色的动作配置                             |
| `DynamicContext`                  | `IConvaiDynamicContext`  | 读取  | 动态上下文命令接口                            |
| `NarrativeDesign`                 | `IConvaiNarrativeDesign` | 读取  | 叙事设计接口                               |
| `IsInjected`                      | `bool`                   | 读取  | 当依赖项已由 SDK 注入时为 True                 |

#### `ConvaiConfigSourceMode` 枚举

| 值    | 说明                                   |
| ---- | ------------------------------------ |
| `内联` | 在 Inspector 中直接设置在组件上的配置             |
| `资源` | 从……加载的配置 `ConvaiCharacterProfile` 资源 |

#### 会话控制

| 方法                                                                                         | 返回                       | 说明                                                       |
| ------------------------------------------------------------------------------------------ | ------------------------ | -------------------------------------------------------- |
| `StartConversationAsync(CancellationToken ct = default)`                                   | `IConvaiOperation<Unit>` | 为此角色启动一个对话会话                                             |
| `StopConversationAsync(CancellationToken ct = default)`                                    | `IConvaiOperation<Unit>` | 停止此角色的对话会话                                               |
| `WaitForCharacterReadyAsync(float? timeoutSeconds = null, CancellationToken ct = default)` | `IConvaiOperation<Unit>` | 等待直到角色完成其就绪握手。请在……之后使用 `StartConversationAsync` 在发送输入之前。 |
| `ResetAndRetryAsync(CancellationToken ct = default)`                                       | `IConvaiOperation<Unit>` | 重置角色的会话状态并重试初始化。请在出错后使用。                                 |
| `Reset()`                                                                                  | `bool`                   | 同步重置本地会话状态。返回 `true` 如果重置已应用，则为 true。                    |

```csharp
// 连接并在发送输入前等待角色就绪
var startOp = character.StartConversationAsync(destroyCancellationToken);
await startOp;
if (startOp.IsSuccessful)
{
    await character.WaitForCharacterReadyAsync(timeoutSeconds: 10f, destroyCancellationToken);
    Debug.Log("角色已准备好接收输入。");
}
```

#### 语音控制

| 方法                    | 返回                       | 说明                         |
| --------------------- | ------------------------ | -------------------------- |
| `ToggleSpeech()`      | `void`                   | 切换角色的对话会话。若已断开则启动；若已连接则停止。 |
| `ToggleSpeechAsync()` | `IConvaiOperation<Unit>` | 的异步版本 `ToggleSpeech`.      |

#### 远程音频控制

| 方法                                    | 返回     | 说明                                           |
| ------------------------------------- | ------ | -------------------------------------------- |
| `SetRemoteAudioEnabled(bool enabled)` | `bool` | 设置此角色的音频输出是否在本地播放。返回 `true` 如果更改已应用，则为 true。 |
| `EnableRemoteAudio()`                 | `bool` | 启用此角色的音频输出。返回 `true` 如果已应用，则为 true。          |
| `DisableRemoteAudio()`                | `bool` | 禁用此角色的音频输出。返回 `true` 如果已应用，则为 true。          |
| `ToggleRemoteAudio()`                 | `void` | 切换此角色的音频输出状态。                                |

{% hint style="info" %}
按角色的音频控制可让你在多角色场景中静音单个角色——例如，在主讲者发言时静音次要讲师。对于麦克风静音（你的输入），请使用 `ConvaiManager.ActiveManager.Audio`.
{% endhint %}

#### 动态上下文和叙事

| 方法                                                                                          | 返回     | 说明                                          |
| ------------------------------------------------------------------------------------------- | ------ | ------------------------------------------- |
| `DynamicContext.AddEvent(string text, ConvaiRespondMode reaction = ConvaiRespondMode.Auto)` | `void` | 将按时间顺序排列的事件追加到角色跟踪的动态上下文中，并为 Convai 安排批量更新。 |
| `SendTrigger(string triggerName)`                                                           | `void` | 按名称调用已保存的叙事设计触发器。                           |
| `SendNarrativeEvent(string eventMessage)`                                                   | `void` | 发送内联叙事事件上下文，并让 Convai 自然响应。                 |
| `UpdateTemplateKeys(Dictionary<string, string> templateKeys)`                               | `void` | 更新叙事设计模板键值，以便进行动态叙事变量替换。                    |

{% hint style="info" %}
`SendTrigger` 按名称触发已在叙事设计中配置的内容。 `SendNarrativeEvent` 而是发送你在代码中编写的内联消息。使用与事件文本编写位置相匹配的方式。
{% endhint %}

#### 注意力和动作

| 方法                                                                                                                               | 返回                         | 说明                       |
| -------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | ------------------------ |
| `DynamicContext.SetCurrentAttentionObject(object currentAttentionObject, ConvaiRespondMode reaction = ConvaiRespondMode.Silent)` | `void`                     | 按名称或动作对象资源设置角色当前关注的场景对象。 |
| `DynamicContext.ClearCurrentAttentionObject(ConvaiRespondMode reaction = ConvaiRespondMode.Silent)`                              | `void`                     | 清除当前场景内注意力对象。            |
| `GetActionConfigSource()`                                                                                                        | `ConvaiActionConfigSource` | 返回此角色的动作配置源组件。           |

#### `ConvaiCharacter` 事件

在……中订阅 `OnEnable`，在……中取消订阅 `OnDisable`.

| 事件                            | 签名                                           | 触发时机                                         |
| ----------------------------- | -------------------------------------------- | -------------------------------------------- |
| `OnTranscriptReceived`        | `Action<string, bool>`                       | 转录到达。参数：text, isFinal。                       |
| `OnSpeechStarted`             | `操作`                                         | 角色开始生成音频输出                                   |
| `OnSpeechStopped`             | `操作`                                         | 角色停止生成音频输出                                   |
| `OnTurnCompleted`             | `Action<bool>`                               | 角色的对话轮次结束。参数：wasInterrupted。                 |
| `OnCharacterReady`            | `操作`                                         | 角色完成其就绪握手                                    |
| `OnSessionStateChanged`       | `Action<SessionState>`                       | 此角色的单独会话状态发生变化                               |
| `OnEmotionChanged`            | `Action<string, int>`                        | 情绪发生变化。参数：emotion label, raw intensity（1–3）。 |
| `OnActionsReceived`           | `Action<IReadOnlyList<ConvaiActionCommand>>` | Convai 为此角色发送场景内动作命令                         |
| `OnRemoteAudioEnabledChanged` | `Action<bool>`                               | 此角色的远程音频输出已启用或禁用                             |

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

public class CharacterSessionMonitor : MonoBehaviour
{
    [SerializeField] private ConvaiCharacter _character;

    private void OnEnable()
    {
        _character.OnSessionStateChanged      += OnStateChanged;
        _character.OnRemoteAudioEnabledChanged += OnAudioChanged;
        _character.OnCharacterReady            += OnReady;
    }

    private void OnDisable()
    {
        _character.OnSessionStateChanged      -= OnStateChanged;
        _character.OnRemoteAudioEnabledChanged -= OnAudioChanged;
        _character.OnCharacterReady            -= OnReady;
    }

    private void OnStateChanged(SessionState state) =>
        Debug.Log($"[{_character.CharacterName}] 会话状态：{state}");

    private void OnAudioChanged(bool enabled) =>
        Debug.Log($"[{_character.CharacterName}] 音频输出：{(enabled ? "开启" : "关闭")}");

    private void OnReady() =>
        Debug.Log($"[{_character.CharacterName}] 已准备好。");
}
```

***

### `ConvaiPlayer`

`ConvaiPlayer` 表示会话中的本地人类参与者。它拥有玩家的显示名称和身份，并提供文本消息发送。

#### 属性

| 属性             | 类型       | 访问 | 说明               |
| -------------- | -------- | -- | ---------------- |
| `PlayerName`   | `string` | 读取 | 玩家的显示名称          |
| `PlayerId`     | `string` | 读取 | 玩家身份标识符          |
| `NameTagColor` | `Color`  | 读取 | 用于该玩家 UI 名称标签的颜色 |

#### 方法

| 方法                                                     | 返回     | 说明                                                    |
| ------------------------------------------------------ | ------ | ----------------------------------------------------- |
| `SendTextMessage(string message)`                      | `void` | 以此玩家身份向 Convai 发送文本消息，绕过麦克风输入。适用于文本输入模式或程序化玩家对话。      |
| `Configure(string playerName, string playerId = null)` | `void` | 设置玩家的显示名称和可选身份。请在……之前调用 `ConnectAsync` 以确保该身份在会话中被使用。 |
| `SetRuntimeDisplayName(string displayName)`            | `void` | 在运行时更新玩家的显示名称，而不更改身份。会反映在转录参与者名称中。                    |

#### `ConvaiPlayer` 事件

| 事件                  | 签名               | 触发时机                            |
| ------------------- | ---------------- | ------------------------------- |
| `OnTextMessageSent` | `Action<string>` | 通过……发送了一条文本消息 `SendTextMessage` |

```csharp
using Convai.Runtime.Facades;
using TMPro;
using UnityEngine;

public class TextInputController : MonoBehaviour
{
    [SerializeField] private TMP_InputField _inputField;

    public void OnSubmit()
    {
        var text = _inputField.text.Trim();
        if (string.IsNullOrEmpty(text)) return;

        ConvaiManager.ActiveManager?.Player?.SendTextMessage(text);
        _inputField.text = string.Empty;
    }
}
```

***

### 使用示例

#### 示例 1 — 连接角色并基于就绪状态进行门控

一个医疗培训模拟会确保 AI 医师角色在评估开始前已完全准备就绪，防止学习者与尚未初始化的角色对话。

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

public class AssessmentStarter : MonoBehaviour
{
    [SerializeField] private ConvaiCharacter _physician;
    [SerializeField] private GameObject      _startPanel;

    private async void Start()
    {
        _startPanel.SetActive(false);

        var startOp = _physician.StartConversationAsync(destroyCancellationToken);
        await startOp;
        if (!startOp.IsSuccessful)
        {
            Debug.LogError($"角色连接失败：{startOp.Error.Message}");
            return;
        }

        var readyOp = _physician.WaitForCharacterReadyAsync(
            timeoutSeconds: 15f, destroyCancellationToken);
        await readyOp;

        if (readyOp.IsSuccessful)
            _startPanel.SetActive(true);
        else
            Debug.LogWarning("角色就绪超时——请检查你的 API 密钥和网络。");
    }
}
```

#### 示例 2 — 多 NPC 场景中的按角色音频切换

一个企业入职模拟包含三个 AI 顾问。UI 面板允许学习者静音任意单个顾问，而不影响其他顾问。

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

public class AdvisorMuteButton : MonoBehaviour
{
    [SerializeField] private ConvaiCharacter _advisor;
    [SerializeField] private Button         _muteButton;
    [SerializeField] private Sprite         _mutedIcon;
    [SerializeField] private Sprite         _unmutedIcon;

    private Image _buttonImage;

    private void Awake() => _buttonImage = _muteButton.GetComponent<Image>();

    private void OnEnable()
    {
        _muteButton.onClick.AddListener(OnToggleMute);
        _advisor.OnRemoteAudioEnabledChanged += RefreshIcon;
        RefreshIcon(_advisor.IsRemoteAudioEnabled);
    }

    private void OnDisable()
    {
        _muteButton.onClick.RemoveListener(OnToggleMute);
        _advisor.OnRemoteAudioEnabledChanged -= RefreshIcon;
    }

    private void OnToggleMute() => _advisor.ToggleRemoteAudio();

    private void RefreshIcon(bool enabled) =>
        _buttonImage.sprite = enabled ? _unmutedIcon : _mutedIcon;
}
```

#### 示例 3 — 无障碍文本输入模式

一个工业安全模拟在麦克风访问不可用或受限的环境中提供文本输入备用方案。

```csharp
using Convai.Runtime.Facades;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class AccessibilityTextInput : MonoBehaviour
{
    [SerializeField] private TMP_InputField _inputField;
    [SerializeField] private Button        _submitButton;

    private void OnEnable()  => _submitButton.onClick.AddListener(Submit);
    private void OnDisable() => _submitButton.onClick.RemoveListener(Submit);

    private void Submit()
    {
        var text = _inputField.text.Trim();
        if (string.IsNullOrEmpty(text)) return;

        var player = ConvaiManager.ActiveManager?.Player;
        if (player == null) return;

        player.SendTextMessage(text);
        _inputField.text = string.Empty;
        _inputField.ActivateInputField(); // 保持焦点以便快速输入
    }
}
```

***

### 故障排查

| 症状                                 | 可能原因                                          | 修复                                                                                       |
| ---------------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `WaitForCharacterReadyAsync` 超时    | 角色从未收到来自 Convai 的就绪确认                         | 验证 API 密钥，检查网络；在……之后调用 `StartConversationAsync` 成功，而不是之前                                 |
| `DynamicContext.AddEvent` 没有可见效果   | 在角色进入对话之前调用，因此该事件被本地暂存，但尚未发送                  | 一旦……，暂存的事件会自动发送 `WaitForCharacterReadyAsync` 解析； `DynamicContext.Flush()` 也只会在处于对话中时发送一次 |
| `ToggleRemoteAudio()` 没有效果         | `EnableRemoteAudioOnStart` 是 `false` 而且音频从未启用 | 调用 `EnableRemoteAudio()` 先激活音频，然后切换                                                      |
| `SendTextMessage` 已发送，但角色没有响应      | 会话不在 `已连接` 状态                                 | 检查 `character.IsSessionConnected` 在发送之前                                                  |
| `OnActionsReceived` 已触发，但没有执行场景内动作 | `ConvaiActionDispatcher` 不在场景中，或动作名称不匹配       | 验证调度器是否存在；动作名称不区分大小写，但必须与已配置名称匹配                                                         |

***

### 下一步

关于房间级别的音频和麦克风控制，请参见 [音频 API](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/scripting-reference/audio-api.md)。关于会话连接控制，请参见 [ConvaiManager API](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/scripting-reference/convaimanager-api.md)。关于通过 relay 或 C# hub 订阅角色事件，请参见 [角色事件](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/scripting-reference/character-events.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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/scripting-reference/character-and-player-api.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
