Memory Game デモ

Memoryデモは非同期的にプレイできるシンプルな2プレイヤーゲームのデモです。マッチメイキング、「保存されたゲーム」、ターンの順番、対戦相手が非アクティブな場合のプッシュ通知などについて説明します。

Memory Main Menu
Memory メインメニュー

クライアントはUnity SDKを使用します。

サーバーサイドには、参考用に非同期ゲームが実装されています。

これはダッシュボードで有効化できます。 ソースはGitHubから入手できます。

クライアントの準備

MemoryDemoSceneを読み込み、EditorにAppIDを入力すれば、クライアントを実行するための準備は完了します。

無料サインアップ後に、RealtimeダッシュボードからアプリケーションのAppIDを取得してください。

ダッシュボードからAppIDをコピーして、MemoryDemoSceneを開いてください。 「スクリプト」GameObjectとMemoryGuiコンポーネントで、 「AppID」を設定してください。

MemoryGui Component
MemoryGui コンポーネント

サーバーの準備

Realtimeダッシュボードから、「管理」をクリックしてアプリケーションの管理ページへ進んでください。

Manage Application
Realtimeダッシュボード上の「MemoryDemo」アプリケーション例

最下部のWebhookセクションにある「新しいWebhookを作成」ボタンで、新しいWebhook設定を作成します。

Webhooks section
Webhookが設定されていないWebhookセクション

ドロップダウンリストから、「WebHooks 1.2デモ」を選択します。 独自のウェブサービスが準備できている場合には、「WebHooks 1.2」を選択することもできます。 このデモでは、関連するパラメータのみを以下のスクリーンショットで表示しています。

スクリーンショットに表示されるBaseUrlはデフォルトなので、独自のカスタムURLに変更してください。

Webhookの詳細については、 このページを参照してください。

Webhooks example setup
MemoryDemo用のWebhook設定例

コードの参照

Memoryデモを最大限活用するにはコードを参照してください。

Unity Inspector の設定はビジュアルやオプションを定義しますが、ロジックはコードで処理されます。

このデモで確認すべきクラスはMemoryGuiMemoryBoardMemoryGameClientNamePickerGuiです。

認証

ターンベースゲームでは、ユーザーが重要な役割を果たします:

ユーザ毎にゲームリストを残す必要があります。そのリストにはuserIDでアクセスするのが理想的です。

簡潔にするため、このデモではパスワードを必要としません。何を入力してもuserIDとして受け入れます。

実際にユーザーアカウントの認証をおこなうには、Photonのカスタム認証機能を使用できます。 詳細は カスタム認証ドキュメントを参照してください。

参照用に、パッケージには2番目のシーン「CustomAuthDemo」が含まれています。 このシーンでは、クライアントサイドに認証を実装する方法を説明します。

マッチメイキング

このデモは、簡潔なマッチメイキングのワークフローを使用しています: ランダムなルームへの参加を試みて、失敗した場合には新たなルームを作成します。

詳細情報は マッチメイキングとロビーの参照ドキュメントを参照してください。

マッチメイキングはルームに少なくとも1人のプレイヤーが参加していないと機能しません。

プレイヤー1人ではできることが限られているため、プレイヤーが1人の場合にはルームのマッチメイキングにかかる時間は短くなります。

マッチメイキングをおこなうにはguiエレメントが NewGameMsg()を呼び、順番にOpJoinRandomRoom()が呼ばれます。 他のオペレーションへのレスポンスと同様に、このコールへのレスポンスがOnOperationResponseを呼びます。 必要に応じて、MemoryGameClientMemoryGameClient.CreateTurnbasedRoom()で実際のルーム作成コードを呼びます。

ステートの保存

明示的に放棄されたゲームでない限り、後から継続することができます

これはゲーム中にクライアントがクローズされた場合や切断された場合を含みます。

Photon Realtimeはバッファされたイベント、およびルームとプレイヤーのすべてのプロパティを自動的に保存します。

プレイヤーがルームに再度参加すると、アクティブおよび非アクティブなプレイヤーのプロパティとバッファされたイベントを取得します。 このデータを使ってプレイヤーはステートの再現をおこない、プレイを続行できます。

Memoryゲームでは、ボード上のタイルの履歴を保持する必要があります。

Memory Board
Memoryボード

キャッシュされたイベント

ルーム内のアクティブなプレイヤーに対してイベントを送るだけでなく、参加または「復帰」するプレイヤー向けに意図的にイベントをキャッシュすることもできます。 ルームは到着した順にイベントをキャッシュし続け、参加するプレイヤーが「ライブ」イベントを取得する前に、キャッシュしたイベントを送信します。

Photon Realtimeではキャッシュされたイベントも保存され、プレイヤーが後でゲームを続行するたびに送信されます。

このデモでは、キャッシュされたイベントは使用しませんが、以下にOpRaiseEventコールの例を示します:

public void CachedRaiseEvent(byte evCode, object content)
{
    // the content can be anything Photon-serializable. Most often a Hashtable is used.
    RaiseEventOptions options = new RaiseEventOptions() { CachingOption = EventCaching.AddToRoomCache };
    this.OpRaiseEvent(evCode, content, true, options);  // cached events should be sent reliable
}

通常どおり、ターンベースゲームで保存されたイベントはOnEvent()を呼んでコードにパスされます。

ターンの履歴を長く残す必要がない場合(再生用)、使用されていないイベントのキャッシュをクリアしてゲームの読み込みを加速する必要があります。 少し分かりにくいのですが、これはOpRaiseEventでおこないます。この場合、CachingOptionをEventCaching.RemoveFromRoomCacheに設定する点に留意してください。

プロパティ

Memoryゲームはゲーム全体のステートをルームプロパティのセットとして保存します。 変化が少ない値で、最新の値のみを保存する必要がある場合にはプロパティが最適です。

もっとも最適な方法は、ルームプロパティの一部を選択し、「保存されたゲーム」リストで利用可能にすることです。

Photon Realtimeでは、すべてのプレイヤーがインアクティブになるとすべてのプロパティが保存されます。 このため、ダッシュボードで "IsPersistent"を"true"に設定する必要があります。

デモでは、個々のタイルのプロパティ内でタイルIDをストリングキーとし、各タイルを保存します。 このようにして、各タイルの個々のアップデートが可能になります。 ただし、デモでは各ターン後に常にゲームステート全体を保存します。

Memoryゲームの主要な点は、他のプレイヤーが裏返したタイルが見えるということです。 (少なくともタイルが一致しなかった場合)

この機能を実現するため、プレイヤーは完了後にターンを受け渡しません。 その代わりに裏返したタイルは両方とも保存され、ロジックによって他のタイルを選択できなくなります。 対戦相手は準備が出来次第、タイルを見せてターンを「引き継ぐ」必要があります。

スコアやターンカウンターも、保存されるプロパティの一部です。

MemoryGameClient.csSaveBoardToPropertiesLoadBoardFromPropertiesをご確認ください。

保存されたゲーム

Photon Realtimeは各ルームのステートを保存するだけでなく、ユーザーごとのゲームリストも保存します。

ユーザーが決まったアカウントでログインした場合(すなわち、同じUserId)には、ユーザーが別のデバイスに切替えた場合でもこのリストは利用可能です。

 保存されたゲームの取得

クライアントは、保存されたゲームのリストにWebRPCコールでアクセスします。

WebRPCは、固有のウェブサービスで実行される単純なオペレーションです。

WebRPCはクライアントサイドから簡単に呼ぶことができます:

    Dictionary<string, object>() jsonParameters = new Dictionary<string, object>();
    this.GameClientInstance.OpWebRpc("GetGameList", jsonParameters);

PhotonはBaseUrl configured for Webhooksを使用し、最後に「GetGameList」を追加します。 もちろん「Base Path」として設定されたサーバーは、このパスの下にプレイヤーごとのゲームリストを提供する必要があります。

この場合、辞書jsonParametersは不要ですが、必要な場合にはこの辞書でWebサーバにパラメータを渡す方法を参照できます。 Photonは、自動的にWebサービスに、ユーザーに関する情報(UserIDなど)を提供します。

保存されたゲームの返されたリストには、プレイヤーが再参加できるルームの名前が含まれています。

サーバーは、ユーザーがそのルームで使用していたactorNumberもルームごとに送信します。

最後に、ロビーで利用可能なルームプロパティのリストを提供している場合には、デフォルトの実装でそれらのルームプロパティもGetGameListに送信されます。

MemoryGameClientはそれらのプロパティをList<SaveGameInfo> SavedGamesに変換し、ルームリストの表示に使用します。

デモでは、ゲームごとにターンの順番や対戦相手を示すうえで適切な量のデータを送信します。 ここでは、実際のルーム名は重要ではありません。

注:送信するデータを最小限にして、プレイヤーの負担を減らしてください。

GetGameList WebRPCの詳細な説明は、こちらを参照してください。

保存したゲームを開く

  プレイヤーはゲームに「再参加する」ことで保存されたゲームに戻ることができます。 ルームに戻るには、ルームの名前を知っている必要があります。

この処理は、MemoryGui.LoadGameMsg()で実行されます。

    this.GameClientInstance.OpRejoinRoom(roomName);

プッシュ通知

通知に最適なPushWooshというサービスがあります。例として、弊社のSDKにはこのサービスのAndroid用クライアントライブラリが含まれています。

Photonでは、ゲームのマルチプレイ/ネットワーキングに焦点を当てています。 弊社は既存の機能は新たに開発はせず、必要に応じて他社のサービスを活用しています。 プッシュ通知は、その一例です。

概要として、Remote API(実際のプッシュを送る)がWebサービスに実装されます。 クライアントには必要に応じてプッシュをトリガーするなんらかの方法と、サーバーにプッシュ先を伝える方法のみが必要です。 クライアントは現在2つの「タグ」、すなわちUID2およびAppIDを設定するため、各ユーザーを個別にターゲットに設定することが可能です。 これらのタグによって、1つのアプリケーション内でユーザーが特定されます。

トリガーのプッシュには、オンデマンドで(ターンが終了する際に)SetPropertiesを「Webフォワード」とともに使用します。 SetPropertiesは新しいルームプロパティをWebサービスに転送し、「ターン」を調査します。 「ターン」には、プッシュを受けるべきプレイヤーのIDが含まれます。 Websサービスは関連するユーザーIDを検出し、タグ「UID2」とAppIDを使用して、指令されたプッシュを送信します。

タグを使用する代わりに、WebService内にユーザーごとに「PushWoosh Device ID」保管することもできます。 または、deviceIdを(プレイヤーのプロパティとして)各ルームに送信することも可能です。

もしくは、他の方法も考えられます。

拡張について

このデモにはまだ未完全な部分があります。 特に、以下の部分について調整が必要かもしれません・・・

  • このゲームは、Webhookが構成されていないことを検知できません。  通知を表示するよう設定することは可能です。
  • 切断された場合には、その通知が表示されません。
  • 他のプレイヤーがアクティブかどうか表示する必要があります。現状では、待機すべきかどうかプレイヤーは判断できないためです。
  • オンライン中のプレイヤーの人数を表示してください。
  • その他...

さあ、プレイを楽しみましょう!

 ドキュメントのトップへ戻る