こちらは、2014年9月18日に公開された(2019年3月27日に更新)以下のドキュメントを翻訳したものとなります。
Dev Story: Das Tal’s Backend & Exit Games’ Photon
この記事は、インディーズスタジオ、Fairytale Distilleryによる「Das Tal」のテクニカルディレクターおよび開発者であるセバスチャン・ドルダ(Sebastian Dorda)氏に投稿いただいたゲストポストとなります。
「Das Tal」は、プレイヤーのインタラクションに焦点を当てたハイペースなサンドボックスMMORPGです。勝者が敗者のインベントリをすべて奪うことができる「Full Loot」やオープンPvPにより、プレイヤーは仲間を増やす前に「リスク対報酬」について考えるようになります。PvPはいつでも可能ですが、協力プレイには必ず報酬が与えられます。各ワールドには固有の特性と地理があり、その特性は自分で選ぶことが出来ます!
タイムボックス化されたサーバーにより勝者と敗者が決まり、規則的に新規ゲームを開始することができます。また、クラスレスなキャラクターシステムを使用しているので、プレイスタイルに合わせてキャラクターを形作ることができます。プレイヤーは装備や建物を作るために必要な原料を求めて戦います。
PvP対戦は完全にスキルベース、マッチはすぐに見つかります。小規模な戦闘も大規模な攻城戦も、すべてのプレイヤーがいつでもプレイできます。
「Das Tal」のアルファテスターに是非ご登録ください。
今回のトピックはバックエンドです。まずインフラストラクチャの概要を説明して、その後にPhoton Server上のゲームサーバーについてより詳しくお話します。
これは一つの方法に過ぎません。ご意見またはコメントがありましたらご連絡ください。
ゲーム設計の背景
「Das Tal」には、孤立した小さなゲームワールドがたくさんあります。登録人数は1000人〜2000人、1つのワールドには100〜200 CCU(同時接続プレイヤー)が接続しています。この規模にした理由は、ワールドをタイムボックス化するため、また、親密なプレイヤーコミュニティを作ったり(一つのワールド内で)、技術的な複雑さを最小限にするためです。
ゲームプレイのコアは、非常に高速で単調なゲームプレイを中心としています。
つまり、複数のカウンターストライクサーバーと、接続インスタンスで構成される巨大なワールドを持つ「古典的な」WoWのようなMMORPG構造の中間です。ゲームプレイの例を次に示します:
インフラストラクチャーの全容
ゲームのライブ操作または開発のいずれかに関連するサービスの概要です。一部のサービスはダミーですが、ほとんどのサービスは既に稼働しています。
本番環境
- アカウント管理(REST)
- ゲームワールドを出ても保持される情報(名前、実績など)(REST)
- ゲームワールドリスト(REST)
- マップファイルの保管(HTTP, FTP)
- パッチャー/クライアントファイル(HTTP、FTP)
- ゲームサーバー(Photon Server)
- クライアント(Unity)
- データ収集(Graphite)
- 追跡(Honeytracks、Piwik、Google Analytics)
- 監視(icinga2)
- ウェブサイト(Tumblr、ウェブ)
- フォーラム(ウェブ)
- バックアップ(Bacula)
- ニュースレター(Mailchimp)
開発環境
これらのサービスの多くは非常にわかりやすいものです。ほとんどのMMO系のゲームで、何らかの形で使用されています。
ゲームサーバー
どのサービスも重要ですが、中心はゲームサーバーです。さまざまなことを検討した結果、Photon Serverを使用することにしました。主な理由は、コードが高速で、確立されているため、サーバーとクライアント(C#)の間でコードを簡単に共有できるからです。運用前にゲームサーバーミドルウェアの評価について少し書きました。
概要
私達のゲームサーバーは完全に権威のあるサーバーです。ゲームループのコア部分(シミュレーション)は、約15ティック/秒で実行されます。マルチスレッドは非常に難しいため、ゲームループのコア部分を1つのスレッドに制限することにしました。
シングルスレッドゲームループの設計と保持の方がはるかに簡単です。たとえば、高負荷時の謎のタイミングバグ(複製アイテムなど)が少なくなります。
欠点は、ゲームループのコア部分が水平方向にスケーリングできないため、1つのゲームワールドをスケールアップできなくなったことです。しかし、ゲームワールドが小さく設計されているため、問題ありません。
ネットワークIO、外部サービスの呼び出し、長時間実行の計算(パス検索)、またはワールドデータの保存など、その他のことは異なるスレッドで実行されます。実際、これらすべては、Photonの内部ファイバー実装を使用してファイバーとして実行されます。
クライアントインタレストの管理に2Dグリッドインデックスを使用しています。これは、トラフィックとCPUの負荷を管理可能なレベルに減らすために必要です。
ゲームプレイは、Artemisのエンティティシステムフレームワークに基づいています。衝突と動きにFarseerを使用しています。
クライアントとサーバー間の小さなメッセージには、Photon独自のシリアル化(基本的なデータ型のキー値リスト)のみを使用します。より大きくより構造化された/複雑なデータは、protobufによってシリアル化され、Photonプロトコルのバイト配列としてエンコードされます。
ゲームの状態は完全にメモリに収まります。したがって、protobufを使用して完全にシリアル化し、ファイルに保存するだけです。これは、各ゲームワールドが小さく、時間に制限があり、指数関数的に成長しないため、実現可能です。
アドバイス:protobufでエンコードされたファイルの横に、ゲームの完全な状態を1つの大きなYAMLファイルとしてダンプします。このようにすれば、テキストエディターだけでゲームの状態を簡単に調査できます。追加のツールは必要ありません。
すべてのログは、Photon Serverに含まれるlog4netを介して行われます。 私達が使用しているIoCコンテナーはWindsorです。
単体テストのセットアップはVisual Studio単体テストフレームワークを使用し、Photon MMOデモ例のセットアップに基づいています。
ピアとサービスの相互作用
ピアは接続されたクライアントを表し、プレイヤーと通信するためのゲートウェイとして機能します。各ピアには、受信メッセージと発信メッセージを処理するファイバーがあります。すべてのピアは別々に存在します。
異なるピア間の通信はサービスを使用して行われます。たとえば、認証が完了すると、ピアはチャットサービスに登録し、チャットメッセージを送受信するようになります。
ピアには、プロトコルの現在の状態を表すプロトコル状態オブジェクトがあります。したがって、クライアントがゲームに接続して参加すると、次の手順が実行されます(クライアントの観点から)。
- クライアントバージョンとサーバーバージョンが一致することを確認する
- ログインしてクライアントを認証する(アカウントサービスを使用)
- キャラクターを選択する(アカウントサービスを使用)
- サーバーからスキル定義と他のグローバルバランス値を取得する(バランスファイル、スキル定義サービスを使用)。
- 外部サーバーから静的な地図データをダウンロードするために必要な地図情報を取得する(ダウンロードURL、ハッシュ)(地図ストレージサービスを使用)
- 静的マップをダウンロードする(クライアント上にまだ存在しない、ハッシュが一致する場合)(ゲームサーバーは使用しない)
- マップをインスタンス化する(ゲームサーバーは使用しない)
- ゲームに参加する(ゲームサービスを使用)
ゲームサービス
ゲームサービスは、ゲームループのコア部分のファイバーを実行します。ゲーム内(ゲームファイバーで実行されているもの)とゲーム外の間のゲートウェイです。ピアがゲームに参加したい場合、gameService.Enter(peer)を呼び出します。
ゲームサービスは、ゲームループのコア部分のファイバーを実行します。ゲーム内(ゲームファイバーで実行されているもの)とゲーム外の間のゲートウェイです。ピアがゲームに参加したい場合、gameService.Enter(peer)を呼び出します。
キャラクター動作のメッセージは“gameService.OperationBodyMovement(peer, movement)を通ります。これらのコマンドはすべて、ゲームループファイバーのキューに入れられ、ティック間で実行されます(ゲームシミュレーションの1ステップ)。
ゲームサービスのもう一つの役割は、現在のゲーム状態のスナップショットを作成し(同期)、状態をシリアル化して保存するマップストレージサービスに渡すことです(非同期)。
ゲームプレイループのコア部分
このスニペットは、ゲームサービス内でティックメソッドがどのように呼び出されるかを示します。
ゲームループの中心はArtemisエンティティシステムです。エンティティシステムとそれとインタレストするすべてのコードは、クライアントとサーバー間で共有されます(ゲームネットワークの同期を処理するシステムは例外です)。
これは、クライアントが必要に応じて同じデータ構造とメソッドを使用できるようにするためです。現在、クライアントは共有ゲームプレイコードのデータ構造と一部のチェックメソッドのみを使用しています。Artemisには、さまざまなゲームプレイパーツ用のシステムが含まれています(たとえば、HPとエネルギーの再生用に使用するASCharacterRegsやキャスティングスキルに使用するASCharacterCast)。
ASFarseerは非常に重要なシステムです。Farseerライブラリを使用して、衝突と動きを計算します。
ASSpatialIndexシステムには、グリッドに基づくさまざまな空間インデックスが含まれており、効率的なクライアントのインタレスト管理に必要です。足跡を処理するための特別なグリッドインデックスもあります(リージョンが足跡だらけになるのを避けるため)。
ASNetwork(図では「game network outgoing」と呼ばれています)は、クライアントとのすべてのゲーム状態の同期を処理します。このコンポーネントは、ゲームサービス内で現在アクティブなすべてのピアインスタンスにアクセスできます。
次の手順は、例に基づいてこれらのコンポーネントの相互作用を示しています
例:プレイヤーがキャラクターを動かし、その結果が画面に表示される。
- クライアントが動作メッセージを送信する
- ピアが動作メッセージを受信する
- ピアのゲーム内状態がメッセージを処理し、ゲームサービスに配信する
- 現在のティックを終了した後、ゲームサービスは動作コマンドを処理し、影響を受けるエンティティで動作を計画します
- 次のゲームティック中に、ASFarseerシステムはすべてのエンティティの動作をシミュレートします
- ティックの終わりに、ASNetworkシステムは処理された入力メッセージに対するACKメッセージを送り返します
- さらに、ASNetworkシステムは、関連する位置の更新をアクティブなピアに送信します(グリッドを使用)
- クライアントがローカル予測位置を受信位置に調整します
今週はここまでです。実装方法に関するご質問やご意見がございましたら、sebi@fairydist.comまでご連絡ください。
コメント
0件のコメント
サインインしてコメントを残してください。