学マスのカードゲーム部分の実装を再現した話#

学マスというゲームの再現実装をしたので、その作業中に感じた開発ノウハウを話す内容です。

この記事は、カンム Advent Calendar 2024の9日目の記事です。

学マスとは?#

学園アイドルマスターというバンナムが運営している、いわゆるカジュアルスマホゲーム・ソーシャルゲームです。

2024/05/16にリリースしており、初日にプレイして出来の良さにびっくりしてバンナムの株を買ったほどです。別段上がってないです。

おおよそのゲーム性は、デッキ構築型のカードゲームと、パワプロのサクセス部分もしくはウマ娘の育成ゲーム部分の組み合わせです。

なぜ再現実装をしたの?#

特に目的はなく、楽しいからです。

もう少し解説すると、仕事内容や時代の変化により、仕事で答えのあるロジックをプログラミングをする機会が減少しており、時折無性にロジックとテストコードを交互に書くような作業がしたくなることがあります。その題材として、既存のゲームの再現を選んでいる感じです。

他には、少し前の完成品だとローゼンケーニッヒというボードゲームの再現を書きました。また、途中で学マスへ切り替えてしまいましたが、ポケカの再現をしようとして初手のデータモデリングで止まってます。exスタートデッキの範疇でも中々複雑で、TCGの闇を知った気がします。

また、単に感銘したモノに対しての推し心の発露という側面もあります。なんかしたくなる。

再現実装のこと#

関連リポジトリは以下です。

gakumas-coreは、TypeScript製のPure JavaScriptのコードです。ゲームロジックを表現したデータ構造と関数の集合です。gakumas-lesson-simulatorは、UIへ反映する検証をゲームのシミュレーターという形で表現するためのGatsbyの実装で、今回の話にはあまり関係ありません。

再現したのはデッキ構築型カードバトル部分のみで、育成ゲーム部分は含みません。プレイヤーから見てゲームの研究対象になるのは主に育成ゲーム部分であり、全く世間の需要を満たしていない実装でもあります。カードバトル部分については、通常発生しないような状況を除いて、正確に再現できているつもりです。

2024/09/22にマージした【Feel Jewel Dream】有村麻央を追加が最後のプルリクエストで、それ以後は残念ながらゲームへの興味を失ってしまったので、追随していません。プレイヤーとしての現状はログボ勢です。アノマリーって何ですか?

良いと思った開発ノウハウ#

はじめにですが、自分はゲーム作成のプロどころかアマチュアですらありません。10年前くらいに2本程度JSである程度の大きさのゲームを作りましたが、過去の話です。どちらかというと、Webフロントエンド開発者としての意見です。

状態の更新は間にイベントを介して表現する#

例えば、ライフへ5ダメージを与えることを、以下のようなイベントを作成して表現しました。

const ライフを5減少するイベント = {
  type: "life",
  value: -5,
};

加えて、別にイベントリストを集計して現在値を算出する処理を作成し、各種状態の現在値の算出はこの関数を介して行いました。イベントリストは、ゲームの開始から終了までの発行したイベント全てを保持します。

const 各種現在値を算出する = (初期値, イベントリスト) => {
  let 現在値 = 初期値;
  for (const イベント of イベントリスト) {
    switch (イベント.type) {
      case "life": {
        現在値 = {
          ...現在値,
          life: 現在値.life + イベント.life,
        };
      }
    }
  }
  return 現在値;
};

特に事情がない場合は、以下のように現在値を減少すると思うのですが、それと比較して何が良かったのか、という話です。

const 新しい現在値 = {
  ...現在値,
  life: 現在値.life - 5,
};

良かったと実感した点は、2点ありました。

ひとつめは、状態の変化を直接抽出できるため、テストコードのアサーションの期待値が明確になることです。例えば、「5ダメージ発生した」というテストケースを書く場合に、イベントの5の値を期待値として使うことができます。現在値を修正する方式の場合は、変化前の現在値から変化後の現在値を差し引くことでしかその差がわかりません。単純な加減算の計算しか発生しないなら問題は少ないですが、ゲームによっては、1回の攻撃で複数回ダメージが発生したり、毒や混乱などの何らかの効果を付与するようなものもあります。それらのデータ構造は辞書やリストで表現されるようなより複雑なものなので、差し引きによる変化の抽出が困難または不可能です。

ふたつめは、イベントリストをUIへ渡すと、アニメーション等に使える簡易的な状態変化の明細になることです。例えば、ライフが20のキャラクターが5ダメージを受けて15になった場合に、ほとんどの状況ではUIへ15の値だけ渡しても情報が足りません。5ダメージは何らかの概念により2と3に分割して発生したかもしれないし、色味をつけるために炎や氷属性などの属性情報が必要かもしれません。そういった情報をUIへ渡すために、イベントリストがそのまま使えます。もちろん、計算時にアニメーション用の専用のイベントを出力するのが正攻法ですが、複雑なデータ構造を増やすのは手間が大きいです。ゲーム作成素人の主観ですが、デッキ構築型カードゲーム程度に見た目の動きの少ないゲームなら、ロジックの都合で生成されたイベントリストでもおおよそ足りるのではないかと思っています。ちなみに、これにより、古今東西のゲームでよくある「すごいダメージの攻撃をしたのに、敵の体力を減らした分までしかダメージ量が表示されない」問題が解決します。

一方で、悪そうな点も、2点あります。

ひとつめは、現在値の算出にイベントリストを集計する必要があるので、処理が遅くなりそうなことです。物凄く雑にデッキ構築型で1戦闘で発生しそうなイベント数を試算してみると、「4キャラクター(プレイヤー+敵3体) * 10ターン * 10イベント/行動 = 400イベント」程度なので、集計処理の複雑さ次第でもありますが、問題になる計算量ではなさそうです。

ふたつめは、イベントの概念が増え、集計処理が必要になることで、実装が複雑になり見通しが悪くなることです。これは一定の影響はあると思っています。例えば、ReduxのActionを介した状態の更新に似た影響があるでしょう。

悪い点もありますが、総じて良かったと思いました。

この設計は、イベントソーシング設計の一側面に似ています。イベントソーシング自体は多くの問題を視野に入れた設計ですが、単一システムかつオンメモリな状況でも役立つものですねという所感です。

データファイルの記述において行数を減らそうとしない#

ゲームデータを定義するファイルは長くなりがちです。例えば、今回の自分の実装だとカードの定義は8千行にもなっています。

ファイルが長いと何かと不便なので、データの行数を減らすための工夫をしたくなります。初期値を設けたり、プロパティの組み合わせを類型化して共通化したり、関数を使ってデータを生成するようにしたり、などです。

そういった工夫は、ゲームデータに対しては、存否をプロパティの有無で表現する以外は上手く機能しないので、基本的には項目をベタ書きで定義することを推奨します。

上手く機能しない理由は、何らかの類型化・共通化が有効に機能するにはデータの内容に頻出するパターンなどの偏りがあることが前提になっていると思っていますが、ゲームデータの場合はあえてパターンを多様にする傾向があり、それと相性が悪いのではと考えています。

デッキ構築型として、学マスはかなりシンプルな方だと思いますが、それでもこの感想を持っています。何倍も多様な表現を持つSlay the SpireやMonster Trainなどの規模を想定するなら、なおさらです。

また、ベタ書きの方が記述の際にAIの補助を受け易かったり、JSON互換を保ち易いのでゲームデータを編集するサブツールを作る時に便利という、細かな利点もあります。

データのIDはローマ字にする#

学マスは日本のゲームなので、キャラクターやカード等のゲームデータの名称は日本語です。

それらのデータをこちらの実装のデータとして定義する際にIDの文字列が必要になりますが、それを日本語の名称を元としたヘボン式ローマ字で記述すると良かったです。

ほぼ間違いなく名称から一意に決まり、人間の目で不便ながらも意味が読み取れ、idへ"kingyosukuideshobu"を書くとnameへ"金魚すくいで勝負"という感じに、Copilotが正しい日本語を5割程度の確率で補完してくれます。

実際のゲームプレイを再現したテストを書く#

ある程度開発が進んだ時に、公開APIを使って、実際のゲームプレイを再現したテストを書いてみたら良かったです。

例えば、プレイ動画を撮って、それを再現するテストケースを書いていました。

テストの効用は、一般的な結合テストやE2Eテストによるものと大体似ていますが、まだ純粋なロジックの状態なので記述も保守も比較的容易というのがお得なところです。

なお、ゲーム画面を撮影するとそれを再現したテストコードが書けるというのは、学マスのゲーム設計と情報設計の良さを示すものでもあると思います。

締め#

以上、デッキ構築型カードバトルの再現実装をする際の開発ノウハウを話しました。

世界に一人くらいは役立つ人が居ると信じて!ご愛読ありがとうございました!