こんにちは! 私の名前はセルゲイ・カシャンで、私はWar Robotsプロジェクトのクライアント開発者です。 戦争ロボットは何年も前から存在しており、この時間の間、ゲームはさまざまなコンテンツを蓄積してきました:ロボット、武器、ドローン、タイタン、パイロットなど。 今日は、私たちのプロジェクトでバランスがどのように構成されているか、過去11年間で彼らに何が起こったのか、そして私たちがどのように対処したのかについて話します。 プロジェクトにおけるバランス 他のプロジェクトと同様に、War Robotsはメタとコアゲームプレイの2つの部分に分けられます。 ゲームのコア・ループを超えてもゲームプレイに影響を与える活動は、ゲームコンテンツの購入やアップグレード、社会的活動やイベントに参加を含む。 Meta gameplay (metagaming) プレイヤーが自分の目標を達成するためにゲームで行う行動の主な繰り返しサイクルです 私たちのケースでは、特定の地図上のロボットの戦いです。 Core gameplay (core gameplay loop) プロジェクトの各部分には独自のバランスが必要なので、バランスもメタとコアの2つのカテゴリーに分けます。 戦争ロボットとも呼ばれる。 独自のバランスを必要とするもの。 Skirmish modes A Skirmish モードは、多くの場合、イベントベースのモードであり、さまざまな休日の間、主に楽しみのためにプレイヤーに利用できます。 Skirmish mode したがって、合計で、我々は4つのバランスを有する: 2 デフォルトモードと 2 スカーミッシュモード。 11年以上にわたり、War Robotsは素晴らしいコンテンツを大量に蓄積してきました。 95 ロボット 21 タイタン 175種類の武器 40 ドローン 16 母性 a huge number of skins, remodels, modules, pilots, towers, ultimate versions of content, and maps. 多くのスキン、リモーデル、モジュール、パイロット、タワー、コンテンツの究極のバージョン、およびマップ そして、あなたが想像できるように、このすべての仕事を行うためには、行動、統計、可用性、価格、および多くの、多くの情報を保存する必要があります。 その結果、私たちのバランスは不適切なサイズに成長しました: Default mode Skirmish mode Meta balance 9.2 MB 9.2 MB Core balance 13.1 MB 13.1 MB Meta balance 9.2 MB 9.2 MB Core balance 13.1 MB 13.1 MB いくつかの迅速な計算の後、私たちはプレイヤーがダウンロードする必要があることを発見しました。 それはかなり多い! 44.6 MB 我々は本当にプレイヤーに、バランスを変更するたびにこのような大量のデータをダウンロードするよう強要したくなかった。 ただ、あなたを思い出させるために:War Robots has reached 2024年、私たちの月間アクティブな観客は そして、 毎日ログインしました。 300 million registered users 4.7 million people 690 thousand players データの量を想像してみてください! たくさんありますよね? 私たちもそう思いました! だから、私たちはバランスを減らすために全力を尽くすことを決めました! 問題を狩り下ろす 最初のステップは、バランスを分析し、「何がそんなに多くのスペースを占めているのか」を把握しようとすることでした。 すべてを手動で行うことは、私たちがやりたかった最後のことでした - それは何年もかかるでしょう. だから、私たちはバランスについて必要なすべての情報を収集し、合計するツールセットを書きました。 ツールは、バランスファイルを入力として取って、反射を使用して、すべての構造を繰り返し、どの種類の情報が保存され、どの程度のスペースが占められているかに関するデータを収集します。 結果は絶望的だった: 目標バランス % in balance Usage count String 28.478 % 164 553 Int32 27.917 % 161 312 Boolean 6.329 % 36 568 Double 5.845 % 33 772 Int64 4.682 % 27 054 Custom structures 26.749 % — String 28 478% 164 553 Int32 27 917% 161 312 Boolean 3629% 36 568 Double 5 845% 33 772 Int64 4682位 27 054 さん Custom structures 26 749% — コアバランス % in balance Usage count String 34.259 % 232 229 Double 23.370 % 158 418 Int32 20.955 % 142 050 Boolean 5.306 % 34 323 Custom structures 16.11 % — String 34259位 232 229 Double 23370% 158 418件 Int32 20955位 142 050 Boolean 5306位 34 323 Custom structures 16.11% — 状況を分析した後、私たちは気づいた。 そして、それについて何かをしなければならなかった。 strings were taking up far too much space このツールはバランスファイルをスキャンし、それぞれの文字列が複製された回数とともにすべての文字列のマップを生成しました。 結果も刺激的ではありませんでした! いくつかの文字列は何万回も繰り返されました! 我々は問題を見つけたが、今、問題は「どうやって解決するか」だった。 バランスを最適化 明らかな理由から、我々は単に文字列を完全に取り除くことができなかった。文字列は、ローカライゼーションキーやさまざまなIDのようなものに使用されるが、我々は文字列の重複を排除することができた。 アイデアは、それが得られるほどシンプルでした: 各バランスのためのユニークな文字列のリストを作成する(基本的に専用ストレージ)。 このリストをデータと共に送信します。 public class BalanceMessage { public BalanceMessageData Data; public StringStorage Storage; public string Version; } StringStorage は本質的に、文字列のリストの周りに包装します。 文字列のストレージを構築すると、各バランス構造は必要な文字列のインデックスを記憶します。 public class StringStorage { public List<string> Values; public string GetValue(StringIdx id) => Values[id]; } バランスの構造体内で文字列自体を渡すのではなく、文字列が文字列ストレージに保存されている場所のインデックスを渡し始めた。 以前: public class SomeBalanceMessage { public string Id; public string Name; public int Amount; } その後: public class SomeBalanceMessageV2 { public StringIdx Id; public StringIdx Name; public int Amount; } StringIdx は基本的に、Int の周囲に包装するだけです. この方法で、バランスの構造体内の直接の string 転送を完全に排除しました. public readonly struct StringIdx : IEquatable<StringIdx> { private readonly int _id; internal StringIdx(int value) {_id = value; } public static implicit operator int(StringIdx value) => value._id; public bool Equals(StringIdx other) => _id == other._id; } このアプローチは、ストレンドの数を数十倍減らしました。 String usage count String usage count Before After Meta balance 164 553 10 082 Core balance 232 229 14 228 Before After Meta balance 164 553 10 082位 Core balance 232 229 14 228位 悪くないですよね。 しかし、それは始まりに過ぎなかった――我々はそこで止まらなかった。 データプロトコルの再編 バランス構造を送信し処理するために、我々は使用していた。 . MessagePack MessagePack は、よりコンパクトで高速な JSON の代替として設計されたバイナリデータシリアリゼーション形式で、アプリケーションやサービス間の効率的なデータ交換を可能にし、特にパフォーマンスと帯域幅が重要な場合に有用なデータサイズの大幅な削減を可能にします。 最初、MessagePack は JSON のような形式で登場し、データが使用されるようになりました。 それは確かに便利ですが、かなりスペースを消費しますので、私たちはいくつかの柔軟性を犠牲にし、 . string keys binary byte array 以前: public class SomeBalanceMessage { [Key("id")] public string Id; [Key("name")] public string Name; [Key("amount")] public int Amount; } その後: public class SomeBalanceMessageV2 { [Key(0)] public StringIdx Id; [Key(1)] public StringIdx Name; [Key(2)] public int Amount; } また、すべての空のコレクションを削除しました - それらを送信する代わりに、私たちは現在ゼロ値を送信します. This reduced both the overall data size and the time required for serialization and deserialization. 変化をテスト 良い開発のゴールデンルール(そしてあなたにたくさんの神経を節約するルール)は、何かが間違っている場合にすぐに戻すことができるように新しい機能を常に実装することです。 開発中は、すべてのデータが正しく転送されたことを確認する必要がありました。古いバランスと新しいバランスは、フォーマットや構造に関係なく、同じ値を生成しなければなりませんでした。 これを達成するために、我々は各バランスのための多数の単位テストを書きました。 最初は、すべてのフィールドを「ヘッドオン」で比較し、それぞれのフィールドを明確にチェックしました。これは機能しましたが、それは時間がかかり、バランスの最小の変更さえテストを破り、私たちはそれらを絶えず書き直すことを余儀なくされました。 結局、我々はそれに十分で、バランスを比較するためのより便利なテストアプローチを発見しました。 We took two versions of the balance structures, e.g. SomeBalanceMessage and SomeBalanceMessageV2, and iterated over them — comparing field counts, names, and values. If anything didn't match, we tracked down the problem. このソリューションは後で私たちに大きな時間を節約しました。 最適化結果 これらの最適化のおかげで、ネットワーク経由で送信されるファイルのサイズと、クライアントでそれらを deserialize するのにかかる時間の両方を減らすことができました。 ファイルサイズ Old balances Optimized balances Profit Meta balance 9.2 MB 1.28 MB - 86 % Core balance 13.1 MB 2.22 MB - 83 % Meta balance 9.2 MB 1.28 MB 86% Core balance 13.1 MB 2.22 MB 83パーセント 砂漠化の時間 Old balances Optimized balances Profit Meta balance 967 ms 199 ms - 79 % Core balance 1165 ms 265 ms - 77 % Meta balance 967 ms 199 ms 79パーセント Core balance 1165 ms 265 ms 77% データ メモリ Old balances Optimized balances Profit Meta + Core ~ 45.3 MB ~ 33.5 MB - 26 % Meta + Core 453 MB 33,5 MB 26% 結論 最適化の結果は完全に私たちを満足させました. バランスのファイルは80%以上減少しました. トラフィックは減少し、プレイヤーは幸せでした. 要するに、あなたが送信するデータには注意し、不要なものを送信しないでください。 文字列は単一のストレージに保存するのが最善で、複製を作成しないようにします。また、カスタマイズされたデータ(価格、統計など)が繰り返しが多く含まれている場合は、それらを単一のストレージにパッケージしてみてください。