【初心者向け】第四回 メモ帳だけでゲーム作ってみた〈スライドパズル編〉

今回も、パソコンに入っている「メモ帳」だけを使って、HTMLJavaScriptを駆使して「スライドパズル」を作っていきたいと思います!

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


今回作るもの

  1. 準備編
  2. 数字を表示させよう!
  3. 要らないマスを塗りつぶそう! ←前回
  4. 数字を動かせるようにしよう! ←今回
  5. 完成させよう!

前回は、第二回までに作ったものをよりスライドパズルっぽくするためのの調整をしましたね。

そして今回は、数字をキーボードの矢印キーを使って動かせるようにしたいと思います!

今回で、ゲームのシステムのほとんどが完成するので、遊べるようになりますね~

おそらく今回が全5回の中で一番難しいと感じるかもしれませんが、ゆっくり理解していきましょう!


今回必要になる知識

今回もコードを書いていく上で必要になってくるプログラミングの知識が2つほどあります。

1つ目が、関数です!
関数については、詳しく別の記事でまとめましたので、ぜひそちらを参考にしてください!

【誰でもわかる!】関数とは

プログラミングを勉強していく中で、関数とは非常に便利な存在です! 完全に理解して使いこなせるようになるまでは、…

そして、2つ目が条件分岐です!

この条件分岐についても、別の記事で詳しくまとめたので、ぜひ見てください!


コードを書いていこう!

関数を使ってまとめる

前回は、繰り返しを使ってうまく長いコードを短くしましたね!

今回は、最初に関数を使って見やすくしていきたいと思います。

まずは、前回書いた線を表示させるコードから、数字を表示させるコードを「drawNumber」という名前の関数にしましょう!

…
let y = 0;

function drawNumber() {
  CONTEXT.beginPath();
  for (let i = 100; i <= 300; i = i + 100) {
    CONTEXT.moveTo(i, 0);
    CONTEXT.lineTo(i, 400);
    CONTEXT.stroke();

    CONTEXT.moveTo(0, i);
    CONTEXT.lineTo(400, i);
    CONTEXT.stroke();
  }

  CONTEXT.font = "50px serif";
  CONTEXT.textAlign = "center";

  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      CONTEXT.fillText(number[i][j], 50 + j * 100, 70 + i * 100);
    }
  }

  CONTEXT.fillRect(x * 100, y * 100, 100, 100);
}

drawNumber();

…

function drawNumber~ の部分で、仕事の内容を決めて、最後の「drawNumber();」呼び出していますね!

そしてもう一つ!

キーが押されたとに、数字を動かすための処理を「moveNumber」という名前の関数として新しく作りましょう!

…
drawNumber();

function moveNumber(e){

}

</script>

今回は、押されたキーによって処理を変えるため、「e」という引数を設定しました!

これで関数の設定は終わりです!
次からは、数字を動かすための処理について考えて行きましょう!


キーボードの入力を検知する

数字を動かす処理を書いていく前に、キーボードの入力を検知するためのコードを一行書きます。

…
let y = 0;

document.addEventListener("keydown", moveNumber);

function drawNumber() {
…

真ん中の一行ですね。

これについて詳しく説明すると難しくなってしまうので、簡単に説明すると…

キーボードのキーのどれかが押されたときに、
「moveNumber」という仕事をする

といった意味があります。


数字を動かす処理を書いていく

ということで本題です。

先ほど作った「moveNumber」という関数に、数字を動かす処理を書いていきましょう!

まず初めに、条件分岐を用いて、どのキーが押されたのかというのを判別しましょう!

function moveNumber(e){
  if (e.key == "ArrowRight") {

  }else if (e.key == "ArrowLeft") {

  }else if (e.key == "ArrowDown") {

  }else if (e.key == "ArrowUp") {

  }
}

先ほども少し説明しましたが、どのキーが入力されたかが引数によってわかるので、
引数の「key」が「ArrowRight」(右矢印キー)なら右に動かすという処理をする、
といったように条件で分けました。

そしてここから、前回作った変数の「x」と「y」を使っていきます。

前回にも少し説明しましたが、変数のx、yにはこういう意味があります。

つまり、右キーが押されたときには、xの値を1つだけ大きくしてあげればいいですね。

なので、

if (e.key == "ArrowRight") {
  x = x + 1;
}else if (e.key == "ArrowLeft") {

}else if (e.key == "ArrowDown") {

}else if (e.key == "ArrowUp") {

}

のように書くことができます。

ほかも同じように考えると、

左キーが押されたら、xの値を1つ減らす
下キーが押されたら、yの値を1つ増やす
上キーが押されたら、yの値を1つ減らす

のようになるので、

if (e.key == "ArrowRight") {
  x = x + 1;
}else if (e.key == "ArrowLeft") {
  x = x - 1;
}else if (e.key == "ArrowDown") {
  y = y + 1;
}else if (e.key == "ArrowUp") {
  y = y - 1;
}

のように書くことができます。

あとは、キーボードが押されたら(=moveNumberという仕事をしたら)、drawNumberをもう一度してもらって、画面を再更新すればいいですね。

なので、

function moveNumber(e){
  if (e.key == "ArrowRight") {
    x = x + 1;
  }else if (e.key == "ArrowLeft") {
    x = x - 1;
  }else if (e.key == "ArrowDown") {
    y = y + 1;
  }else if (e.key == "ArrowUp") {
    y = y - 1;
  }
  
  drawNumber();
}

のように、moveNumberの最後に、「drawNumber();」と書けばいいですね!

これで一度実行してみましょう!

するとどうなりましたか?

こんな感じ?

キーボードの入力で、黒いマスが増えていく状態になっているはずです。

いろいろある問題を一つ一つ直していきましょう!

黒いマスが増えないようにする

実はこれ、黒いマスが増えていっているように見えるのですが、実際は、

一個前の黒いマスを消していない

というのが問題です。

黒いマスを動かした後に一つ前の黒いマスを消していなければ、まるで黒いマスが増えたように見えますね。

これを直すために、「moveNumber」の中に1行足しましょう。

function moveNumber(e){
  CONTEXT.clearRect(0, 0, 400, 400);
  if (e.key == "ArrowRight") {
  …

clearRectというのは、

clearRect(x座標, y座標, 横の長さ, 縦の長さ);

といった使い方をするので、ここでは、画面の全体を消すように設定されています。

「画面の全部を消していいのか?」と聞かれると思いますが、大丈夫です!

その後に数字や線を書き直すようなコードを組んでいます!

これで実行してみると、黒いマスが増殖してしまう問題は解決しているはずです!


画面外に黒いマスが出てしまう

次に、黒いマスが外に出てしまう問題を解決しましょう!

とりあえず右キーを押したときだけを考えてみましょう!

これを見ながら考えよう!

右キーを押したとき、xは3までしか行けませんね!

逆に言えば、xは2以下の時だけ、右キーを押してxを1増やせばいいですね。

なので、if文を一つ追加して、下のようにしてみましょう!

…
if (e.key == "ArrowRight") {
  if (x <= 2) {
    x = x + 1;
  }
}else if (e.key == "ArrowLeft") {
…

これで、右の壁より右側にはいかなくなったはずです!

あとは、ほかも同じようにすればいいですね!

自分で少しコードを考えてから、下の答えを見てみましょう!

if (e.key == "ArrowRight") {
  if (x <= 2) {
    x = x + 1;
  }
}else if (e.key == "ArrowLeft") {
  if (x >= 1) {
    x = x - 1;
  }
}else if (e.key == "ArrowDown") {
  if (y <= 2) {
    y = y + 1;
  }
}else if (e.key == "ArrowUp") {
  if (y >= 1) {
    y = y - 1;
  }
}

これで実行すれば、黒いマス問題はすべて解決しましたね!


数字が移動していない

これが一番の大問題ですね!

数字を移動させるのには、前回使った「多次元配列」の中の数字を変えればいいですね。

ここのコードが一番難しいので、まずは、右キーを押したときだけを考えてみましょう!

例えば、

xは0、yも0の状態

この状態で、右キーを押したとき、0と1を入れ替えればいいですね!

0は、number[0][0]に入っていて、1は、number[0][1]に入っていますね!

そして次の画像の状態を考えてみましょう!

xは2、yは3の状態

この状態で、右キーを押すと、14(黒で見えないけど)と15を入れ替えればいいですね!

14は、number[3][2]に、15は、number[3][3]に入っていますね!

上の二つの例をぱっと見ただけでは、共通点はなさそうですが、xとyの数字に注目してもう一度見てみると、何か共通点はありませんか?

答えは、

どちらも、number[y][x]とnumber[y][x + 1]を入れ替えている

という点です。

よくわからなかったという方は、xとyの数字に注目してもう一度上の2つの例を見てみましょう!

右キーを押したときには、number[y][x]とnumber[y][x + 1]を入れ替えればいいですね!

なので、コードでは以下のように書けます!

…
if (e.key == "ArrowRight") {
  if (x <= 2) {
    number[y][x] = number[y][x + 1];
    number[y][x + 1] = 0;
    x = x + 1;
  }
}else if (e.key == "ArrowLeft") {
…

number[y][x + 1]のマスは、黒で塗りつぶされて、数字は関係ないので、0を入れておきましょう!

あとは、これを左と下と上に対応させるだけ!

少し考えてみてから、下の答えのコードを見ましょう!

if (e.key == "ArrowRight") {
  if (x <= 2) {
    number[y][x] = number[y][x + 1];
    number[y][x + 1] = 0;
    x = x + 1;
  }
}else if (e.key == "ArrowLeft") {
  if (x >= 1) {
    number[y][x] = number[y][x - 1];
    number[y][x - 1] = 0;
    x = x - 1;
  }
}else if (e.key == "ArrowDown") {
  if (y <= 2) {
    number[y][x] = number[y + 1][x];
    number[y + 1][x] = 0;
    y = y + 1;
  }
}else if (e.key == "ArrowUp") {
  if (y >= 1) {
    number[y][x] = number[y - 1][x];
    number[y - 1][x] = 0;
    y = y - 1;
  }
}

これで、数字を自由に動かすことができるようになりました!

また、スライドパズルとして遊ぶこともできるようになりました!

思っている通りに動かすことができると、とても達成感があってうれしいですよね!


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

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

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

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


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

次回は、ゲームを開始したら、自動で数字がぐちゃぐちゃになるようにしたり、タートボタンを用意したりするなど、ゲームとしての完成度を上げたいと思います!

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

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

第五回はこちらから!