paint-brush
Singularity: ユニバーサル バックエンド フレームワークによるゲーム開発の効率化@makhorin
450 測定値
450 測定値

Singularity: ユニバーサル バックエンド フレームワークによるゲーム開発の効率化

Andrei Makhorin13m2024/07/25
Read on Terminal Reader

長すぎる; 読むには

ゲーム デザインにおける「ユニバーサル フレームワーク」とは何でしょうか? なぜそれが必要なのか、または便利なのでしょうか? 開発の効率化にどのように役立つのでしょうか? 私たちはこれらすべての質問 (およびその他) に答え、私たちのソリューションである Singularity を紹介します。
featured image - Singularity: ユニバーサル バックエンド フレームワークによるゲーム開発の効率化
Andrei Makhorin HackerNoon profile picture


こんにちは!Pixonic (MY.GAMES) のサーバー開発者、Andrey Makhorin です。この記事では、私のチームと私がバックエンド開発用のユニバーサル ソリューションを作成した方法についてお話しします。コンセプト、その結果、そして Singularity と呼ばれるシステムが実際のプロジェクトでどのように機能したかについて学びます。また、私たちが直面した課題についても詳しく説明します。

背景

ゲーム スタジオを立ち上げる際には、魅力的なアイデアを迅速に考案して実装することが重要です。数十の仮説がテストされ、ゲームは絶えず変更され、新しい機能が追加され、失敗したソリューションは修正または破棄されます。ただし、この迅速な反復プロセスは、厳しい期限と短い計画期間と相まって、技術的負債の蓄積につながる可能性があります。


既存の技術的負債がある場合、古いソリューションを再利用するのは、さまざまな問題を解決する必要があるため複雑になる可能性があります。これは明らかに最適ではありません。しかし、別の方法があります。それは「ユニバーサル フレームワーク」です。汎用的で再利用可能なコンポーネント (レイアウト要素、ウィンドウ、ネットワーク インタラクションを実装するライブラリなど) を設計することで、スタジオは新機能の開発に必要な時間と労力を大幅に削減できます。このアプローチにより、開発者が記述する必要があるコード量が削減されるだけでなく、コードがすでにテスト済みであることが保証されるため、メンテナンスに費やす時間が短縮されます。


これまで、1 つのゲームのコンテキスト内での機能開発について説明してきましたが、今度は別の角度から状況を見てみましょう。どのゲーム スタジオにとっても、プロジェクト内の小さなコードを再利用するというのは、制作を効率化するための効果的な戦略になり得ますが、最終的には、新しいヒット ゲームを作成する必要があります。既存のプロジェクトのソリューションを再利用すると、理論上はこのプロセスが加速される可能性がありますが、2 つの大きなハードルが生じます。まず、ここでも同じ技術的負債の問題が当てはまります。次に、古いソリューションは以前のゲームの特定の要件に合わせて調整されている可能性が高いため、新しいプロジェクトには適していません。


これらの問題は、データベースの設計が新しいプロジェクトの要件を満たしていない、テクノロジが時代遅れである、新しいチームに必要な専門知識が不足しているなどのさらなる問題によってさらに複雑になります。


さらに、コアシステムは特定のジャンルやゲームを念頭に置いて設計されることが多く、新しいプロジェクトに適応することが困難になります。


ここでも、ユニバーサル フレームワークが役立ちます。互いに大きく異なるゲームを作成することは克服できない課題のように思えるかもしれませんが、この問題にうまく対処したプラットフォームの例があります。PlayFab、Photon Engine、および同様のプラットフォームは、開発時間を大幅に短縮し、開発者がインフラストラクチャではなくゲームの構築に集中できるようにする能力を実証しています。


さて、私たちの物語に入りましょう。

シンギュラリティの必要性

マルチプレイヤー ゲームには、堅牢なバックエンドが不可欠です。その好例が、当社の主力ゲームである War Robots です。これはモバイル PvP シューティング ゲームで、10 年以上にわたってリリースされており、バックエンド サポートを必要とする多数の機能が蓄積されています。また、当社のサーバー コードはプロジェクトの詳細に合わせて調整されていましたが、時代遅れになったテクノロジを使用していました。


新しいゲームを開発する段階になって、War Robots のサーバー コンポーネントを再利用しようとすると問題が発生することに気付きました。コードはプロジェクト固有のものであり、新しいチームには欠けているテクノロジの専門知識が必要でした。


また、新しいプロジェクトの成功は保証されておらず、たとえ成功したとしても、最終的にはまた別の新しいゲームを作成する必要があり、同じ「白紙の状態」の問題に直面することになるだろうということも認識していました。これを回避し、将来に備えるために、ゲーム開発に必要な必須コンポーネントを特定し、将来のすべてのプロジェクトで使用できる汎用フレームワークを作成することにしました。


私たちの目標は、開発者がバックエンドアーキテクチャ、データベース スキーマ、インタラクション プロトコル、特定のテクノロジを繰り返し設計する必要がなくなるツールを提供することでした。認証、支払い処理、ユーザー情報の保存を実装する負担から開発者を解放し、ゲームプレイ、デザイン、ビジネス ロジックなど、ゲームの中核部分に集中できるようにしたいと考えました。


さらに、私たちは新しいフレームワークで開発を加速するだけでなく、クライアント プログラマーがネットワーク、DBMS、インフラストラクチャに関する深い知識がなくてもサーバー側のロジックを記述できるようにしたいと考えました。


さらに、一連のサービスを標準化することで、DevOps チームは IP アドレスのみを変更して、すべてのゲーム プロジェクトを同様に扱うことができます。これにより、再利用可能なデプロイメント スクリプト テンプレートと監視ダッシュボードを作成できるようになります。


プロセス全体を通じて、将来のゲームでバックエンドを再利用する可能性を考慮したアーキテクチャ上の決定を行いました。このアプローチにより、フレームワークが柔軟でスケーラブルになり、さまざまなプロジェクト要件に適応できるようになりました。


(フレームワークの開発は単独で行われたのではなく、別のプロジェクトと並行して作成されたことも注目に値します。)

プラットフォームの作成

私たちは、ゲームのジャンル、設定、コアゲームプレイに依存しない次のような一連の機能を Singularity に提供することにしました。

  • 認証
  • ユーザーデータストレージ
  • ゲーム設定とバランス解析
  • 支払い手続き
  • ABテストの配布
  • 分析サービスの統合
  • サーバー管理パネル


これらの機能は、あらゆるマルチユーザー モバイル プロジェクトの基本となります (少なくとも、Pixonic で開発されたプロジェクトに関連しています)。


これらのコア機能に加えて、Singularity はビジネス ロジックに近いプロジェクト固有の機能をさらに収容できるように設計されています。これらの機能は抽象化を使用して構築されているため、さまざまなプロジェクト間で再利用および拡張可能です。


例としては次のようなものがあります:

  • クエスト
  • オファー
  • 友達リスト
  • マッチメイキング
  • 評価表
  • プレイヤーのオンラインステータス
  • ゲーム内通知



技術的には、Singularity プラットフォームは次の 4 つのコンポーネントで構成されています。

  • サーバー SDK:これは、ゲーム プログラマーがサーバーを開発できるライブラリのセットです。
  • クライアント SDK:モバイル アプリケーションを開発するためのライブラリ セットです。
  • 既製のマイクロサービスのセット:これらは変更を必要としない既製のサーバーです。 認証サーバー、バランスサーバーなどが含まれます。
  • 拡張ライブラリ:これらのライブラリには、オファー、クエストなどのさまざまな機能がすでに実装されています。ゲーム プログラマーは、ゲームで必要な場合にこれらの拡張機能を有効にすることができます。


次に、これらの各コンポーネントを調べてみましょう。


サーバーSDK

プロファイル サービスやマッチメイキングなどの一部のサービスでは、ゲーム固有のビジネス ロジックが必要です。これに対応するために、これらのサービスはライブラリとして配布されるように設計されています。開発者はこれらのライブラリ上に構築することで、コマンド ハンドラー、マッチメイキング ロジック、その他のプロジェクト固有のコンポーネントを組み込んだアプリケーションを作成できます。


このアプローチは、フレームワークが低レベルの HTTP プロトコル機能を提供する ASP.NET アプリケーションの構築に似ていますが、開発者はビジネス ロジックを含むコントローラーとモデルの作成に集中できます。


たとえば、ゲーム内でユーザー名を変更する機能を追加したいとします。これを行うには、プログラマーは新しいユーザー名とこのコマンドのハンドラーを含むコマンド クラスを作成する必要があります。


ChangeNameCommand の例を次に示します。

 public class ChangeNameCommand : ICommand { public string Name { get; set; } }


このコマンド ハンドラーの例:

 class ChangeNameCommandHandler : ICommandHandler<ChangeNameCommand> { private IProfile Profile { get; } public ChangeNameCommandHandler(IProfile profile) { Profile = profile; } public void Handle(ICommandContext context, ChangeNameCommand command) { Profile.Name = command.Name; } }


この例では、ハンドラーは IProfile 実装で初期化される必要があります。これは、依存関係の挿入によって自動的に処理されます。IProfile、IWallet、IInventory などの一部のモデルは、追加の手順なしで実装できます。ただし、これらのモデルは抽象的な性質を持ち、特定のプロジェクトのニーズに合わせて調整されていないデータを提供し、引数を受け入れるため、操作があまり便利ではない場合があります。


作業を簡単にするために、プロジェクトは独自のドメイン モデルを定義し、それをハンドラーと同様に登録し、必要に応じてコンストラクターに挿入することができます。このアプローチにより、データを操作する際に、よりカスタマイズされた便利なエクスペリエンスを実現できます。


ドメイン モデルの例を次に示します。

 public class WRProfile { public readonly IProfile Raw; public WRProfile(IProfile profile) { Raw = profile; } public int Level { get => Raw.Attributes["level"].AsInt(); set => Raw.Attributes["level"] = value; } }


デフォルトでは、プレーヤー プロファイルにはレベル プロパティは含まれません。ただし、プロジェクト固有のモデルを作成することで、この種のプロパティを追加でき、コマンド ハンドラーでプレーヤー レベルの情報を簡単に読み取ったり変更したりできるようになります。


ドメイン モデルを使用したコマンド ハンドラーの例:

 class LevelUpCommandHandler : ICommandHandler<LevelUpCommand> { private WRProfile Profile { get; } public LevelUpCommandHandler(WRProfile profile) { Profile = profile; } public void Handle(ICommandContext context, LevelUpCommand command) { Profile.Level += 1; } }


このコードは、特定のゲームのビジネス ロジックが、基盤となるトランスポート層またはデータ ストレージ層から分離されていることを明確に示しています。この抽象化により、プログラマーはトランザクション性、競合状態、その他の一般的なバックエンドの問題を気にすることなく、コア ゲーム メカニズムに集中できます。


さらに、Singularity はゲーム ロジックを強化するための幅広い柔軟性を提供します。プレイヤーのプロファイルは「キー型の値」のペアのコレクションであり、ゲーム デザイナーは思い描いたとおりにプロパティを簡単に追加できます。


プロフィール以外にも、Singularity のプレイヤー エンティティは柔軟性を維持するために設計されたいくつかの重要なコンポーネントで構成されています。特に、これには各通貨の量を追跡するウォレットと、プレイヤーのアイテムをリストするインベントリが含まれます。


興味深いことに、Singularity のアイテムはプロファイルに似た抽象的なエンティティです。各アイテムには一意の識別子とキー型の値のペアのセットがあります。したがって、アイテムは必ずしもゲーム世界の武器、衣服、リソースなどの実体である必要はありません。代わりに、クエストやオファーなど、プレイヤーに一意に発行される一般的な説明を表すことができます。次のセクションでは、これらの概念が特定のゲーム プロジェクト内でどのように実装されるかを詳しく説明します。


Singularity の重要な違いの 1 つは、アイテムがバランスシート内の一般的な説明への参照を保存することです。この説明は静的のままですが、発行された個々のアイテムのプロパティは変更できます。たとえば、プレイヤーに武器のスキンを変更する機能を与えることができます。


さらに、プレイヤー データを移行するための堅牢なオプションも用意されています。従来のバックエンド開発では、データベース スキーマはビジネス ロジックと密接に結合されていることが多く、エンティティのプロパティを変更するには、通常、直接スキーマを変更する必要があります。


ただし、従来のアプローチは Singularity には適していません。フレームワークではプレイヤー エンティティに関連付けられたビジネス プロパティが認識されず、ゲーム開発チームにはデータベースへの直接アクセスがないため、このアプローチは適していません。代わりに、移行は、リポジトリとの直接的なやり取りなしで動作するコマンド ハンドラーとして設計および登録されます。プレイヤーがサーバーに接続すると、そのプレイヤーのデータがデータベースから取得されます。サーバーに登録されている移行がまだこのプレイヤーに適用されていない場合は、その移行が実行され、更新された状態がデータベースに保存されます。


適用された移行のリストもプレーヤー プロパティとして保存されます。このアプローチには、移行を時間の経過とともに段階的に実行できるというもう 1 つの大きな利点があります。これにより、すべてのプレーヤー レコードに新しいプロパティを追加してデフォルト値に設定する場合など、大量のデータ変更によって発生する可能性のあるダウンタイムやパフォーマンスの問題を回避できます。

クライアントSDK

Singularity は、バックエンドのインタラクションにわかりやすいインターフェイスを提供するため、プロジェクト チームはプロトコルやネットワーク通信テクノロジの問題を気にすることなく、ゲーム開発に集中できます。(ただし、SDK では、必要に応じてプロジェクト固有のコマンドのデフォルトのシリアル化方法をオーバーライドする柔軟性が提供されます。)


SDK を使用すると、API との直接的なやり取りが可能になりますが、日常的なタスクを自動化するラッパーも含まれています。たとえば、プロファイル サービスでコマンドを実行すると、プレーヤーのプロファイルの変更を示す一連のイベントが生成されます。ラッパーはこれらのイベントをローカル状態に適用し、クライアントがプロファイルの現在のバージョンを維持できるようにします。


コマンド呼び出しの例を次に示します。

 var result = _sandbox.ExecSync(new LevelUpCommand())


すぐに使えるマイクロサービス

Singularity 内のほとんどのサービスは、汎用性を考慮して設計されており、特定のプロジェクトに合わせてカスタマイズする必要はありません。これらのサービスは、事前に構築されたアプリケーションとして配布され、さまざまなゲームで利用できます。


既成のサービス スイートには次のものが含まれます。

  • クライアントリクエストのゲートウェイ
  • 認証サービス
  • 設定とバランステーブルを解析して保存するサービス
  • オンラインステータスサービス
  • 友人のサービス
  • リーダーボードサービス


認証サービスやゲートウェイなど、一部のサービスはプラットフォームの基本であり、展開する必要があります。その他のサービスは、フレンド サービスやリーダーボードなどオプションであり、必要のないゲームの環境から除外できます。

多数のサービスの管理に関連する問題については後で触れますが、現時点では、オプションのサービスはオプションのままにしておく必要があることを強調することが重要です。サービスの数が増えると、新しいプロジェクトの複雑さとオンボーディングのしきい値も増加します。


拡張ライブラリ

Singularity のコア フレームワークは非常に優れていますが、重要な機能は、コアを変更することなくプロジェクト チームが独自に実装できます。機能が複数のプロジェクトに有益である可能性があると判断された場合、フレームワーク チームによって開発され、個別の拡張ライブラリとしてリリースされます。これらのライブラリは、ゲーム内のコマンド ハンドラーに統合して使用できます。


ここで適用できる機能の例として、クエストやオファーが挙げられます。コア フレームワークの観点から見ると、これらのエンティティは単にプレイヤーに割り当てられたアイテムです。ただし、拡張ライブラリはこれらのアイテムに特定のプロパティと動作を付与して、クエストやオファーに変換できます。この機能により、アイテムのプロパティを動的に変更して、クエストの進行状況を追跡したり、オファーがプレイヤーに最後に提示された日付を記録したりすることができます。


これまでの結果

Singularity は、弊社の最新のグローバル ゲームの 1 つである Little Big Robots に実装され、クライアント開発者がサーバー ロジックを独自に処理できるようになりました。さらに、プラットフォーム チームからの直接サポートを必要とせずに、既存の機能を活用したプロトタイプを作成することができました。


しかし、このユニバーサル ソリューションには課題がないわけではありません。機能の数が増えるにつれて、プラットフォームとのやり取りも複雑になりました。Singularity は、単純なツールから洗練された複雑なシステムへと進化しました。これは、基本的なプッシュ ボタン式の電話からフル機能のスマートフォンへの移行と似ています。


Singularity により、開発者がデータベースやネットワーク通信の複雑さに深く関わる必要性が軽減されましたが、同時に独自の学習曲線も導入されました。開発者は Singularity 自体のニュアンスを理解する必要があり、これは大きな変化となる可能性があります。


開発者からインフラストラクチャ管理者まで、さまざまな人々がこの課題に直面しています。これらの専門家は、Postgres や Kafka などのよく知られたソリューションの導入と保守について深い専門知識を持っていることがよくあります。しかし、Singularity は社内製品であるため、新しいスキルを習得する必要があります。つまり、Singularity のクラスターの複雑さを学び、必須サービスとオプション サービスを区別し、どのメトリックが監視に重要であるかを理解する必要があります。


社内では、開発者がプラットフォームの作成者にいつでもアドバイスを求めることができるのは事実ですが、このプロセスには必然的に時間がかかります。私たちの目標は、参入障壁を可能な限り最小限に抑えることです。これを達成するには、新しい機能ごとに包括的なドキュメントを作成する必要があります。これにより開発が遅くなる可能性がありますが、それでもプラットフォームの長期的な成功への投資と見なされます。さらに、システムの信頼性を確保するには、堅牢なユニット テストと統合テストのカバレッジが不可欠です。


Singularity は自動テストに大きく依存しています。手動テストでは個別のゲーム インスタンスを開発する必要があり、現実的ではないためです。自動テストでは、エラーの大部分 (つまり 99%) を検出できます。ただし、特定のゲーム テスト中にのみ明らかになる問題も常に少数存在します。Singularity チームとプロジェクト チームは非同期で作業することが多いため、これがリリース タイムラインに影響を与える可能性があります。かなり前に書かれたコードにブロッキング エラーが見つかり、プラットフォーム開発チームが別の重要なタスクで忙しい可能性があります (この課題は Singularity に固有のものではなく、カスタム バックエンド開発でも発生する可能性があります)。


もう 1 つの大きな課題は、Singularity を使用するすべてのプロジェクトの更新を管理することです。通常、機能リクエストと機能強化を継続的に提供してフレームワークの開発を推進する 1 つの主要プロジェクトがあります。このプロジェクト チームとのやり取りは緊密で、私たちは彼らのニーズを理解し、彼らが問題を解決するために当社のプラットフォームをどのように活用できるかを理解しています。


一部の主力プロジェクトはフレームワーク チームと密接に連携していますが、開発初期段階にある他のゲームは、既存の機能とドキュメントのみに依存して独立して動作していることがよくあります。開発者がドキュメントを誤解したり、利用可能な機能を誤用したりする可能性があるため、冗長または最適ではないソリューションにつながることがあります。これを軽減するには、プレゼンテーション、ミートアップ、チーム間の交流を通じて知識の共有を促進することが重要ですが、このような取り組みにはかなりの時間の投資が必要です。

未来

Singularity はすでに当社のゲーム全体でその価値を実証しており、さらに進化する準備ができています。新しい機能を導入する予定ですが、現時点での主な焦点は、これらの機能強化によってプロジェクト チームにとってプラットフォームの使いやすさが複雑にならないようにすることです。

これに加えて、参入障壁を下げ、導入を簡素化し、分析の面で柔軟性を高め、プロジェクトがソリューションを接続できるようにする必要があります。これはチームにとって課題ですが、私たちのソリューションに投資した努力は間違いなく十分に報われると信じており、実際にそれを目の当たりにしています。