カードシャッフルゲームを作ろう【後編】

この記事は後編です。まだ前半を読んでいない方はプログラミングでゲームを作ってみよう!【前編】を先に読んでください!

こんにちは、くしらっちょです。前回の記事から作成しているゲームを引き続き解説していきたいと思います。

目次

  • カードに絵柄を描き込む
  • カードをシャッフルする①
  • カードをシャッフルする②
  • その他の仕様を作る
  • UIを整える
  • おわりに

カードに絵柄を描き込む

カードに絵柄を描き込みます。

今回、カードに書いてある絵柄は疑似要素で実装しているのでdata-name属性の属性値をJavaScriptで変更することで実装します。

また、JavaScriptでカードの位置とそのカードに書かれている絵柄を記録しておく必要があります。

ここで役に立つのが二進数です。正直に実装すると配列で管理する方法が考えられますが、今後の実装のことを考えると数字として扱うことが出来るので便利です。

例えば上図のように、左から一番目が〇の時は、二進数では「0001」で、十進数では「1」です。

正解が左から1番目、2番目、3番目、4番目の4種類のパターンが考えられるので、全ての状態は4つの数字で表せます。

具体的には

0001 → 1
0010 → 2
0100 → 4
1000 → 8

となります。

カードをシャッフルする①

どのカードをシャッフルするかも先ほどと同じように二進数で表します。

シャッフルするカードを1、そのままのカードを0とします。4つの中から2つを選ぶ方法は4C2=6パターンあります。

具体的には

0011 → 3
0101 → 5
0110 → 6
1001 → 9
1010 → 10
1100 → 13
となります。

続いてカードをシャッフルしたときに〇が移動する場所を求めます。

この演算は2通りに場合分けを行うことで実装できます。

ここで、正解のカードの位置を示した数字をa、シャッフルする2つのカードを表した数字をbとします。

(1) aとbの1の位置が同じ場合
(2) aとbの1の位置が違う場合
(上図参照)

(1)について、正解の位置が変わるのでシャッフル後の正解の位置はb-aとなります。

(2)について、正解の位置は変わらないのでシャッフル後の正解の位置はaのままです。

この部分は実際に手を動かして法則を見つけることも出来るのですが、数学的に証明することも可能です。

カードをシャッフルする②

この項目は少し難易度が高いため、難しいと感じたら一旦読み飛ばしてもらっても大丈夫です。

対象の2枚のカードをシャッフルするときの楕円軌道の模式図を示しました。

2枚のカードの中心をそれぞれA,Bとおき、その中点をOと置きます。また、ABの長さをdとします。

dは選べれた2枚の距離なので、シャッフルする2つのカードを表した数字bを用いて計算することが出来ます。

具体的には1があるインデックスをそれぞれ求めて、引き算します。そして求まった数にr(=160px)を掛けます。

次にカードの軌道について考えます。とりあえず、軌道を楕円ではなく円とし、左側のカードだけを考えます。シャッフル開始してtミリ秒後のカードの中心をPと置きます。(tは媒介変数)

今回はCSSのtransisionプロパティのtranslateの値をJavaScriptで時間ごとに書き換える実装するので、Aを(0,0)としてPのx,y座標をtの関数で表したいです。

∠AOPを偏角θ[rad]とし、角度をω[rad/s]、時間をt[s]とすると、点Pは

と表すことが出来ます。

y座標の値に適当な定数(今回は1/5)を掛けることで横長な楕円軌道を実現することが出来ました。

同様に右側のカードについても考えることが出来ます。具体的にはx,y座標の値の正負を反転させます。

この求まった式をもとにJavaScriptのasync awaitを使用して実装していきます。10msごとにtranslateの更新、ωtがπを超えるまで処理を繰り返します。

JavaScriptのasync awaitでは秒数の単位がmsで与えられることに注意してください。

1回シャッフルを完了した後、カードを初期状態に戻し、書かれている絵柄だけ更新します。

transrateの値が小数点になったり、移動後のカードの管理が困難であったりするために初期化をするプロセスをはさみます。

この一連の流れをfor文で繰り返すことでゲームのメインとなる構造が作成できました。

これでシャッフルをする機構が作れました。難易度によって各速度ω、シャッフル回数を変化させることで調整を行うことが出来ます。

今回ではシャッフル回数は「難易度×8」、角速度ωは「π/難易度」としています。

その他の仕様を作る

最後にゲームシステムの仕様を前編で軽く作成したフローチャートを見ながら作成していきます。

  • スタートボタンを押したら、ユーザーにカードを見せる : スタート後全てのカードにopenクラスを付け、そのあとにcloseクラスに切り替える。
  • カードをクリックしてオープン : クリックしたカードだけにopenクラスを表向きにする
  • 正誤判定 : そのカードに書かれている絵柄が〇であるならば正解、そうでなければ不正解と表示する。(クリックしたcardのdata-nameの属性値が〇の時正解)

UIを整える

しかし、カードがシャッフルしている最中でもカードをめくれたりしてしまいます。

そのように開発側が予想していない挙動を制限するために、クリックできるタイミングを調整する必要があります。

シャッフル中は… カードを選択できない/スタートボタンを押せない/難易度を変更できないようにします。

それぞれの要素に対し、操作が出来なくなるクラスを付与、除去します。少し大変ですが、親切なUIにするためにもこの工程を頑張りましょう。

おわりに

以上で全ての解説が終わりました。後半は少し小難しい話になってしまいましたが、出来るだけ分かりやすく書いたつもりです。

今回の記事が皆様のお役に立てたならば幸いです。

これからもJavaScriptなどの技術系のブログをアップしていく予定なので、これからもよろしくお願いします。