僕が カードゲームを作るなら #1

社内の1人アドベントカレンダーに投稿している内容を見たいという声があったので転載。内容的には会社まったく関係無いので問題は無いでしょう。

今日は、カードゲーム を作るとしたら、どんな設計になるかをやってみたいと思います。
1人のエンジニアが1つのゲームをどのような順序で設計していくか、その過程で色々な気付きもありそうで、そういった実験をします。

Hearthstone

この記事は、ある程度ルールや仕様を知っている前提で話を進めます。

まずどこから作るか

さて、皆さんならどこから作りますか?
例えば、データベース設計とモジュール設計ならどちらを先にやりますか?
あるいは、フレームワークのインストールが最初でしょうか?
是非、ここで一旦読み進めるのをやめて考えてみて下さい。

どうでしょう?

僕はモジュール設計をやりました。
理由は、データベースはゲームの仕様とまったく関係が無いため、ここから設計を始めると実装がデータベース設計に引っ張られてうまくいかないかなと思うからです。
また、フレームワークも同様の理由でゲームにまったく関係がありません。そのため、これらより先にゲームを作ります。

システム設計は、ゲームをどのような部品で組み立てるかを考えるフェーズです。
例えば、以下のような要素があります。

  • ヒーロー
  • カード
  • デッキ

僕はまず カードゲーム を上記3つで構成出来ると考えました。
次に、これらの関係図を作ってみます。以下のようになりました。

                    +-------+
+---*| Hero |*---+
| +-------+ |
| |
| |
+-------+ +-------+
| Card |--------*| Deck |
+-------+ +-------+

* が付いてる方の要素が、紐付く要素を包含している、という意味になります。
この図は、

  • HeroはCardとDeckを持っている
  • DeckはCardを持っている

という意味になります。
もしかすると、以下のように考える人も居るかもしれません。

                    +-------+
| Hero |
+-------+
*
|
+-------+
| Deck |
+-------+
*
|
+-------+
| Card |
+-------+

僕はこれには問題があると思っていて、何故なら、この図は Hero が最低1つ以上の Deck を所持していないと Card を所持出来ないことを示しているからです。
Hero は Deck をまだ作っていなくても Card を持てるべきですよね。こういった細かな設計上の違和感も、上記のような簡単な関係図によってレビューが出来るので良いですね。

僕はまずここから作りました。皆さんはどうだったでしょう?

次は?

先程の関係図を再度掲載します。

                    +-------+
+---*| Hero |*---+
| +-------+ |
| |
| |
+-------+ +-------+
| Card |--------*| Deck |
+-------+ +-------+

もし上記のままクラスにするとどうなるでしょう?
Dependency Inversion Principle 違反が起きます。Hero という上位モジュールが、Card, Deck といった下位モジュールに依存しているためです。
そうすると何が起きるかというと、

  • 全てのCardの1クラスで表現する必要がある
  • 全てのDeckを1クラスで表現する必要がある

そのため、Card と Deck はそれぞれインターフェースとして、依存関係を反転させようと思います。

                    +-------+
+---*| Hero |*---+
| +-------+ |
| |
| |
+-------+ +-------+
| ICard |--------*| IDeck |
+-------+ +-------+

これにより、Hero はカードやデッキとして振る舞うものであればなんでも所持出来るようになりました。

次は、ICard を実装するものは何かを考えてみます。
Hearthstone のカードにはミニオン、スペル、装備の3種類があります。
また、ミニオンにはタウントと呼ばれる種類のものがあったり、スペルはその場で使用するものと場に伏せておくものがあったり、装備には攻撃用と防御用があります。
なので、ICard を実装する IMinion, ISpel, IEquipment の3インターフェースに切り出してみました。

                    +-------+
+------->| ICard |<-------+
| +-------+ |
| ^ |
| | |
+---------+ +-------+ +-------------+
| IMinion | | ISpel | | IEquipment |
+---------+ +-------+ +-------------+

次に、切り出した3つのインターフェースを実装するクラスを順に考えてみます。
まずはミニオンから。ミニオンは、戦士と守衛という2タイプとなるので、以下のような設計にしてみました。

                   +---------+
+--->| IMinion |<---+
| +---------+ |
+---------+ +-----------+
| Warrior | | Guardian |
+---------+ +-----------+

スペルはどうでしょうか。その場で使うものと、場に伏せて何らかの行動をきっかけに発動するもの2タイプです。
それぞれ Instant, Interrupt としてクラス化しました。

                    +-------+
+--->| ISpel |<---*
| +-------+ |
+---------+ +-----------+
| Instant | | Interrupt |
+---------+ +-----------+

装備は、武器と盾なので、これもシンプルにクラスにしました。

                 +-------------+
+--->| IEquipment |<---+
| +-------------+ |
+---------+ +---------+
| Weapon | | Buckler |
+---------+ +---------+

ここまででゲームに出てくる要素の関係が大体クラスとして浮き出てきましたね。

難所: 効果をどう表現するか

  • 相手を選択して攻撃する
  • 相手を選択して回復する
  • 場に出た時にダメージを与える
  • 場に出た時に味方を強化する
  • 破壊された時にダメージを与える

などなど・・ここがゲームのキモの部分です。
効果として一言でまとめていますが、以下の2つに分別が出来ます。

  • 選択されて行動すると効果が発動
  • 場に存在するだけで効果が発動

僕は今回、効果を 行動振る舞い に分けて考えました。
さて、これらをどのように設計しましょうか。まず、以下のことが言えますね。

  • 効果を持つのはカード
  • カードが効果を直接持つと DIP 違反になるので、効果はインターフェースにする

まずは行動から。

        +---------+           +-----------+ 
| Warrior |*--+ +--*| Guardian |
+---------+ | | +-----------+
+---------+ | | +-----------+
| Instant | | | | Interrupt |
+---------+ | | +-----------+
* +---------+ | | +---------+ *
| | Weapon | | | | Buckler | |
| +---------+ | | +---------+ |
| * | | * |
| | +---------+ | |
+------+---| IAction |---+-------+
+---------+

全カードが行動を持ちました。
ICard を Abstract クラスにしてそっちで持った方が良かったかもしれません。ただ、言語によってはそれが出来ないのでこうしました。

次は振る舞いです。
スペルは行動以外無いので、振る舞いを持たせませんでした。

        +---------+           +-----------+ 
| Warrior |*--+ +--*| Guardian |
+---------+ | | +-----------+
| |
+---------+ | | +---------+
| Weapon | | | | Buckler |
+---------+ | | +---------+
* | | *
| +-----------+ |
+--| IBehavior |--+
+-----------+

IAction と IBehavior はカード効果を表現します。全ての効果を図に出来ないので簡略化しています。
Action クラスは1つの行動ごとに増えます。

                   +---------+
| IAction |
+---------+
^
|
+---------+
| Action |
+---------+

Behavior クラスは1つの振る舞いごとに増えます。

                  +-----------+
| IBehavior |
+-----------+
^
|
+-----------+
| Behavior |
+-----------+

関係図が出来た

次回は、これらを使ってバトルするならどうなる?をやってみたいと思います。

※投稿したものをそのまま再掲しているので、随所に後から修正するに至ったものも含まれています。オリジナルをそのままに。

Written by

#umedago #osakaup #gglt hatajoe.github.io/talks

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store