【中級者向け】第二回 メモ帳だけでゲーム作ってみた〈ぷよぷよ編・続〉

2020年11月9日

今回も、前回までと同様にパソコンに標準装備されているメモ帳だけを使って、あの有名なパズルゲーム「ぷよぷよ」を作り、本家に近づけていきたいと思います!

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


今回作るもの

前回は、ぷよが落ちる場所を表示するコードを書きましたね!

  1. 落ちる場所を表示しよう! ←前回
  2. 壁にぶつかっている状態でも回転できるようにしよう! ←今回
  3. 設置する前にすこし動かせるようにしよう!
  4. スコアを表示しよう!
  5. コードをまとめてきれいにしよう!

そして今回は、壁やぷよが横にある状態でも回転できるようにしていきたいと思います!

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

回したときに横にスペースがあれば横に移動して回転させ、横にスペースがなければ180度回転させるようになっています!

操作性が上がると気持ちいいですよ!


コードを書いていこう!

早速コードを書いていきましょう!

今回は前に書いた回転のコードに条件分岐を追加していくだけなので、前回に比べれば比較的簡単だと思います

ですが、回転のコードを忘れていると難しいかもしれませんので、忘れている方は復習しておきましょう☺


まずは左回転から!

ではまず左回転で、さらに横にスペースがあるときのコードから考えていきましょう。

壁があるときに左回転をすると…

このように右に移った場所を計算しないといけませんね。

ここで重要なのが、黒丸(親ぷよ)は、白丸(子ぷよ)が右に回転した場所に動いているということですね!

なので、左回転のコードにも、右回転の計算のコードを差し込まないといけないということですね!

あとは条件分岐で、移った場所にぷよがあるかをチェックして、適切な場所に移動させればOKです!

else if (e.key == "z" || e.key == "i") {
  let temp_x = Math.round(Math.cos((direction + 90) / 360 * 2 * Math.PI));
  let temp_y = -Math.round(Math.sin((direction + 90) / 360 * 2 * Math.PI));
  let opposite_x = Math.round(Math.cos((direction - 90) / 360 * 2 * Math.PI));
  let opposite_y = -Math.round(Math.sin((direction - 90) / 360 * 2 * Math.PI));
  if (position[0] + temp_x >= 0 && position[0] + temp_x <= 5 && position[1] + temp_y >= 0 && position[1] + temp_y <= 11) {
    if (field[Math.ceil(position[1]) + temp_y][position[0] + temp_x] == 0) {
      child = [temp_x, temp_y];
      direction = direction + 90;
    }else if (field[Math.ceil(position[1]) + opposite_y][position[0] + opposite_x] == 0) {
      position = [position[0] + opposite_x, position[1] + opposite_y];
      child = [temp_x, temp_y];
      direction = direction + 90;
    }
  }
}

「opposite_x, y」というのが、右回転をした場所の計算で、あとは回転した場所にぷよがあるかを条件分岐でチェックして、右に移った場所を「position」に入れています!

では次に、右にも左にもぷよがあるときを考えましょう。

この時には、180度回転させるようにします。

これは先ほどに比べたらすごく簡単で、条件分岐で先ほど以外の時に180度回転させるだけです!

また、180度回転させるには、sin、cosを使ってもいいんですが、単に例えば「child」が「0, 1」の時に「0. -1」にすればいいので…

else if (e.key == "z" || e.key == "i") {
  let temp_x = Math.round(Math.cos((direction + 90) / 360 * 2 * Math.PI));
  let temp_y = -Math.round(Math.sin((direction + 90) / 360 * 2 * Math.PI));
  let opposite_x = Math.round(Math.cos((direction - 90) / 360 * 2 * Math.PI));
  let opposite_y = -Math.round(Math.sin((direction - 90) / 360 * 2 * Math.PI));
  if (position[0] + temp_x >= 0 && position[0] + temp_x <= 5 && position[1] + temp_y >= 0 && position[1] + temp_y <= 11) {
    if (field[Math.ceil(position[1]) + temp_y][position[0] + temp_x] == 0) {
      child = [temp_x, temp_y];
      direction = direction + 90;
    }else if (field[Math.ceil(position[1]) + opposite_y][position[0] + opposite_x] == 0) {
      position = [position[0] + opposite_x, position[1] + opposite_y];
      child = [temp_x, temp_y];
      direction = direction + 90;
    }else {
      child[1] = -child[1];
      direction = direction + 180;
    }
  }
}

こんな感じになりますね!

これで横にぷよがある場合は完成です!

では次に横に壁があるときを考えましょう。

これは実はほぼ同じで、横に壁があるときには条件分岐が1つ外にあるので…

}else if (e.key == "z" || e.key == "i") {
  let temp_x = Math.round(Math.cos((direction + 90) / 360 * 2 * Math.PI));
  let temp_y = -Math.round(Math.sin((direction + 90) / 360 * 2 * Math.PI));
  let opposite_x = Math.round(Math.cos((direction - 90) / 360 * 2 * Math.PI));
  let opposite_y = -Math.round(Math.sin((direction - 90) / 360 * 2 * Math.PI));
  if (position[0] + temp_x >= 0 && position[0] + temp_x <= 5 && position[1] + temp_y >= 0 && position[1] + temp_y <= 11) {
    if (field[Math.ceil(position[1]) + temp_y][position[0] + temp_x] == 0) {
      child = [temp_x, temp_y];
      direction = direction + 90;
    }else if (field[Math.ceil(position[1]) + opposite_y][position[0] + opposite_x] == 0) {
      position = [position[0] + opposite_x, position[1] + opposite_y];
      child = [temp_x, temp_y];
      direction = direction + 90;
    }else {
      child[1] = -child[1];
      direction = direction + 180;
    }
  }else if (field[Math.ceil(position[1]) + opposite_y][position[0] + opposite_x] == 0) {
    position = [position[0] + opposite_x, position[1] + opposite_y];
    child = [temp_x, temp_y];
    direction = direction + 90;
  }else {
    child[1] = -child[1];
    direction = direction + 180;
  }
}

先ほど書いた2つの条件分岐を外にもつけてあげるだけです。

*少しややこしくなってしまいましたが、わかりやすさ重視で作っていますのでコードが汚いのはお許しください💦

これで自由に回転できるようになりましたね!

最後は右回転です!


最後は右回転!

では最後に右回転を作っていきましょう。

else if (e.key == "x" || e.key == "o") {
  let temp_x = Math.round(Math.cos((direction - 90) / 360 * 2 * Math.PI));
  let temp_y = -Math.round(Math.sin((direction - 90) / 360 * 2 * Math.PI));
  let opposite_x = Math.round(Math.cos((direction + 90) / 360 * 2 * Math.PI));
  let opposite_y = -Math.round(Math.sin((direction + 90) / 360 * 2 * Math.PI));
  if (position[0] + temp_x >= 0 && position[0] + temp_x <= 5 && position[1] + temp_y >= 0 && position[1] + temp_y <= 11) {
    if (field[Math.ceil(position[1]) + temp_y][position[0] + temp_x] == 0) {
      child = [temp_x, temp_y];
      direction = direction - 90;
    }else if (field[Math.ceil(position[1]) + opposite_y][position[0] + opposite_x] == 0) {
      position = [position[0] + opposite_x, position[1] + opposite_y];
      child = [temp_x, temp_y];
      direction = direction + 90;
    }else {
      child[1] = -child[1];
      direction = direction + 180;
    }
  }else if (field[Math.ceil(position[1]) + opposite_y][position[0] + opposite_x] == 0) {
    position = [position[0] + opposite_x, position[1] + opposite_y];
    child = [temp_x, temp_y];
    direction = direction + 90;
  }else {
    child[1] = -child[1];
    direction = direction + 180;
  }
}

基本的にはほとんど変わりませんね笑

回転の方向が逆なので、90度足すのか引くのかという部分に注意しましょう!


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

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

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

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


今回はここまで!

というわけで続編の第二回はここまでとしたいと思います!

次回は、下に設置する前に時間を設けて、ぷよを動かす猶予を作りたいと思います!

というわけで長くなってしまいましたが、

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

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

第三回はこちらから!