カードゲームでカードをどのように作るか:効果編

過去に大富豪を作っていた際は、リフレクションを使って文字列からインスタンスを取得する方法を考えていましたが、実際のカードゲームとなると、非効率なプログラムになると考えています。そのため、リフレクションを使わずにどのように […]

過去に大富豪を作っていた際は、リフレクションを使って文字列からインスタンスを取得する方法を考えていましたが、実際のカードゲームとなると、非効率なプログラムになると考えています。そのため、リフレクションを使わずにどのようにカードのスクリプトを実装するのかを改めて確認していきます。

トランプとTCGでは性質が異なる

トランプの場合、ジョーカーを抜くと52種類のカードが存在しますが、カードの種類には1~13の数字と4つのスートと一定の決まりがあり、同じカードは存在しません。しかし、TCGでは無数のカードが存在し、山札には同じカードが複数枚存在し、カード毎に違った特性があり、人によって山札のカードが異なります。さらに、カードが持つ情報もかなり多いです。つまり、トランプと実際のカードゲームでは、根本的な性質が異なります。なので、プログラムの作り方や考え方も発展させる必要があります。

トランプの場合は、カードプレハブを1つ作って、スクリプトファイルもコンパクトにまとめる事ができましたが、TCGの場合はカードの種類も魔法やモンスターなどの種類があり、その中にも更に装備魔法や効果モンスターなどの種類があります。トランプで再現するには限度がありますが、トランプを使ったTCGを作る事が、最も勉強に繋がることかもしれません。もちろん、いきなり対戦ゲームは作れませんから、最初は一人で遊べるプロトタイプを作っていきましょう。それでは早速、ゲームのルールを決めていきましょう。

なぜトランプを使うか?

ゲームプログラミングのチュートリアルで嫌になるほどトランプを扱ってきたと思いますが、なぜトランプを使うのかも説明しておきます。トランプと遊戯王で、カードが持つ情報を比較すると分かると思いますが、トランプは数字とマークと色の3つだけを管理するだけでカードができます。さらに、それぞれの情報は一貫性があり、増減しません。ですが、遊戯王となると、まず魔法かトラップかモンスターか。さらに属性、ステータス、名前、イラストなどなど、データに一貫性が無く、扱う情報量もかなり多く、魔法だと属性とステータスが無いとか、情報が増減します。

せっかくプロトタイプを作ろうというのに、カードの情報を管理するのに悪戦苦闘していてはもったいないですよね。なので、まず情報をどう管理すると効率がいいか等を、簡単に作れるトランプで構想を練るわけです。要するに、小学生にいきなり掛け算を教えるのではなく、まず足し算を教えるような感じです。なので、簡単に作れるのであればトランプでなくても一応は大丈夫です。

ゲームのルールを決定する

ルールを複雑にしてしまうと、ルールの実装だけで骨が折れてしまいますので、あくまでシンプルなルールを考えます。今回は、手札のカードを場に出して、ポイントを加算していき、目標スコアに到達したらクリアするゲームを制作します。カードの種類が多いと大変になりそうなので、黒と赤のJQKAの合計8種類のカードで行いたいと思います。各カードは、それぞれ効果が異なるように制作しますが、ゲームバランスとかは完全に無視します。

カード効果の管理について

今回のメインは、あくまで効果の実装と管理の方法なので、その部分にだけ焦点を当てます。なので、カードをどう作るかとかは飛ばしちゃいます。以前カード効果を実装した際は、カードが持つ効果を自身のIDからリフレクションでインスタンス化して、取得していました。しかし、リフレクションはあくまで動作テストで使うもので、通常は使わないようです。そのため、今回は別の手段を使います。

クラスのインスタンス化 = プレハブのインスタンス化

クラスをインスタンス化させるという考え方は、よくよく考えてみると、プレハブをインスタンス化することと似ています。結構色々調査したり、考えをまとめたりしてプレハブ化にたどり着いたのですが、これが中々合理的なんです。ただし「カード効果」ごとにプレハブを作るのではなく、効果が与える「影響」ごとにプレハブを作ります。理解が難しいと思いますので、詳しくご説明しましょう。

影響ごとにプレハブを作る

まず、イメージしやすいようにカード効果を2つ、例として考えてみました。
例1:場に出た時、カードを1枚ドローして、スコアを-2する。ターン終了時、スコアを+1する。
例2:ターン終了時、場のカードをランダムに1枚破壊する。このカードが破壊された時、スコアを+3する。

あくまでも例ですが、いい感じの効果が出来上がりましたね。上記の例には、ある共通点があります。それは「発動タイミング」と、与える「影響」の2つに分ける事ができる、という点です。さらに「影響」も、カードのドロー、スコアの増減、カードの破壊と、3つの影響に分ける事ができると分かるでしょうか。これは、上記の例だけが特殊という訳ではなく、カード効果は、必ず「タイミング」と「影響」に分ける事ができます。

つまり、カード効果はあくまで「タイミング」と複数の「影響」を組み合わせた、グループであるという事が分かります。なので、カード毎に効果クラスを毎回つくる必要はなく、ブロックを積み重ねるように、既にある影響クラスを組み合わせて、タイミングを設定してあげるだけで、作れるという訳です。

そうした方が、効果の強化や弱体化も簡単ですし、何かエラーがあっても1つのスクリプトを触るだけで修正できます。例えば、例1のドロー枚数を2枚にして、スコアを-5するように修正する時も、Unity上で数字を変化させるだけで済みます。ターン終了時の効果を消したい時も、Unity上でタイミングと影響を消してしまえば完了です。

まとめ

かなり長くなってしまったので、今回はここまでとします。効果をどう管理して、どのように実装するかの構想はある程度かたまりましたね。あとは、カードのデータをどのように管理するかです。こちらも長くなりそうなので、次回のメモにまとめたいと思います。