先决条件
Game Chat 2 要求您的项目已经为 GDK 进行了设置。有关如何设置的详细信息,请参见 开始使用 Microsoft Game Development Kit。 编译 Game Chat 2 需要包含主要的 GameChat2.h 头文件。 为了正确链接,您的项目还必须在至少一个编译单元中包含 GameChat2Impl.h(我们建议使用共同的预编译头,因为这些存根函数实现很小,编译器容易将它们生成为 “内联”)。 Game Chat 2 接口不要求项目在使用 C++/CX 与传统 C++ 编译之间做出选择。它可以与两者一起使用。实现也不会抛出异常作为非致命错误报告的手段。如果您愿意,可以轻松地从无异常的项目中使用它。但是,该实现会抛出异常作为致命错误报告的手段。(有关更多详细信息,请参见本主题稍后的 失败模型 部分。)初始化
通过使用适用于单例初始化生命周期的参数初始化 Game Chat 2 单例实例,开始与库交互。通过调用 chat_manager::initialize 初始化单例实例,如下所示。您必须通过
RegisterAppStateChangeNotification 注册挂起和恢复事件。挂起时,必须使用 chat_manager::cleanup() 清理 Game Chat 2。恢复时,应重新初始化 Game Chat 2。如果尝试在挂起/恢复周期中使用它,它可能会崩溃。配置用户
将用户添加到 Microsoft Game Development Kit (GDK) 游戏
在将用户添加到 Game Chat 2 实例之前,请确保它们已添加到 GDK 游戏中。 这是通过使用 XUserAddAsync API 完成的。有关使用此 API 的更多信息,请参见 用户身份和 XUser。 获得要添加到 Game Chat 2 的用户的XUserHandle 之后,您需要使用 XUserGetId API 获取用户的 XBOX 用户 ID (XUID)。
用户必须在线,并且您必须获得用户对此步骤的同意。
XUserGetId 以 uint64_t 形式提供 XUID。您必须将 XUID 转换为 std::wstring 才能与 Game Chat 2 一起使用。
以下是一个代码示例,演示了如何在拥有 XUserHandle 之后将用户添加到 Game Chat 2。
请注意,调用 XUserResolveIssueWithUiAsync 会显示系统对话框。
将用户添加到 Game Chat 2
初始化实例后,必须使用 chat_manager::add_local_user 将本地用户添加到 Game Chat 2 实例。在此示例中,用户 A 代表本地用户。c_communicationRelationshipSendAndReceiveAll 是 GameChat2.h 中定义的常量,用于表示双向通信。
使用 chat_user_local::set_communication_relationship 设置用户 A 与用户 B 的关系。
c_communicationRelationshipSendAll 是 GameChat2.h 中定义的常量,用于表示这种单向通信。
按如下方式设置关系。
chat_manager::remove_user() 可能使用户对象无效。如果您使用 实时音频处理,请参阅 聊天用户生命周期 了解更多信息。否则,在调用 chat_manager::remove_user() 时,用户对象立即失效。有关何时可以删除用户的细微限制在本主题稍后的 处理状态更改 部分详细介绍。
处理数据帧
Game Chat 2 没有自己的传输层。它必须由应用提供。 此插入通过应用定期、频繁地调用 chat_manager::start_processing_data_frames() 和 chat_manager::finish_processing_data_frames() 这对方法来管理。这些方法是 Game Chat 2 向应用提供输出数据的方式。 这些方法设计为快速操作。可以在专用网络线程上频繁轮询它们。 这为检索所有排队数据提供了方便的位置,而不必担心网络时序的不可预测性或多线程回调的复杂性。 当调用chat_manager::start_processing_data_frames() 时,所有排队的数据都在 game_chat_data_frame 结构指针数组中报告。
应用应遍历数组,检查目标端点,并使用应用的网络层将数据传送到适当的远程应用实例。
在完成数组中所有 game_chat_data_frame 结构的处理后,应通过调用 chat_manager:finish_processing_data_frames() 将数组传回 Game Chat 2 以释放资源。
这在以下示例中显示。
处理状态更改
Game Chat 2 通过应用定期、频繁调用 chat_manager::start_processing_state_changes() 和 chat_manager::finish_processing_state_changes() 这对方法向应用提供更新,例如收到的文本消息。 这些方法操作迅速,因此可以在您的 UI 渲染循环中的每个图形帧调用它们。 这为检索所有排队更改提供了方便的位置,而不必担心网络时序的不可预测性或多线程回调的复杂性。 当调用chat_manager::start_processing_state_changes() 时,所有排队的更新都在 game_chat_state_change 结构指针数组中报告。
应用应遍历数组,检查基础结构的具体类型,将基础结构强制转换为相应的更详细类型,然后适当地处理该更新。
在完成当前所有可用的 game_chat_state_change 对象处理后,应通过调用 chat_manager::finish_processing_state_changes() 将数组传回 Game Chat 2 以释放资源。
这在以下示例中显示。
chat_manager::remove_user() 会立即使与用户对象关联的内存无效,并且状态更改可能包含指向用户对象的指针,因此在处理状态更改时不得调用 chat_manager::remove_user()。
文本聊天
要发送文本聊天,请使用 chat_user::chat_user_local::send_chat_text()。 这在以下示例中显示。无障碍功能
无障碍功能要求支持文本聊天输入和显示。 需要文本输入是因为,即使在历史上没有广泛使用物理键盘的平台或游戏类型,用户也可以配置系统以使用文本转语音辅助技术。 同样,需要文本显示是因为用户可以配置系统以使用语音转文本。 可以通过分别调用 chat_user::chat_user_local::text_to_speech_conversion_preference_enabled() 和 chat_user::chat_user_local::speech_to_text_conversion_preference_enabled() 方法检测本地用户的这些偏好。我们建议您根据用户偏好有条件地启用文本。文本转语音
当用户启用了文本转语音时,chat_user::chat_user_local::text_to_speech_conversion_preference_enabled() 返回true。检测到此状态时,应用必须提供文本输入方法。
获得由真实或虚拟键盘提供的文本输入后,将字符串传递给 chat_user::chat_user_local::synthesize_text_to_speech() 方法。Game Chat 2 根据字符串和用户的无障碍语音偏好检测和合成音频数据。
这在以下示例中显示。
chat_user::chat_user_local::synthesize_text_to_speech(),则 Game Chat 2 不采取任何操作。
语音转文本
当用户启用了语音转文本时,chat_user::chat_user_local::speech_to_text_conversion_preference_enabled() 返回true。检测到此状态时,应用必须准备提供与转录聊天消息相关的 UI。Game Chat 2 自动转录每个远程用户的音频,并通过 game_chat_transcribed_chat_received_state_change 结构公开。
语音转文本性能注意事项
启用语音转文本时,每个远程设备上的 Game Chat 2 实例与语音服务端点启动 WebSocket 连接。 每个远程 Game Chat 2 客户端通过此 WebSocket 将音频上传到语音服务端点。语音服务端点偶尔向远程设备返回转录消息。 然后远程设备将转录消息(即文本消息)发送到本地设备。转录的消息由 Game Chat 2 提供给应用进行渲染。 因此,语音转文本的主要性能成本是网络使用。 大部分网络流量是编码音频的上传。 WebSocket 上传的音频已经在 “正常” 语音聊天路径中由 Game Chat 2 编码。应用通过 chat_manager::set_audio_encoding_bitrate 控制比特率。UI
我们建议在向用户显示 UI 的任何地方,特别是玩家标签列表(例如记分牌),您还显示静音/说话图标作为用户的反馈。 这是通过调用 chat_user::chat_indicator() 检索表示该用户当前瞬时聊天状态的 game_chat_user_chat_indicator 枚举来完成的。以下示例演示了检索由chatUserA 变量指向的 chat_user 对象的指示器值,以确定要分配给 iconToShow 变量的特定图标常量值。
静音
chat_user::chat_user_local::set_microphone_muted() 方法可用于切换本地用户麦克风的静音状态。当麦克风被静音时,不会捕获该麦克风的任何音频。如果用户在共享设备上(例如 Kinect),静音状态适用于所有用户。 chat_user::chat_user_local::microphone_muted() 方法可用于检索本地用户麦克风的静音状态。此方法仅反映本地用户的麦克风是否已在软件中通过调用chat_user::chat_user_local::set_microphone_muted() 静音。此方法不反映由硬件控制的静音,例如通过用户耳机上的按钮。
无法通过 Game Chat 2 检索用户音频设备的硬件静音状态。
chat_user::chat_user_local::set_remote_user_muted() 方法可用于切换特定本地用户对远程用户的静音状态。当远程用户被静音时,本地用户将不会听到来自远程用户的任何音频或收到任何文本消息。
不良信誉自动静音
通常,远程用户以未静音状态开始。 Game Chat 2 在以下情况下将用户设置为静音状态:- 远程用户不是本地用户的好友。
- 远程用户具有不良信誉标志。
chat_user::chat_indicator() 返回 game_chat_user_chat_indicator::reputation_restricted。
此状态在第一次调用 chat_user::chat_user_local::set_remote_user_muted() 时被覆盖,其中远程用户作为目标用户包含在内。
特权和隐私
除了游戏配置的通信关系外,Game Chat 2 还强制执行特权和隐私限制。 Game Chat 2 在首次添加用户时执行特权和隐私限制查找。用户的chat_user::chat_indicator() 始终返回 game_chat_user_chat_indicator::silent,直到这些操作完成。
如果与用户的通信受到特权或隐私限制的影响,则用户的 chat_user::chat_indicator() 返回 game_chat_user_chat_indicator::platform_restricted。
平台通信限制适用于语音和文本聊天。永远不会出现文本聊天被平台限制阻止但语音聊天未被阻止的情况,反之亦然。
chat_user::chat_user_local::get_effective_communication_relationship() 可用于帮助区分由于不完整的特权和隐私操作而导致用户无法通信的情况。
它以 game_chat_communication_relationship_flags 的形式返回 Game Chat 2 强制执行的通信关系,并以 game_chat_communication_relationship_adjuster 枚举的形式提供该关系可能不等于已配置关系的原因。
例如,如果查找操作仍在进行中,则 game_chat_communication_relationship_adjuster 将为 game_chat_communication_relationship_adjuster::initializing。
此方法不应用于影响 UI。(有关更多信息,请参见本主题前面的 UI 部分。)
如果 Game Chat 2 遇到特权问题,它将在 communication_relationship_adjuster_changed 状态更改中报告。
如果 Game Chat 2 因不可恢复的原因未能检索用户的特权,它将报告为 game_chat_communication_relationship_adjuster::privilege_check_failure 调整程序。
如果 Game Chat 2 因用户可能能够解决的原因未能检索用户的特权,它将报告为 game_chat_communication_relationship_adjuster::resolve_user_issue 调整程序。
如果用户缺少可以通过 UI 解决的特权,它将报告为 game_chat_communication_relationship_adjuster::privilege 调整程序。
在这些情况下,通信将受到限制。
以下是如何检查用户是否具有以下常见问题之一的示例。
- 用户需要同意 XBOX 服务才能让 Game Chat 2 检查特权。
- 用户的账户配置为拒绝特权(例如,儿童账户因此无法使用聊天)。
game_chat_communication_relationship_adjuster::privilege 调整程序报告的问题,您可以使用 XUserPrivilegeOptions::None 和 XUserPrivilege::Communications 调用 XUserResolvePrivilegeWithUiAsync 以尝试解决问题。
对于以 game_chat_communication_relationship_adjuster::resolve_user_issue 调整程序报告的问题,您可以为 URL 传递 nullptr 调用 XUserResolveIssueWithUiAsync 以尝试解决问题。
我们建议您显示 UI 以指示存在特权问题。让用户决定是否要尝试解决问题,可以通过按下按钮或菜单选项。
用户可能无法或不愿意解决问题。
如果用户确实解决了问题,下一次将用户添加到 Game Chat 2 时将生效。
处理状态更改时(即在调用 chat_manager::start_processing_state_changes() 之后和对应的 chat_manager::finish_processing_state_changes() 调用之前)不得调用 chat_manager::remove_user()。在处理状态更改时调用
chat_manager::remove_user() 可能会使与已删除用户关联的内存无效。
如果您看到 game_chat_communication_relationship_adjuster::privilege 调整程序并想尝试解决用户特权,应等待状态更改处理完毕后再尝试。XUserHandle(调用 XUserResolvePrivilegeWithUiAsync 需要),您可以使用 XUserFindUserById API 获取新的 XUserHandle。或者,您可以保留通过 XUserAddAsync 获取的那个,并跟踪哪个 XUID 映射到它。
以下是如何解决这些问题的示例。
清理
当应用不再需要通过 Game Chat 2 进行通信时,您应调用 chat_manager::cleanup()。 这允许 Game Chat 2 回收分配给管理通信的资源。失败模型
Game Chat 2 实现不会将异常作为非致命错误报告手段抛出。如果您愿意,可以轻松从无异常项目中使用它。 但是,Game Chat 2 会抛出异常以通知您有关致命错误的信息。 这些错误是 API 误用的结果,例如在初始化 Game Chat 实例之前将用户添加到 Game Chat 实例,或在从 Game Chat 2 实例中删除后访问用户对象。 预期这些错误将在开发早期被捕获,并可通过修改与 Game Chat 2 交互的模式来纠正。 发生此类错误时,在引发异常之前会向调试器打印有关导致错误的原因的提示。如何配置常见场景
按住说话
按住说话应使用 chat_user::chat_user_local::set_microphone_muted() 实现。 调用set_microphone_muted(false) 允许讲话,调用 set_microphone_muted(true) 限制讲话。
此方法提供 Game Chat 2 的最低延迟响应。
