こんにちは、あんみんどうふです。
今回はミノを即時落下させる「ハードドロップ」、ミノを1つだけ保持できる「ホールド」の実装を行います。
前回の記事はこちら。
全編見たい方はタグからどうぞ。
7-0.各機能のおさらい
「ハードドロップ」は上方向に1回入力することで、操作しているミノを一番下まで即時落下させるものです。テトリスに慣れてくると8割ぐらいはこれで落下させます。
ちなみに下方向に入力し続けることで、即時ではないですが普通よりも高速で落下させることを「ソフトドロップ」と呼びます。本家のテトリスにより近づけたいならこれも必要ですが、Windowsの仕様で勝手にそれっぽくなっているので今のところは保留します。
「ホールド」は操作しているミノを1つだけ取っておくことができる機能です。例えるとポケットに1つしまっておくみたいな感じです。
ホールドが空の時はそこにミノを保持してNEXTを次に進めますが、既に入っているときはホールド内と操作中のミノが入れ替わります。
また、ミノを設置し終えるまでに一回しか使えない制限もあります(設置したら再度使えます)。
どんなものかを再確認したところで早速作っていきます。
7-1.ハードドロップ
上に入力したら発動します。キーを司る部分といったらアレっしょ!ということで、KeyDownイベントをいじります。
上キーを押したら何かにぶつかるまでずっと下方向へ移動させます。
while文だけだとぶつかったらその場で止まってしまうので、もう一歩進ませると設置処理をしてくれます。これだけ。
1 2 3 4 5 6 7 8 9 | /* ミノ操作(KeyDown) */ private void mainForm_KeyDown(object sender, KeyEventArgs e) { switch (e.KeyCode) { case Keys.Up: while(minoMoveCheck(2, 1)) minoMoving(2, 1); minoMoving(2, 1); break; |
7-2.ホールド
ホールドも描画する必要があるので、デザイナーを開きpictureboxを一つ追加します。NEXTのpictureboxをコピペし、nameは「view_hold」にしました。
場所はフィールドの左上隣に配置します。これに関してはガイドライン通りです。
コードに戻り、グローバル変数の所に変数holdを追加します。これはホールド中のミノを表します。
そしてもう一つ、変数usedHoldを追加します。ホールドはミノを1回設置し終えるまでに一度しか使えないので、bool型変数でフラグを管理します。
1 2 | int hold = 0; // ホールド内のミノ(0=空) bool usedHold = false; // ホールド使用フラグ |
次に、新しいメソッドholdDrawingを追加します。その名の通りホールドの描画です。
コードもnextDrawingからコピペして若干修正したぐらいです。
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | /** * ホールド描画 */ void holdDrawing() { const int SQUARE_HOLD = 10; // ホールドの1マスの大きさ int[,] holdField = new int[6, 6]; // 描画フィールド for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { holdField[i + 1, j + 1] = mino[hold, 0, i, j]; } } Bitmap canvas = new Bitmap(view_hold.Width, view_hold.Height); Graphics g = Graphics.FromImage(canvas); for (int i = 0; i < holdField.GetLength(0); i++) { for (int j = 0; j < holdField.GetLength(1); j++) { Color color = Color.White; switch (holdField[i, j]) { case 0: // 空白 color = Color.FromArgb(255, 255, 255); break; case 1: // Iミノ color = Color.FromArgb(0, 255, 255); break; case 2: // Oミノ color = Color.FromArgb(255, 255, 0); break; case 3: // Sミノ color = Color.FromArgb(0, 221, 0); break; case 4: // Zミノ color = Color.FromArgb(255, 0, 0); break; case 5: // Jミノ color = Color.FromArgb(30, 128, 255); break; case 6: // Lミノ color = Color.FromArgb(255, 140, 0); break; case 7: // Tミノ color = Color.FromArgb(255, 0, 255); break; case 8: // 壁 color = Color.FromArgb(0, 0, 0); break; } SolidBrush brush = new SolidBrush(color); // 四角形を描画 g.FillRectangle(brush, j * SQUARE_HOLD - 5, i * SQUARE_HOLD - 5, SQUARE_HOLD, SQUARE_HOLD); brush.Dispose(); } } g.Dispose(); view_hold.Image = canvas; } |
そして処理の部分を書くために新しくholdControlメソッドを作成します。
一旦フィールドに描画している現在ミノを削除し、ホールド中のミノと現在ミノを交換します。もしホールドが空の場合、ホールドに現在ミノを入れて次のNEXTに進めるためnextToNextメソッドを呼び出しています。
また、一度設置するまで1回しか使えないようにするため、先ほど作ったbool型変数「hold」でフラグの管理をします。
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 37 38 39 40 41 42 43 44 | /** * ホールド */ void holdControl() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { // 現在位置に描画しているミノを一旦削除 int target = field[i + y + FIELD_SPACE - 1, j + x + FIELD_WALL]; if (target == mino[next[0], dir, i, j]) { field[i + y + FIELD_SPACE - 1, j + x + FIELD_WALL] = 0; } } } if (hold == 0) { // ホールドに現在ミノを格納 hold = next[0]; // NEXTを進める nextToNext(); } else { // ホールドと現在ミノを交換 int tmp = next[0]; next[0] = hold; hold = tmp; } // 座標・向きのリセット x = INITIAL_X; y = INITIAL_Y; dir = 0; holdDrawing(); minoDrawing(next[0], 0); // ホールド使用フラグON usedHold = true; } |
最後に、minoDropに”ホールド使用フラグ”を解除するように追記します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /** * テトリミノ設置 */ void minoDrop() { // フィールドに描画 minoDrawing(next[0], dir); // NEXTを進める nextToNext(); // ライン消去 minoDelete(); // 座標・向きの初期化 x = INITIAL_X; y = INITIAL_Y; dir = 0; // ホールドフラグの初期化 usedHold = false; } |
ハードドロップとホールドが完成しました。これで安定して開幕パフェが組めますね。最後回転入れ1回ミスってるけど…
欲しかった機能はだいたい作り終えましたが、テトリスプレイヤーからするとまだまだ違和感が抜けきっていません・・・。
というわけで、次回はマス目とゴーストの実装をします。ゴーストとは、現在位置から落下させた時のプレビューみたいなものをリアルタイムで表示させる機能です。
#8 マス目・ゴースト実装編へ続く(7月12日投稿予定)。
↓ちょっとした番外編です。