AWS & Game Advent Calendar 2020 の 21 日目の記事になります。
Amazon GameLift FlexMatch でマッチングルールセットの記述方法を簡単な例をベースにスッテプバイステップで解説付きで記載します。
「GameLift FlexMatch を使ってみたいけど、どのようにルールを記述すればよいかわからない」といった方向けの記事となっています。
GameLift FlexMatch とは?
GameLift FlexMatch の概要については、本アドベントカレンダーの@oreo367 さんによるAmazon GameLift FlexMatch でマッチメイキング“だけ”動かしてみる に記載されているのでそちらをご参照ください。この記事にも記載されていますが、2020 年 11 月のアップデートにより、GameLift FlexMatch を Stand alone で利用することが可能になっています!!🎉👏
AWS announces General Availability of Amazon GameLift Feature Update
GameLift FlexMatchにおけるマッチング開始までの流れ
GameLift FlexMatch のルールを説明する前に、改めて GameLift FlexMatch におけるマッチングの説明を簡単に記載します。
- ゲームクライアントがクライアントサービスにゲームプレイリクエストを送信
- クライアントサービスがマッチメイキングリクエストを GameLift FlexMatchに送信
- GameLift FlexMatch がマッチングチケットを発行し、チケットIDをクライアントサービスに返信
- GameLift FlexMatch のマッチメイキング設定において指定されたルールセットに従って、チケットプール内のチケットでマッチングを実施し、チケットステータスを更新
上記の流れに沿って、GameLift FlexMatch のマッチングが実施されます。この 4 のステップにおけるルールセットがマッチメイキングの肝となります。
FlexMatch のルールセットについて
GameLift FlexMatch のルールセットが行うことは基本的に以下の2点であり、様々なオプションを用いてルールセットをカスタマイズすることが可能です。
- マッチングのチーム構造とサイズを決定
- 可能な限り最良のマッチを見つけるために、プレーヤーをどのように評価するかを指示
このルールセットはJSON 形式で記述します。詳細なパラメータを省略した大まかな構造を記載してみました。ここまでシンプルにすると、かなりとっつきやすいのではないでしょうか。
{
"name": "string",
"ruleLanguageVersion": "1.0",
"playerAttributes":[{...}],
"algorithm": {...},
"teams": [{...}],
"rules": [{...}],
"expansions": [{...}]
}]
}
それぞれのコンポーネントの内容を簡単に以下に記載します。スキーマの詳細を確認したい方はFlexMatchルールセットスキーマをご参照ください。
- name : ルールセットを説明するラベル [必須]
- ruleLanguageVersion : ルール言語のバージョン(現時点で1.0のみ) [必須]
- playerAttributes : プレイヤーデータの属性
- algorithm : マッチメイキングプロセスのアルゴリズムをカスタマイズする設定
- teams : チームの構造とサイズ範囲 [必須]
- rules : マッチングのプレイヤーの評価方法
- expansions : マッチング完了できない場合の時間経過に伴うマッチング要件を緩和する設定
この内、ルールに直接作用する操作可能な項目は「playerAttributes」「algorithm」「teams」「rules」「expansions」の5つのコンポーネントとなります。また「teams」のみ必須となっています。
では、実際にこの構造でどのようにルールを組み立てていけばいいのか、例をもとに順を追って説明します。
ルールセットを1から作ってみる
今回、2-4 vs 2-4 のオンラインFPS対戦ゲームを例として、5つのコンポーネントを使って順番にルールを組み立てていきます。
1.teams の追加
まずは、必須条件となっている「teams」を追加します。このコンポーネントでは、チームの構造とサイズを設定します。今回は 2-4 vs 2-4 なので、以下のように記載することができます。また、各チームに Red / Blue という名前をつけています。
{
"name": "fpsgame",
"ruleLanguageVersion": "1.0",
"teams": [{
"name": "Red",
"maxPlayers": 4,
"minPlayers": 2
}, {
"name": "Blue",
"maxPlayers": 4,
"minPlayers": 2
}]
}
最低限のルールセットの内容としてはこれだけです。流石に寂しいので、このルールをベースに順を追って拡張していきましょう。
2.rules の追加
今回のチーム構造としては、最小チーム人数と最大チーム人数に差があるので、ゲーム開始時にチームに人数差が発生する可能性があります。そこで、今回の場合、Red チームのプレイヤー人数と Blue チームのプレイヤー人数が一致している場合のみ開始するように条件を設定したいと思います。
特定の条件を設定したい場合は、「rules」コンポーネントを利用します。FlexMatch の「rules」ではいくつかのルールタイプをサポートしています。値を比較して評価したい場合は、comparison というルールタイプを利用することで、記述可能です。例では、count 関数を使って、Red チームのプレイヤーと Blue チームのプレイヤー人数が一致しているかどうかを確認しています。このようにあらかじめ用意された関数等を利用して集計して評価することが可能です。
他のルールタイプや関数等について知りたい場合は、FlexMatchルール言語のドキュメントを参照してください。
"rules": [{
"name": "EqualTeamSizes",
"type": "comparison",
"measurements": [ "count(teams[Red].players)" ],
"referenceValue": "count(teams[Blue].players)",
"operation": "="
}]
3.playerAttributes (と rules )の追加
対戦型ゲームでは、試合が均衡するようにプレイヤーのスキルに基づいてマッチングを行うケースがあります。では、スキルがチーム間で均等になるように評価を行うルールを追加してみましょう。
プレイヤースキルといったプレイヤーの属性に基づいてルールを設定する場合、「playerAttributes」を利用します。この属性は、マッチメイキングリクエスト時に Gamelift FlexMatchに送信することが可能です。今回は、1000を基準とするユーザのスキルレートをもとにルールを設定しようと思いますので、「playerAttributes」として Skill を追加します。
"playerAttributes": [{
"name": "Skill",
"type": "number",
"default": 1000
}]
では、次にこのパラメータを使って、チームのプレイヤーのスキルが均等になるように「rules」を作成します。プレイヤーが持つスキルレベル間の差(距離)を評価したいので、distance というタイプを使用します。マッチングにおける各チームの平均スキルレベル(measurementsにて設定)とすべてのプレイヤー平均スキルレベル(referenceValueにて設定)が100以内に収まるようにルールを記述し、先程の EqualTeamSizes と同じように「rules」に追加します。
"rules": [{
"name": "FairSkill",
"type": "distance",
"measurements": [ "avg(teams[*].players.attributes[Skill])" ],
"referenceValue": "avg(flatten(teams[*].players.attributes[Skill]))",
"maxDistance": 100
},{
"name": "EqualTeamSizes",
"type": "comparison",
"measurements": [ "count(teams[Red].players)" ],
"referenceValue": "count(teams[Blue].players)",
"operation": "="
}]
このように 「rules」 では、複数のルールを設定することが可能ですが、それらは AND 条件として評価されます。そのため、マッチングはすべてのルールを満たした際に成立します。
4.expansions の追加
ここまでチーム人数・スキルレベル調整を「rules」に設定しましたが、場合によっては、条件を満たすプレイヤーが存在せず、マッチングが完了しない可能性があります。このような場合、マッチングの時間経過に伴って、マッチング要件を緩和する仕組みを「expansions」を使って記述することができます。
例として、FairSkill というスキルマッチを行うルールを緩和させる例を以下に記載します。この例では、30秒後に maxDistance を100 -> 200 に、40秒後に200 -> 300 に変更しています。
"expansions": [{
"target": "rules[FairSkill].maxDistance",
"steps": [{
"waitTimeSeconds": 30,
"value": 200
}, {
"waitTimeSeconds": 40,
"value": 300
}]
}]
5.algorithm の追加
このコンポーネントを理解するためには、まず FlexMatch がどのようにマッチメイキングのプロセスをすすめるかを知る必要があります。Gamelift FlexMatch におけるデフォルトの FlexMatch マッチメイキングプロセスは次のとおりです。
- すべてのオープンなマッチメイキングチケットとバックフィルチケットは、チケットプールに配置されます。
- チケットはマッチングのために 1 つ以上のバッチにランダムにグループ化されます。チケットプールのサイズが大きすぎる場合のみ、マッチング最適化のために複数のバッチが生成されます。
- 各バッチはチケットの古い順によってソートされます。
- 最も古いチケットから順に、バッチ内のすべてのチケットが、マッチ条件を満たすか評価されます。
このデフォルトのアルゴリズムは、ほとんどのゲームにおいてマッチングリクエストを迅速かつ効率的に処理するために設計されています。しかしながら、状況やニーズによっては、このプロセスをカスタマイズしたい場合があります。そのような場合、プロセスのアルゴリズムを修正する方法として、「algorithm」 コンポーネントがあります。
それでは、この「algorithm」で設定できる例を一つ見てみましょう。今回のゲームでは、最小 2 vs 2 でゲームが開始します。ただし、4 vs 4 のように可能な限り最大人数でゲームを進行するように設定したいと思います。
この場合、マッチメイキング設定で自動バックフィルをオンにすることで、少ない人数で開始したゲームセッションの人数を自動的に埋めることが可能になります。さらに、ルールセットにて「algorithm」 を追加し、backfillPriority を high に設定することで、バックフィルを優先させることが可能になります。backfillPriority を有効にする場合は、strategy を exhaustiveSearch(デフォルト) に設定する必要があります。
"algorithm": {
"backfillPriority": "high",
"strategy": "exhaustiveSearch"
}
最終的に完成したルールセットは以下のようになります。
{
"name": "fpsgame",
"ruleLanguageVersion": "1.0",
"playerAttributes": [{
"name": "Skill",
"type": "number",
"default": 1000
}],
"algorithm": {
"backfillPriority": "high",
"strategy": "exhaustiveSearch"
},
"teams": [{
"name": "Red",
"maxPlayers": 4,
"minPlayers": 2
},{
"name": "Blue",
"maxPlayers": 4,
"minPlayers": 2
}],
"rules": [{
"name": "FairSkill",
"type": "distance",
"measurements": [ "avg(teams[*].players.attributes[Skill])" ],
"referenceValue": "avg(flatten(teams[*].players.attributes[Skill]))",
"maxDistance": 100
},{
"name": "EqualTeamSizes",
"type": "comparison",
"measurements": ["count(teams[Red].players)"],
"referenceValue": "count(teams[Blue].players)",
"operation": "="
}],
"expansions": [{
"target": "rules[FairSkill].maxDistance",
"steps": [{
"waitTimeSeconds": 30,
"value": 200
}, {
"waitTimeSeconds": 40,
"value": 300
}]
}]
}
設計方針のまとめ
例を踏まえた手順では5つのコンポーネントを追加して、ルールセットを作成しました。改めて一からルールを作成するなら個人的には「こういった形ですすめるかな」という例をまとめます。
- 「team」 の構造を決定する
- マッチング時にどのうような「rules」 を設定したいか検討
- 「rules」で必要になる「playerAttributes」を定義
- 「rules」 を実装
- 「expansions」 による段階的なマッチング条件の緩和を検討
- (オプショナル)「algorithm」のカスタマイズが必要かを検討
難しいところが、2-4 の実際にルールを組むステップだと思います。2.の検討段階で、以下の公式ドキュメントを参照して、予めどのようなルールタイプ・式・関数が利用できるのかを確認し、希望するルールの実現可能性を検討する形が良いかと思います。
FlexMatchルールセットスキーマ
FlexMatchルール言語
また、この記事で紹介したようなサンプルがいくつか以下のドキュメントにはありますので、こちらを踏まえて色々なパターンを確認すると良いかなと思います。
FlexMatchルールセットの例
補足ですが、上記の方針は前提として大規模でないマッチング(40人以下)が前提となります。大規模なマッチングを作成する場合は、より最適化したアルゴリズムに設定するために、algorithm の strategy や balancedAttribute といった設定を行うことが推奨されています。詳細はこちらをご参照ください。
大規模なマッチングの一致アルゴリズムをカスタマイズする
最後に
この記事を読んだ方が、GameLift FlexMatch でどのようにルールを作ればいいか、少しでもイメージできるようになれば嬉しいです。
また、GameLift FlexMatch のスタンドアローン版も提供開始され、触りやすくなったかと思います。ぜひこれを機に試してみると面白いかなと思います。
今後は「え?これできるの?」というようなパターンのルールセットを集めたサンプル集を作りたいです。