【初心者向け】第五回 メモ帳だけでゲーム作ってみた〈神経衰弱編〉

今回も、パソコンに標準装備されている「メモ帳」だけを使って、「神経衰弱」を作っていきたいと思います♪

前回の記事をまだ読んでいない方は、ぜひそちらから先に読んでください!


今回作るもの

前回は、キーボードの入力で動かせるポインターを作りましたね!

  1. 準備編
  2. カードの枠を表示させよう!
  3. カードの数字を表示させよう!
  4. キー入力で動かせるようにしよう! ←前回
  5. キー入力でカードをひっくり返すようにしよう! 今回
  6. 乱数を使ってカードを並べ替えよう!

そして今回は、すべてのカードを裏返し、エンターキーを押すとひっくり返せるようなコードを組んでいきたいと思います!

完成イメージは以下の通りです!

今回で、カードを裏返して、同じ数字なら裏返したままにしておくといったコードを組むので、神経衰弱として遊ぶことができるようになります!


今回必要になる知識

先にお伝えすると、今回新しく必要になってくるプログラミングの知識はありません!

ですが、今回は条件分岐をちょこちょこ使っていきます!

なので、条件分岐をまだ完全に理解していない方は、下の条件分岐について詳しくまとめた記事を読んで少し理解を深めておきましょう!


コードを書いていこう!

前回と同様に、メモ帳にコードを書いていきましょう!

今回は、似た名前の変数を作る事になるので、どの変数がどういう意味だったかをしっかり把握して進めましょう♪


変数を作る

まずは、今回使う変数を作りましょう!

今回は、またまた多次元配列を使っていきます!

…
let x = 0;
let y = 0;

let flip = [
  [false, false, false, false, false, false, false, false, false, false, false, false, false],
  [false, false, false, false, false, false, false, false, false, false, false, false, false],
  [false, false, false, false, false, false, false, false, false, false, false, false, false],
  [false, false, false, false, false, false, false, false, false, false, false, false, false]
];

let memory_x = 0;
let memory_y = 0;

let flipped = false;

document.addEventListener("keydown", movePointer);
…

flipという多次元配列には、どこのカードの数字が見えているのか、裏を向いているのか、という情報を、「true」と「false」というものでしまっておきます。

「false」の時は、数字や柄が見えていない状態(裏側)にして、「true」のときが数字や柄が見えている状態(表側)であるということがわかっていれば大丈夫です!

そして、memory_x, yには、一枚目にめくったカードの位置を記録させておきます!

最後の「flipped」というのは、一枚目のカードがめくられたかめくられていないかを記録しておきます。

これも先ほどと同様で、「false」の時は、一枚目がめくられていなくて、「true」の時は一枚目がすでにめくられていて、次に二枚目をめくる状態であることを指します!

変数は使うことによってわかることもあると思うので、ここでは完全に理解しなくて大丈夫ですよ~!


カードをひっくり返す

まずは、神経衰弱はカードがすべて裏を向いている状態で始まるので、数字と柄が表示されないようにしましょう!

ここで、先ほど作った「flip」という多次元配列を使います!

数字と柄を表示させるコードの部分を少し変えてみます!

for (let j = 0; j < 4; j++) {
    for (let i = 0; i < 13; i++) {
      CONTEXT.rect(70 + 100 * i, 60 + 100 * j, card_width, card_height);
      CONTEXT.stroke();
      if (flip[j][i] == true) {
        CONTEXT.fillText(card[j][i], 100 + 100 * i, 110 + 100 * j);
      }
    }
  }

flipがすべて「false」になっているので、数字と柄が表示されなくなったはずです。

このコードの意味がいまいちわからない方は、flipの中の「false」をいくつか「true」に変えてみましょう!

例えば…

let flip = [
  [false, false, false, false, false, false, false, false, false, false, false, false, false],
  [false, true, false, false, false, false, false, false, false, false, false, false, false],
  [false, false, false, false, true, false, false, false, false, false, false, false, false],
  [false, false, false, false, false, false, false, false, false, false, false, false, false]
];

これで実行すれば、大体どんな意味になっているのかがわかるはずです!


エンターキーを押してひっくり返す

では、次にエンターキーが押されたときにカードをひっくり返すようにしましょう!

キーを入力したときの処理は前回少し書いたので、その続きに書けばいいですね!

忘れている人がいるかもしれないので念のため

あとは、裏表の情報は「flip」という多次元配列にしまっているので、上の画像のように、対応する場所の「false」を「true」に変えればいいですね!

また、すでにめくられている場合は、めくることができないので、

めくる場所が「false」だったら「true」に変える

という風にすればいいですね!

なので、if文をうまく使い…

…
}else if (e.key == "ArrowUp") {
  if (y >= 1) {
    y = y - 1;
  }
}else if (e.key == "Enter"){
  if (flip[y][x] == false) {
    flip[y][x] = true;
  }
}

のように、エンターキーを押したときの処理を else if文書き、またその中にif文を書けばいいですね!

これで実行すれば、エンターキーを押したカードだけひっくり返るようになるはずです!


一枚目をめくったときの処理を書く

では、まず一枚目をめくったときの処理を書きましょう!

一枚目をめくる前は、最初に作った「flipped」という変数が「false」の時ですね!

あとは、一枚目をめくる処理は単純で、

カードをめくる
「flipped」を「true」にする(=一枚目をめくったら「true」に変えると最初に決めたため)
memory_x, y という変数に対応する場所を覚えさせる

の3つだけですね!

なので、コードも単純で、

…
}else if (e.key == "Enter"){
  if (flip[y][x] == false) {
    flip[y][x] = true;
    if (flipped == false) {
      flipped = true;
      
      memory_x = x;
      memory_y = y
    }
  }
}

と、めくった後に書くだけですね!


二枚目をめくったときの処理を書く

問題はこっちです。

二枚目をめくる時には、「flipped」は「true」の状態で、

「flipped」を「false」にもどす
もし、数字が同じならカードはめくったまま
もし、数字が違うなら何秒後かにカードを元に戻す

という3つの処理が必要ですね。

ですがこれ少し考えてみると

もし、数字が同じならカードはめくったまま

数字が同じなら何もしなくていい

となるので、実際に書かなければいけない処理は

「flipped」を「false」にもどす
もし、数字が違うなら何秒後かにカードを元に戻す

という2つを書くだけでいいのです!

そのまえに、何秒後かにカードを元に戻すためには、新しい「setTimeout」というものを使います。

ここのコードは少し難しいので、先に答えを書いてから、あとで解説という形にします!

function movePointer(e) {

…
中略
…

  }else if (e.key == "Enter"){
    if (flip[y][x] == false) {
      flip[y][x] = true;
      if (flipped == false) {
        flipped = true;
        
        memory_x = x;
        memory_y = y
      }else if (flipped == true) {
        flipped = false;
        if (card[y][x][1] != card[memory_y][memory_x][1]) {
          setTimeout("flipCards()", 1000);
        }
      }
    }
  }
  
  CONTEXT.clearRect(0, 0, 1400, 500);
  drawCard();
}

function flipCards() {
  flip[y][x] = false;
  flip[memory_y][memory_x] = false;
  
  CONTEXT.clearRect(0, 0, 1400, 500);
  drawCard();
}

まずは、

if (card[y][x][1] != card[memory_y][memory_x][1]) {

}

このif文から解説しましょう!

card[y][x]までなら、前回までも使っていますが、card[y][x][1]と、来ましたね。

実はこの「1」というのは、2文字目(コンピューターは0から始まるので、0、1の2番目)を指す意味があります。

なので、例えば…

let a = "こんにちは";
let b = a[3];

とすると、b は「こんにちは」の4番目の「ち」になります!

今回は、cardの中には例えば「♤3」などが入っていますが、実際にプレイしていく中で大事なのは、数字ですよね!

そしてのその数字だけを取り出そうとすると2文字目を指さなければならないので、最後にcard[y][x][1]と指すようにしています!

そして次に、今までの「==」ではなく「!=」となっていますね!

ですがこれは簡単で、数学などで使う「≠」(ノットイコール)と実は同じで、

もし、めくったカードと一枚目のカードが同じじゃなければ

といったように、今までのif文とは逆のものになっているということがわかりますね!

そして、次に…

setTimeout("flipCards()", 1000);

について説明します!

先ほども紹介しましたが、「setTimeout」というものを使って、何秒後かに処理を行うようにしています!

括弧の中の数字にはこのような意味があります!

setTimeout(行わせる処理, 何ミリ秒後に処理を行わせるか);

ここで「ミリ秒」という単位が出ましたね!

日常生活ではほとんど使いませんが、パソコンのような一秒に何億といった処理を行うものでが、1秒の1000分の1の1ミリ秒という単位を使います。

なので、1秒は1000ミリ秒となり、もし4秒遅らせたかったら上の数字を4000にすればいいですね!

ここで書いたコードを理解するのは、少し時間がかかるかもしれませんが、完全に理解しなくても全然大丈夫ですよ~!


うまくいかなかった方向け

うまくいかなかった方は、まずエラーが出ていないか確認しましょう!

エラーの確認方法については、別の記事で詳しくまとめたので、ぜひそちらを参考にしてください!

それでもうまくいかなければ、下のサンプルコードをダウンロードして、自分のコードを比較してみましょう!


ということで、第五回はここまでとしたいと思います!

次回は、新しいプログラミングの知識「乱数」を使って、カードをランダムでシャッフルするようなコードを組んでいきたいと思います!

長くなってしまいましたが、

最後まで読んでいただきありがとうございました!

ご感想などあれば、コメントやTwitterにどしどしお寄せください!

第六回はこちらから!