> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Game Chat 2 C++ API の使用

> XBOX GDK 向け Game Chat 2 C++ API のウォークスルー。初期化、ユーザー追加、チャットチャネル構成、エラー処理パターンについて説明します。

このトピックでは、Game Chat 2 の C++ API を使用してゲームに音声およびテキストコミュニケーションを追加する方法を簡単に説明します。

## 前提条件

Game Chat 2 は、プロジェクトが GDK 用にセットアップされている必要があります。セットアップ方法の詳細については、[Microsoft Game Development Kit の使用開始](https://learn.microsoft.com/gaming/gdk/docs/services/gdk-dev/get-started/get-started-home) を参照してください。

Game Chat 2 をコンパイルするには、主要な *GameChat2.h* ヘッダーをインクルードする必要があります。
適切にリンクするために、プロジェクトは少なくとも 1 つのコンパイル単位に *GameChat2Impl.h* も含める必要があります (これらのスタブ関数の実装は小さくコンパイラが "インライン" として生成しやすいため、共通のプリコンパイル済みヘッダーをお勧めします)。

Game Chat 2 インターフェースは、C++/CX または従来の C++ のどちらでコンパイルするかをプロジェクトに選択させる必要はありません。どちらでも使用できます。実装では、非致命的なエラー報告の手段として例外をスローすることもありません。例外を使用しないプロジェクトから簡単に利用できます。ただし、実装は致命的なエラー報告の手段として例外をスローします (詳細については、このトピックの後半にある [失敗モデル](/services/xbox-services/multiplayer/chat/game-chat2/using-game-chat-2#FAILURE_MODEL) セクションを参照してください)。

## 初期化

シングルトンの初期化のライフタイムに適用されるパラメーターで Game Chat 2 シングルトンインスタンスを初期化することによって、ライブラリの操作を開始します。シングルトンインスタンスは、次に示すように [chat\_manager::initialize](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_initialize) を呼び出して初期化されます。

```cpp theme={null}
chat_manager::singleton_instance().initialize(...);
```

<Note>`RegisterAppStateChangeNotification` を介してサスペンドおよびレジュームイベントを登録する必要があります。サスペンド時には、[chat\_manager::cleanup()](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_cleanup) で Game Chat 2 をクリーンアップする必要があります。レジューム時には、Game Chat 2 を再初期化する必要があります。サスペンド/レジュームサイクルをまたいで使用しようとするとクラッシュする可能性があります。</Note>

<a id="configuring_users" />

## ユーザーの構成

### Microsoft Game Development Kit (GDK) タイトルへのユーザーの追加

Game Chat 2 インスタンスにユーザーを追加する前に、そのユーザーが GDK タイトルに追加されていることを確認してください。
これは [XUserAddAsync API](/reference/system/xuser/functions/xuseraddasync) を使用して行います。この API の使用の詳細については、[ユーザー ID と XUser](/build/core-features/common/user/player-identity-xuser) を参照してください。

Game Chat 2 に追加したいユーザーの `XUserHandle` を取得した後、[XUserGetId API](/reference/system/xuser/functions/xusergetid) を使用してユーザーの XBOX ユーザー ID (XUID) を取得する必要があります。
ユーザーはオンラインである必要があり、このステップにはユーザーの同意が必要です。

[XUserGetId](/reference/system/xuser/functions/xusergetid) は XUID を `uint64_t` として提供します。Game Chat 2 で使用するために XUID を `std::wstring` に変換する必要があります。

以下は、`XUserHandle` を取得した後に Game Chat 2 にユーザーを追加する方法を示すコード例です。
<Note>[XUserResolveIssueWithUiAsync](/reference/system/xuser/functions/xuserresolveissuewithuiasync) を呼び出すとシステムダイアログボックスが表示されることに注意してください。</Note>

```cpp theme={null}
HRESULT
AddChatUserFromXUserHandle(
    _In_ XUserHandle user,
    _In_ XTaskQueueHandle queueHandle,
    _Outptr_result_maybenull_ Xs::game_chat_2::chat_user** chatUser
    )
{
    *chatUser = nullptr;
    uint64_t xuid;
    HRESULT hr = XUserGetId(user, &xuid);
    if (hr == E_GAMEUSER_RESOLVE_USER_ISSUE_REQUIRED)
    {
        XAsyncBlock* asyncBlock = new (std::nothrow) XAsyncBlock;
        if (asyncBlock != nullptr)
        {
            ZeroMemory(asyncBlock, sizeof(*asyncBlock));
            asyncBlock->queue = queueHandle;
            hr = XUserResolveIssueWithUiAsync(user, nullptr, asyncBlock);
            if (SUCCEEDED(hr))
            {
                hr = XAsyncGetStatus(asyncBlock, true);
                if (SUCCEEDED(hr))
                {
                    hr = XUserGetId(user, &xuid);
                }
            }
            delete asyncBlock;
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if (SUCCEEDED(hr))
    {
        try
        {
            std::wstring xuidString = std::to_wstring(xuid);

            // If the user has already been added, this will return the existing user.
            *chatUser = Xs::game_chat_2::chat_manager::singleton_instance().add_local_user(xuidString.c_str());
        }
        catch (const std::bad_alloc&)
        {
            hr = E_OUTOFMEMORY;
        }
    }

    return hr;
}
```

<a id="adding_users_to_game_chat_2" />

### Game Chat 2 へのユーザーの追加

インスタンスが初期化された後、[chat\_manager::add\_local\_user](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_add_local_user) を使用して、Game Chat 2 インスタンスにローカルユーザーを追加する必要があります。この例では、ユーザー A がローカルユーザーを表しています。

```cpp theme={null}
chat_user* chatUserA = chat_manager::singleton_instance().add_local_user(<user_a_xuid>);
```

次に、リモートユーザーと、そのユーザーがいるリモートの "エンドポイント" を表すために使用される識別子を追加します。
*エンドポイント* は、リモートデバイス上で実行されているアプリのインスタンスです。

この例では、ユーザー B はエンドポイント X 上にいます。ユーザー C と D はエンドポイント Y 上にいます。
エンドポイント X には任意に識別子 "1" が割り当てられています。エンドポイント Y には任意に識別子 "2" が割り当てられています。

次の呼び出しで、リモートユーザーを Game Chat 2 に通知します。

```cpp theme={null}
chat_user* chatUserB = chat_manager::singleton_instance().add_remote_user(<user_b_xuid>, 1);
chat_user* chatUserC = chat_manager::singleton_instance().add_remote_user(<user_c_xuid>, 2);
chat_user* chatUserD = chat_manager::singleton_instance().add_remote_user(<user_d_xuid>, 2);
```

次に、各リモートユーザーとローカルユーザー間のコミュニケーション関係を構成します。
この例では、ユーザー A とユーザー B が同じチームにいると仮定します。双方向コミュニケーションが許可されます。
`c_communicationRelationshipSendAndReceiveAll` は、双方向コミュニケーションを表すために *GameChat2.h* で定義されている定数です。

[chat\_user\_local::set\_communication\_relationship](/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_set_communication_relationship) を使用して、ユーザー A のユーザー B に対する関係を設定します。

```cpp theme={null}
chatUserA->local()->set_communication_relationship(chatUserB, c_communicationRelationshipSendAndReceiveAll);
```

ユーザー C と D が "観戦者" で、ユーザー A の話を聞くことは許可されるが、話すことは許可されないと仮定します。
`c_communicationRelationshipSendAll` は、この単方向コミュニケーションを表すために *GameChat2.h* で定義されている定数です。

次のように関係を設定します。

```cpp theme={null}
chatUserA->local()->set_communication_relationship(chatUserC, c_communicationRelationshipSendAll);
chatUserA->local()->set_communication_relationship(chatUserD, c_communicationRelationshipSendAll);
```

4 人のローカルユーザーすべての関係設定の例については、このトピックの後半にある [シナリオ](/services/xbox-services/multiplayer/chat/game-chat2/using-game-chat-2#scenarios) セクションを参照してください。

シングルトンインスタンスに追加されているが、どのローカルユーザーともコミュニケーションを行うように構成されていないリモートユーザーがいる場合、それは問題ありません。
これは、ユーザーがチームを決めているシナリオや、話すチャンネルを任意に変更できるシナリオで想定されます。

Game Chat 2 は、インスタンスに追加されたユーザーの情報 (たとえば、プライバシー関係と評判) のみをキャッシュするため、特定の時点でどのローカルユーザーとも話せないユーザーであっても、すべての可能性のあるユーザーを Game Chat 2 に通知しておくと便利です。

最後に、ユーザー D がゲームを離れ、ローカル Game Chat 2 インスタンスから削除する必要があると仮定します。
これは次のように、[chat\_manager::remove\_user](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_remove_user) を使用して行うことができます。

```cpp theme={null}
chat_manager::singleton_instance().remove_user(chatUserD);
```

`chat_manager::remove_user()` を呼び出すと、ユーザーオブジェクトが無効になる可能性があります。[リアルタイム音声操作](/services/xbox-services/multiplayer/chat/game-chat2/real-time-audio-manipulation) を使用している場合、詳細については [チャットユーザーのライフタイム](/services/xbox-services/multiplayer/chat/game-chat2/real-time-audio-manipulation#chat-user-lifetimes) を参照してください。それ以外の場合、`chat_manager::remove_user()` が呼び出されるとすぐにユーザーオブジェクトが無効になります。ユーザーを削除できるタイミングに関する微妙な制限については、このトピックの後半にある [状態変化の処理](#processing-state-changes) セクションで説明されています。

<a id="processing_data_frames" />

## データフレームの処理

Game Chat 2 は独自のトランスポート層を持ちません。トランスポート層はアプリが提供する必要があります。
このプラグインは、アプリが定期的に頻繁に [chat\_manager::start\_processing\_data\_frames()](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_start_processing_data_frames) および [chat\_manager::finish\_processing\_data\_frames()](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_finish_processing_data_frames) の一対のメソッドを呼び出すことで管理されます。これらのメソッドは、Game Chat 2 が送信データをアプリに提供する方法です。

これらのメソッドは高速に動作するように設計されています。専用のネットワーキングスレッドで頻繁にポーリングできます。
これにより、ネットワークタイミングの予測不可能性やマルチスレッドコールバックの複雑さを心配することなく、キューに入っているすべてのデータを取得できる便利な場所が提供されます。

`chat_manager::start_processing_data_frames()` が呼び出されると、キューに入っているすべてのデータが [game\_chat\_data\_frame](/reference/chat/gamechat2/structs/game_chat_data_frame) 構造体ポインターの配列で報告されます。
アプリは配列を反復処理し、ターゲットエンドポイントを検査し、アプリのネットワーキング層を使用して適切なリモートアプリインスタンスにデータを配信する必要があります。

配列内のすべての [game\_chat\_data\_frame](/reference/chat/gamechat2/structs/game_chat_data_frame) 構造体の処理が完了した後、配列は `chat_manager:finish_processing_data_frames()` を呼び出してリソースを解放するために Game Chat 2 に戻す必要があります。
これは次の例に示されています。

```cpp theme={null}
uint32_t dataFrameCount;
game_chat_data_frame_array dataFrames;
chat_manager::singleton_instance().start_processing_data_frames(&dataFrameCount, &dataFrames);
for (uint32_t dataFrameIndex = 0; dataFrameIndex < dataFrameCount; ++dataFrameIndex)
{
    game_chat_data_frame const* dataFrame = dataFrames[dataFrameIndex];

    // Title-written function responsible for sending packet to remote instances of GameChat 2.
    HandleOutgoingDataFrame(
        dataFrame->packet_byte_count,
        dataFrame->packet_buffer,
        dataFrame->target_endpoint_identifier_count,
        dataFrame->target_endpoint_identifiers,
        dataFrame->transport_requirement
        );
}
chat_manager::singleton_instance().finish_processing_data_frames(dataFrames);
```

データフレームが処理される頻度が高いほど、ユーザーが感じる音声レイテンシは低くなります。
音声は 40 ms のデータフレームに結合されます。これが推奨されるポーリング期間です。

<a id="processing-state-changes" />

## 状態変化の処理

Game Chat 2 は、受信したテキストメッセージなどの更新をアプリに提供します。これは、アプリが定期的に頻繁に [chat\_manager::start\_processing\_state\_changes()](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_start_processing_state_changes) および [chat\_manager::finish\_processing\_state\_changes()](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_finish_processing_state_changes) の一対のメソッドを呼び出すことで行われます。
これらのメソッドは高速に動作するため、UI レンダリングループの各グラフィックスフレームで呼び出すことができます。
これにより、ネットワークタイミングの予測不可能性やマルチスレッドコールバックの複雑さを心配することなく、キューに入っているすべての変化を取得できる便利な場所が提供されます。

`chat_manager::start_processing_state_changes()` が呼び出されると、キューに入っているすべての更新が [game\_chat\_state\_change](/reference/chat/gamechat2/structs/game_chat_state_change) 構造体ポインターの配列で報告されます。
アプリは配列を反復処理し、より具体的な型を確認するために基本構造体を検査し、基本構造体を対応するより詳細な型にキャストし、その更新を適切に処理する必要があります。

配列内の現在利用可能なすべての [game\_chat\_state\_change](/reference/chat/gamechat2/structs/game_chat_state_change) オブジェクトの処理が完了した後、配列は `chat_manager::finish_processing_state_changes()` を呼び出してリソースを解放するために Game Chat 2 に戻す必要があります。
これは次の例に示されています。

```cpp theme={null}
uint32_t stateChangeCount;
game_chat_state_change_array gameChatStateChanges;
chat_manager::singleton_instance().start_processing_state_changes(&stateChangeCount, &gameChatStateChanges);

std::list<Xs::game_chat_2::chat_user*> usersWithPrivilegeIssues;
std::list<Xs::game_chat_2::chat_user*> usersWithPrivilegeCheckIssues;
for (uint32_t stateChangeIndex = 0; stateChangeIndex < stateChangeCount; ++stateChangeIndex)
{
    switch (gameChatStateChanges[stateChangeIndex]->state_change_type)
    {
        case game_chat_state_change_type::text_chat_received:
        {
            HandleTextChatReceived(static_cast<const game_chat_text_chat_received_state_change*>(gameChatStateChanges[stateChangeIndex]));
            break;
        }

        case Xs::game_chat_2::game_chat_state_change_type::transcribed_chat_received:
        {
            HandleTranscribedChatReceived(static_cast<const Xs::game_chat_2::game_chat_transcribed_chat_received_state_change*>(gameChatStateChanges[stateChangeIndex]));
            break;
        }
        case Xs::game_chat_2::game_chat_state_change_type::communication_relationship_adjuster_changed:
        {
            HandleAdjusterChangedStateReceived(static_cast<const Xs::game_chat_2::game_chat_communication_relationship_adjuster_changed_state_change*>(gameChatStateChanges[stateChangeIndex]), usersWithPrivilegeIssues, usersWithPrivilegeCheckIssues);
            break;
        }

        ...
    }
}
chat_manager::singleton_instance().finish_processing_state_changes(gameChatStateChanges);
```

`chat_manager::remove_user()` はユーザーオブジェクトに関連付けられたメモリを直ちに無効にし、状態変化にはユーザーオブジェクトへのポインターが含まれる可能性があるため、状態変化を処理中に `chat_manager::remove_user()` を呼び出してはなりません。

## テキストチャット

テキストチャットを送信するには、[chat\_user::chat\_user\_local::send\_chat\_text()](/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_send_chat_text) を使用します。
これは次の例に示されています。

```cpp theme={null}
chatUserA->local()->send_chat_text(L"Hello");
```

Game Chat 2 は、このメッセージを含むデータフレームを生成します。データフレームのターゲットエンドポイントは、ローカルユーザーからテキストを受信するように構成されているユーザーに関連付けられているものです。
リモートエンドポイントによってデータが処理されると、メッセージは [game\_chat\_text\_chat\_received\_state\_change](/reference/chat/gamechat2/structs/game_chat_text_chat_received_state_change) を介して公開されます。

音声チャットと同様に、テキストチャットでも権限とプライバシー制限が尊重されます。
テキストチャットを許可するようにユーザーのペアが構成されていても、権限またはプライバシー制限によりそのコミュニケーションが許可されない場合、テキストメッセージはドロップされます。

## アクセシビリティ

アクセシビリティには、テキストチャットの入力と表示のサポートが必要です。

テキスト入力が必要なのは、物理キーボードが広く使用されていないプラットフォームやゲームジャンルでも、ユーザーがテキスト読み上げ支援技術を使用するようにシステムを構成できるためです。

同様に、テキスト表示が必要なのは、ユーザーが音声認識技術を使用するようにシステムを構成できるためです。

これらの設定は、ローカルユーザーに対してそれぞれ [chat\_user::chat\_user\_local::text\_to\_speech\_conversion\_preference\_enabled()](/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_text_to_speech_conversion_preference_enabled) および [chat\_user::chat\_user\_local::speech\_to\_text\_conversion\_preference\_enabled()](/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_speech_to_text_conversion_preference_enabled) メソッドを呼び出して検出できます。ユーザー設定に基づいて条件付きでテキストを有効にすることをお勧めします。

### テキスト読み上げ

ユーザーがテキスト読み上げを有効にしている場合、[chat\_user::chat\_user\_local::text\_to\_speech\_conversion\_preference\_enabled()](/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_text_to_speech_conversion_preference_enabled) は `true` を返します。この状態が検出された場合、アプリはテキスト入力の方法を提供する必要があります。

実際または仮想のキーボードから提供されたテキスト入力を取得した後、文字列を [chat\_user::chat\_user\_local::synthesize\_text\_to\_speech()](/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_synthesize_text_to_speech) メソッドに渡します。Game Chat 2 は、文字列とユーザーのアクセシビリティ音声設定に基づいて音声データを検出および合成します。
これは次の例に示されています。

```cpp theme={null}
chat_userA->local()->synthesize_text_to_speech(L"Hello");
```

この操作の一部として合成された音声は、このローカルユーザーから音声を受信するように構成されているすべてのユーザーに送信されます。
`chat_user::chat_user_local::synthesize_text_to_speech()` がテキスト読み上げを有効にしていないユーザーで呼び出された場合、Game Chat 2 は何のアクションも実行しません。

### 音声認識

ユーザーが音声認識を有効にしている場合、[chat\_user::chat\_user\_local::speech\_to\_text\_conversion\_preference\_enabled()](/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_speech_to_text_conversion_preference_enabled) は `true` を返します。この状態が検出された場合、アプリは文字起こしされたチャットメッセージに関連付けられた UI を提供する準備をする必要があります。Game Chat 2 は各リモートユーザーの音声を自動的に文字起こしし、[game\_chat\_transcribed\_chat\_received\_state\_change](/reference/chat/gamechat2/structs/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](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_set_audio_encoding_bitrate) を介してビットレートを制御できます。

<a id="ui" />

## UI

ユーザーに UI が表示される場所、特にスコアボードなどのゲーマータグのリストでは、ユーザーへのフィードバックとしてミュート/発話中のアイコンも表示することをお勧めします。
これは、[chat\_user::chat\_indicator()](/reference/chat/gamechat2/classes/chat_user/methods/chat_user_chat_indicator) を呼び出して、そのユーザーの現在の瞬間的なチャット状態を表す [game\_chat\_user\_chat\_indicator](/reference/chat/gamechat2/enums/game_chat_user_chat_indicator) 列挙を取得することによって行います。次の例は、`chatUserA` 変数が指す [chat\_user](/reference/chat/gamechat2/classes/chat_user/chat_user) オブジェクトのインジケーター値を取得して、`iconToShow` 変数に割り当てる特定のアイコン定数値を判定する方法を示しています。

```cpp theme={null}
switch (chatUserA->chat_indicator())
{
   case game_chat_user_chat_indicator::silent:
   {
       iconToShow = Icon_InactiveSpeaker;
       break;
   }

   case game_chat_user_chat_indicator::talking:
   {
       iconToShow = Icon_ActiveSpeaker;
       break;
   }

   case game_chat_user_chat_indicator::local_microphone_muted:
   {
       iconToShow = Icon_MutedSpeaker;
       break;
   }
   ...
}
```

[chat\_user::chat\_indicator()](/reference/chat/gamechat2/classes/chat_user/methods/chat_user_chat_indicator) によって報告される値は、たとえばプレイヤーが話し始めたり止めたりするたびに、頻繁に変化することが予想されます。
そのため、アプリが UI フレームごとにポーリングすることをサポートするように設計されています。

## ミュート

[chat\_user::chat\_user\_local::set\_microphone\_muted()](/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_set_microphone_muted) メソッドは、ローカルユーザーのマイクのミュート状態を切り替えるために使用できます。マイクがミュートされている場合、そのマイクからの音声はキャプチャされません。ユーザーが Kinect などの共有デバイスを使用している場合、ミュート状態はすべてのユーザーに適用されます。

[chat\_user::chat\_user\_local::microphone\_muted()](/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/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()](/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_set_remote_user_muted) メソッドは、特定のローカルユーザーに関するリモートユーザーのミュート状態を切り替えるために使用できます。リモートユーザーがミュートされている場合、ローカルユーザーはそのリモートユーザーからの音声を聞いたり、テキストメッセージを受信したりしません。

## 悪い評判の自動ミュート

通常、リモートユーザーはミュートされていない状態で開始します。
Game Chat 2 は、次の場合にユーザーをミュート状態で開始します。

1. リモートユーザーがローカルユーザーの友達ではない場合。
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()](/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_get_effective_communication_relationship) は、権限およびプライバシー操作が不完全であるためにユーザーがコミュニケーションできないタイミングを識別するのに役立ちます。
これは、Game Chat 2 によって強制されるコミュニケーション関係を [game\_chat\_communication\_relationship\_flags](/reference/chat/gamechat2/enums/game_chat_communication_relationship_flags) の形式で返し、関係が [game\_chat\_communication\_relationship\_adjuster](/reference/chat/gamechat2/enums/game_chat_communication_relationship_adjuster) 列挙の形式で構成された関係と等しくない可能性がある理由を返します。

たとえば、ルックアップ操作がまだ進行中の場合、[game\_chat\_communication\_relationship\_adjuster](/reference/chat/gamechat2/enums/game_chat_communication_relationship_adjuster) は `game_chat_communication_relationship_adjuster::initializing` になります。
このメソッドは UI に影響を与えるために使用すべきではありません (詳細については、このトピックの前半にある [UI](#ui) セクションを参照してください)。

Game Chat 2 が権限の問題に遭遇した場合、[communication\_relationship\_adjuster\_changed](/reference/chat/gamechat2/structs/game_chat_stream_state_change) 状態変化で報告されます。

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` アジャスターとして報告されます。

これらの場合、コミュニケーションは制限されます。

以下は、ユーザーが次の一般的な問題のいずれかを抱えているかどうかを確認する方法の例です。

1. Game Chat 2 が権限をチェックするために、ユーザーが XBOX services に同意する必要があります。
2. ユーザーのアカウントが権限を拒否するように構成されている (たとえば、子供アカウントで、チャットを使用できない)。

```cpp theme={null}
void
HandleAdjusterChangedStateReceived (
    _In_ const Xs::game_chat_2::game_chat_communication_relationship_adjuster_changed_state_change* adjusterChange,
    _Inout_ std::list<Xs::game_chat_2::chat_user*>& usersWithPrivilegeIssues,
    _Inout_ std::list<Xs::game_chat_2::chat_user*>& usersWithPrivilegeCheckIssues
    )
{
    Xs::game_chat_2::game_chat_communication_relationship_flags communicationRelationship;
    Xs::game_chat_2::game_chat_communication_relationship_adjuster communicationRelationshipAdjuster;
    adjusterChange->local_user->local()->get_effective_communication_relationship(
        adjusterChange->target_user,
        &communicationRelationship,
        &communicationRelationshipAdjuster);

    if (communicationRelationshipAdjuster == Xs::game_chat_2::game_chat_communication_relationship_adjuster::privilege)
    {
        // The local user has privilege issues.
        usersWithPrivilegeIssues.push_back(adjusterChange->local_user);
    }
    else if (communicationRelationshipAdjuster == Xs::game_chat_2::game_chat_communication_relationship_adjuster::resolve_user_issue)
    {
        // The local user has an issue checking privileges.
        usersWithPrivilegeCheckIssues.push_back(adjusterChange->local_user);
    }
}
```

`game_chat_communication_relationship_adjuster::privilege` アジャスターで報告される問題については、`XUserPrivilegeOptions::None` および `XUserPrivilege::Communications` を指定して [XUserResolvePrivilegeWithUiAsync](/reference/system/xuser/functions/xuserresolveprivilegewithuiasync) を呼び出し、問題の解決を試みることができます。

`game_chat_communication_relationship_adjuster::resolve_user_issue` アジャスターで報告される問題については、URL に `nullptr` を指定して [XUserResolveIssueWithUiAsync](/reference/system/xuser/functions/xuserresolveissuewithuiasync) を呼び出し、問題の解決を試みることができます。

権限の問題があることを示す UI を表示することをお勧めします。ユーザーがボタンを押すかメニューオプションで問題の解決を試みるかどうかを決定できるようにします。

ユーザーは、問題を解決できない、または解決したくない場合があります。
ユーザーが問題を解決した場合、その効果はユーザーが次回 Game Chat 2 に追加されるときに発揮されます。

<Note>[chat\_manager::remove\_user()](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_remove_user) は状態変化を処理中に呼び出してはなりません (つまり、[chat\_manager::start\_processing\_state\_changes()](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_start_processing_state_changes) が呼び出された後、対応する [chat\_manager::finish\_processing\_state\_changes()](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_finish_processing_state_changes) の呼び出しの前)。状態変化の処理中に `chat_manager::remove_user()` を呼び出すと、削除されたユーザーに関連付けられたメモリが無効になる可能性があります。
`game_chat_communication_relationship_adjuster::privilege` アジャスターが見られ、ユーザーの権限の解決を試みたい場合、状態変化の処理が終わるまで待ってから試みる必要があります。</Note>

`XUserResolvePrivilegeWithUiAsync` を呼び出すために必要な XUID から `XUserHandle` を取得するには、[XUserFindUserById](/reference/system/xuser/functions/xuserfinduserbyid) API を使用して新しい `XUserHandle` を取得できます。または、[XUserAddAsync](/reference/system/xuser/functions/xuseraddasync) で取得したものを保持し、どの XUID がそれにマップされているかを追跡することもできます。

以下は、これらの問題を解決する方法の例です。

```cpp theme={null}
// If we got an Xs::game_chat_2::game_chat_communication_relationship_adjuster::resolve_user_issue,
// we need to try and fix our issue, if we haven't already, and then remove and re-add that user.
for (Xs::game_chat_2::chat_user* localUser : usersWithPrivilegeCheckIssues)
{
    auto asyncBlock = std::make_unique<XAsyncBlock>();
    ZeroMemory(asyncBlock.get(), sizeof(*asyncBlock));
    asyncBlock->queue = g_asyncQueue;

    XUserHandle userHandle;
    hr = XUserFindUserById(localUser->local()->xbox_user_id(), &userHandle);
    if (SUCCEEDED(hr))
    {
        hr = XUserResolveIssueWithUiAsync(
            userHandle,
            nullptr,
            asyncBlock.get());
        if (SUCCEEDED(hr))
        {
            hr = XAsyncGetStatus(asyncBlock.get(), true);
            if (SUCCEEDED(hr))
            {
                // Remove and re-add the user after fixing the privileges.
                // Users must not be removed while processing state changes.
            }
            asyncBlock.release();
        }
    }
}

// If we got an Xs::game_chat_2::game_chat_communication_relationship_adjuster::resolve_user_privilege,
// we need to try and resolve the privileges, if we haven't already, and then remove and re-add that user.
for (Xs::game_chat_2::chat_user* localUser : usersWithPrivilegeIssues)
{
    auto asyncBlock = std::make_unique<XAsyncBlock>();
    ZeroMemory(asyncBlock.get(), sizeof(*asyncBlock));
    asyncBlock->queue = g_asyncQueue;

    XUserHandle userHandle;
    hr = XUserFindUserById(localUser->local()->xbox_user_id(), &userHandle);
    if (SUCCEEDED(hr))
    {
        hr = XUserResolvePrivilegeWithUiAsync(
            userHandle,
            XUserPrivilegeOptions::None,
            XUserPrivilege::Communications,
            asyncBlock.get());
        if (SUCCEEDED(hr))
        {
            hr = XAsyncGetStatus(asyncBlock.get(), true);
            if (SUCCEEDED(hr))
            {
                // Remove and re-add the user after fixing the privileges.
                // Users must not be removed while processing state changes.
            }
            asyncBlock.release();
        }
    }
}
```

## クリーンアップ

アプリが Game Chat 2 を介したコミュニケーションを不要になったとき、[chat\_manager::cleanup()](/reference/chat/gamechat2/classes/chat_manager/methods/chat_manager_cleanup) を呼び出す必要があります。
これにより、Game Chat 2 はコミュニケーションを管理するために割り当てられたリソースを回収できます。

<a id="FAILURE_MODEL" />

## 失敗モデル

Game Chat 2 の実装は、非致命的なエラー報告の手段として例外をスローしません。例外を使用しないプロジェクトから簡単に利用できます。
ただし、Game Chat 2 は致命的なエラーを通知するために例外をスローします。

これらのエラーは、インスタンスを初期化する前に Game Chat インスタンスにユーザーを追加したり、Game Chat 2 インスタンスから削除された後にユーザーオブジェクトにアクセスしたりするなど、API の誤用の結果です。

これらのエラーは開発の初期段階で捕捉されることが期待され、Game Chat 2 との対話に使用されるパターンを変更することで修正できます。
このようなエラーが発生した場合、例外が発生する前に、エラーの原因に関するヒントがデバッガーに出力されます。

<a id="scenarios" />

## 一般的なシナリオの構成方法

### プッシュ・トゥ・トーク

プッシュ・トゥ・トークは、[chat\_user::chat\_user\_local::set\_microphone\_muted()](/reference/chat/gamechat2/classes/chat_user/chat_user_local/methods/chat_user_local_set_microphone_muted) で実装する必要があります。
発話を許可するには `set_microphone_muted(false)` を、制限するには `set_microphone_muted(true)` を呼び出します。
このメソッドは、Game Chat 2 から最も低いレイテンシの応答を提供します。

### チーム

ユーザー A とユーザー B がチームブルーにおり、ユーザー C とユーザー D がチームレッドにいると仮定します。
各ユーザーはアプリの一意のインスタンスにいます。

ユーザー A のデバイス上:

```cpp theme={null}
chatUserA->local()->set_communication_relationship(chatUserB, c_communicationRelationshipSendAndReceiveAll);
chatUserA->local()->set_communication_relationship(chatUserC, game_chat_communication_relationship_flags::none);
chatUserA->local()->set_communication_relationship(chatUserD, game_chat_communication_relationship_flags::none);
```

ユーザー B のデバイス上:

```cpp theme={null}
chatUserB->local()->set_communication_relationship(chatUserA, c_communicationRelationshipSendAndReceiveAll);
chatUserB->local()->set_communication_relationship(chatUserC, game_chat_communication_relationship_flags::none);
chatUserB->local()->set_communication_relationship(chatUserD, game_chat_communication_relationship_flags::none);
```

ユーザー C のデバイス上:

```cpp theme={null}
chatUserC->local()->set_communication_relationship(chatUserA, game_chat_communication_relationship_flags::none);
chatUserC->local()->set_communication_relationship(chatUserB, game_chat_communication_relationship_flags::none);
chatUserC->local()->set_communication_relationship(chatUserD, c_communicationRelationshipSendAndReceiveAll);
```

ユーザー D のデバイス上:

```cpp theme={null}
chatUserD->local()->set_communication_relationship(chatUserA, game_chat_communication_relationship_flags::none);
chatUserD->local()->set_communication_relationship(chatUserB, game_chat_communication_relationship_flags::none);
chatUserD->local()->set_communication_relationship(chatUserC, c_communicationRelationshipSendAndReceiveAll);
```

### ブロードキャスト

ユーザー A がリーダーで、命令を出すと仮定します。ユーザー B、C、D は聞くことしかできません。
各プレイヤーは一意のデバイス上にいます。

ユーザー A のデバイス上:

```cpp theme={null}
chatUserA->local()->set_communication_relationship(chatUserB, c_communicationRelationshipSendAll);
chatUserA->local()->set_communication_relationship(chatUserC, c_communicationRelationshipSendAll);
chatUserA->local()->set_communication_relationship(chatUserD, c_communicationRelationshipSendAll);
```

ユーザー B のデバイス上:

```cpp theme={null}
chatUserB->local()->set_communication_relationship(chatUserA, c_communicationRelationshipReceiveAll);
chatUserB->local()->set_communication_relationship(chatUserC, game_chat_communication_relationship_flags::none);
chatUserB->local()->set_communication_relationship(chatUserD, game_chat_communication_relationship_flags::none);
```

ユーザー C のデバイス上:

```cpp theme={null}
chatUserC->local()->set_communication_relationship(chatUserA, c_communicationRelationshipReceiveAll);
chatUserC->local()->set_communication_relationship(chatUserB, game_chat_communication_relationship_flags::none);
chatUserC->local()->set_communication_relationship(chatUserD, game_chat_communication_relationship_flags::none);
```

ユーザー D のデバイス上:

```cpp theme={null}
chatUserD->local()->set_communication_relationship(chatUserA, c_communicationRelationshipReceiveAll);
chatUserD->local()->set_communication_relationship(chatUserB, game_chat_communication_relationship_flags::none);
chatUserD->local()->set_communication_relationship(chatUserC, game_chat_communication_relationship_flags::none);
```

## リファレンス API ドキュメント

* [Gamechat2 (API 内容)](/reference/chat/gamechat2/gamechat2_members)
  * 構造体
    * [game\_chat\_data\_frame](/reference/chat/gamechat2/structs/game_chat_data_frame)
    * [game\_chat\_state\_change](/reference/chat/gamechat2/structs/game_chat_state_change)
    * [game\_chat\_text\_chat\_received\_state\_change](/reference/chat/gamechat2/structs/game_chat_text_chat_received_state_change)
    * [game\_chat\_transcribed\_chat\_received\_state\_change](/reference/chat/gamechat2/structs/game_chat_transcribed_chat_received_state_change)
    * [communication\_relationship\_adjuster\_changed](/reference/chat/gamechat2/structs/game_chat_stream_state_change)
* [xuser (API 内容)](/reference/system/xuser/xuser_members)
  * 関数
    * [xuseraddasync](/reference/system/xuser/functions/xuseraddasync)
    * [xusergetid](/reference/system/xuser/functions/xusergetid)
    * [XUserResolveIssueWithUiAsync](/reference/system/xuser/functions/xuserresolveissuewithuiasync)
    * [XUserResolvePrivilegeWithUiAsync](/reference/system/xuser/functions/xuserresolveprivilegewithuiasync)
    * [XUserFindUserById](/reference/system/xuser/functions/xuserfinduserbyid)

## 関連項目

[Game Chat 2 の概要](/services/xbox-services/multiplayer/chat/game-chat2/game-chat-2-intro)

[リアルタイム音声操作](/services/xbox-services/multiplayer/chat/game-chat2/real-time-audio-manipulation)

[API 内容 (GameChat2)](/reference/chat/gamechat2/gamechat2_members)

[Microsoft Game Development Kit](/services/playfab/sdks/platforms/gdk)


## Related topics

- [Game Chat 2](/ja-jp/services/xbox-services/multiplayer/chat/game-chat2/index.md)
- [game_chat_text_chat_received_state_change](/ja-jp/reference/chat/gamechat2/structs/game_chat_text_chat_received_state_change.md)
- [game_chat_transcribed_chat_received_state_change](/ja-jp/reference/chat/gamechat2/structs/game_chat_transcribed_chat_received_state_change.md)
- [game_chat_data_frame](/ja-jp/reference/chat/gamechat2/structs/game_chat_data_frame.md)
- [game_chat_state_change](/ja-jp/reference/chat/gamechat2/structs/game_chat_state_change.md)
