こんにちは、あんみんどうふでーす。
前回はテトリスを遊ぶ上で欠かせない「フィールド」を作りましたが、今回はもっと重要な要素である「テトリミノ」を実装します。
前回の記事はこちら↓
全編見たい方は「新人プログラマーはテトリスを作れるのか?」というタグにまとめてますのでそちらからどうぞ。
2.各種ミノを作る
2-1.全種類全パターンの宣言
ミノは先述の通り7種類ですが、各種類に4方向の「向き」があります。上、右、下、左の4つです。まずはこれを宣言します。
フィールドを作る際に縦横を表す二次元配列を使いましたが、今回ミノでは「ミノの種類、ミノの向き、何列目、値」が必要なパラメータとなっているので、四次元配列を使うことになります。正直これ頭こんがらがりそうになりながら作ってました。
例えば、Tミノ(7)はこんな感じになってます。全部書くと超長いので他のミノは省略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | int[,,,] mino = { ... // Tミノ { // 上 { { 0, 0, 0, 0 }, { 0, 7, 0, 0 }, { 7, 7, 7, 0 }, { 0, 0, 0, 0 } }, // 右 { { 0, 0, 0, 0 }, { 0, 7, 0, 0 }, { 0, 7, 7, 0 }, { 0, 7, 0, 0 } }, // 下 { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 7, 7, 7, 0 }, { 0, 7, 0, 0 } }, // 左 { { 0, 0, 0, 0 }, { 0, 7, 0, 0 }, { 7, 7, 0, 0 }, { 0, 7, 0, 0 } } } }; |
これを全種類ミノ、全方向分宣言します。各方向パターンはTetrisちゃんねる様を参考にしています。
しかし、このままだとミノの色番号とミノ自体の配列番号が合いません。例えばIミノは1を割り当てていますが、配列minoの番号は0です。
ややこしくなるので、配列の一番最初に値が空っぽのダミーデータを入れておくことにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | int[,,,] mino = { // dummy { { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, ... |
2-2.座標の概念
作ったミノを実際にフィールドに配置します・・・いや、したいのですが、実はまだ座標の概念が無いので配置も移動もできません。
というわけで”概念”を作りましょう。これだけ見ると物凄いパワーワードのように感じます・・・。
グローバル変数に追記でX座標、Y座標に当たる変数x、yを宣言します。ついでに初期座標を表す定数も作ります。
1 2 3 4 5 6 7 8 9 10 11 | /* グローバル変数 */ const int FIELD_HEIGHT = 20; // フィールドの高さ const int FIELD_WIDTH = 10; // フィールドの幅 const int FIELD_WALL = 1; // 壁の厚さ const int FIELD_FLOOR = 1; // 床の厚さ const int FIELD_SPACE = 2; // 上の空間 const int SQUARE = 20; // 1マスの大きさ const int INITIAL_X = 3; // 初期X座標 const int INITIAL_Y = 0; // 初期Y座標 int x = INITIAL_X; // 現在のX座標 int y = INITIAL_Y; // 現在のY座標 |
X座標が3なのはミノを中央から出現させたいからです。左端から湧くのもなんか変だし。
次にミノ描画メソッドを作ります。引数m、dでミノの種類と向きを数値で呼び出せるようにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void minoDrawing(int m, int d) { for(int i = 0; i < 4; i++) { for(int j = 0; j < 4; j++) { // 空白で上書きしないように0の時はスキップ if (mino[m, d, i, j] != 0) { field[i + y + FIELD_SPACE - 1, j + x + FIELD_WALL] = mino[m, d, i, j]; } } } draw(); } |
if文を挟んでますが、これは参照するミノの値が0、即ち空白である時に処理を行わないようにするものです。
このままだと既に存在するミノの隣でminoDrawingメソッドを呼び出した時に配列minoの値をそのまま持ってきてしまうので空白で上書きしてしまいます。それを防ぐためのif文です。
とりあえずできたっぽいので試しにメソッドを呼び出します。Zミノ(4)を右向き(1)で呼び出してみます。
「minoDrawing(4, 1)」と書けばいいですね。
お~できてる。じゃあTミノ(7)を左向き(3)で。minoDrawing(7, 3)。
できてますね。
これで好きなミノを好きな向きで簡単に呼び出せるようになりました。テトリミノ完成!
せっかくなので・・・。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | y = 18; x = -1; minoDrawing(2, 0); // O-1 x = 3; minoDrawing(6, 0); // L-1 x = 7; minoDrawing(5, 0); // J-1 y = 17; x = 5; minoDrawing(1, 3); // I-1 x = 3; minoDrawing(3, 0); // S-1 x = 7; minoDrawing(4, 0); // Z-1 y = 15; x = 3; minoDrawing(7, 0); // T-1 x = -1; minoDrawing(5, 1); // J-2 x = 7; minoDrawing(1, 1); // I-2 x = 6; minoDrawing(2, 0); // O-2 y = 14; x = 4; minoDrawing(4, 0); // Z-2 y = 13; x = 2; minoDrawing(6, 3); // L-2 y = 9; x = -1; minoDrawing(7, 1); |
お~~、完璧に描画できてます!
ちなみにこの形、テトリスでは「DT砲(ダブルトリプル砲)」と呼ばれています。Tミノを回転入れで左端のくぼみの中に入れて、中でTスピンダブルをします。更に、もう一つのTミノを回転入れすると3列消えるTスピントリプルができます。
これが非常に強力で、ダブルの火力とトリプルの火力を続けて出すことができるうえに、Tスピンを連続で行うと「Back To Back」が発生し、更に火力がアップします。
これが決まると画面の半分ぐらいはお邪魔ミノで埋まりますね。一番下の土台となるOミノ、Lミノ、Jミノが最初に揃っていれば開幕で組むことも可能です。こちらは「開幕DT砲」と呼ばれています。
・・・余談でした。
次回はNEXTを実装しつつミノを移動できるようにします。開幕TSDやDT砲の形が組めちゃう日も近いです。
#3 NEXT・移動実装編へ続く。