あんみんどうふです。番外編です。
今回は今作っているテトリスのデバッグに便利な機能を実装します。別に必要ないけど自己満足で作ります。
前回の記事はこちら。
全編見たい方はタグからどうぞ。
#○.5シリーズについて
いつも数字で区切って機能の実装等してますが、この0.5区切りの投稿では「別に必要ないけどあれば便利な機能」の実装をしています。主に開発側が楽できるようなものなので、今作っているテトリス自体に影響はあまりありません。
 興味ない方はすっ飛ばして構わないです。
デバッグモードとは
処理を正しく実行できるかを確認したり、このように実行したらどうなるか等のテスト・デバッグを支援するモードのことです。
実際のゲームにも存在し、昔のゲームであればチートコードやバグ技等で呼び出すことができます(今のゲームはほとんど無いです)。
 「ゼルダの伝説 ムジュラの仮面」のRTAでは2019年当時、手順を踏むことでデバッグメニューを表示させ、自由自在にアイテムを取得する技が使われていました…。
今回作るのはデバッグモードでミノを選んだりフィールドを自由に改変したりできるようにする機能です。
 Tスピンのテストをするためにいちいちミノなんて積み上げていられません!
デバッグモード切替
グローバル変数として「DEBUG_MODE」を追加します。
1  | bool DEBUG_MODE = false;  | 
これがtrueの間はデバッグモードが有効になります。
といっても、これだけだとデバッグモードが有効化されているのかわかりづらいので、Loadイベントの最後に以下のコードを追記します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17  | private void mainForm_Load(object sender, EventArgs e) {     ...     x = INITIAL_X;  // 初期X座標設定     y = INITIAL_Y;  // 初期Y座標設定     nextDecide();   // NEXTの設定     nextDrawing();  // NEXTの描画     holdDrawing();  // ホールドの描画     minoDrawing(next[0], 0);    // フィールド・ミノ描画     // デバッグモード     if (DEBUG_MODE)     {         this.Text += " - Debug Mode";     } }  | 
デバッグモードが有効の時、フォームのタイトルバーに「 – Debug Mode」という文字列が追加されます。
ミノの種類変更
デバッグモード有効時のみ、操作中のミノを変更できるようにします。
 変更の操作は数字キーで行いたい為、KeyDownイベントに追記します。
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  | /* ミノ操作(KeyDown) */ private void mainForm_KeyDown(object sender, KeyEventArgs e) {     ...     // デバッグ用・NEXT操作     if(DEBUG_MODE)     {         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;                 }             }         }         switch (e.KeyCode)         {             case Keys.D1:                 next[0] = 1;                 break;             case Keys.D2:                 next[0] = 2;                 break;             case Keys.D3:                 next[0] = 3;                 break;             case Keys.D4:                 next[0] = 4;                 break;             case Keys.D5:                 next[0] = 5;                 break;             case Keys.D6:                 next[0] = 6;                 break;             case Keys.D7:                 next[0] = 7;                 break;         }         nextDrawing();         minoDrawing(next[0], dir);     } }  | 
できました。落とすミノを全部Iミノにして遊ぶこともできますね。
マウスでフィールドを自由に編集
ドット絵を描くペイントツールのように、マウスでフィールド内を編集できるようにします。
 処理はTimerで反復処理させることでリアルタイムに編集できているように見せます。
まず、グローバル変数として以下の変数を追加します。
1 2 3  | int DEBUG_targetX;      // 対象X座標 int DEBUG_targetY;      // 対象Y座標 int DEBUG_mouseClick;   // マウスをクリックしている場所(0=なし, 1=左, 2=右)  | 
マウスをクリックした位置の座標と、クリックされているボタンを表す変数です。これから追加するイベントで使います。
デザイナーを開き、view_fieldから新規でMouseDownイベント、MouseUpイベント、MouseMoveイベントを追加します。
 そして、Timerを追加しnameを「debugTimer」、Intervalを「10」にし、Tickイベントを追加します。
コードに戻り、MouseDownイベントに以下のコードを記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17  | private void view_field_MouseDown(object sender, MouseEventArgs e) {     if (DEBUG_MODE)     {         switch (e.Button)         {             case MouseButtons.Left:                 DEBUG_mouseClick = 1;                 debugTimer.Enabled = true;                 break;             case MouseButtons.Right:                 DEBUG_mouseClick = 2;                 debugTimer.Enabled = true;                 break;         }     } }  | 
MouseDownイベントはマウスのボタンが押されたタイミングで呼び出される処理で、ここで左クリックされたか右クリックされたかを判定しています。
 また、debugTimerを開始(Enabled = true)させることでフィールドを編集する処理が行われます。
ちなみに、左クリックがブロック生成、右クリックがブロック消去のつもりです。
次にMouseUpイベントへコードを書きこみます。
1 2 3 4 5 6 7 8  | private void view_field_MouseUp(object sender, MouseEventArgs e) {     if (DEBUG_MODE)     {         DEBUG_mouseClick = 0;         debugTimer.Enabled = false;     } }  | 
MouseUpイベントはマウスのボタンが離されたタイミングで呼び出される処理で、debugTimerを停止させることで編集の処理を止めています。
次にMouseMoveイベントです。
1 2 3 4 5 6 7 8 9 10 11 12  | private void view_field_MouseMove(object sender, MouseEventArgs e) {     if (DEBUG_MODE)     {         DEBUG_targetX = e.Location.X / SQUARE;         DEBUG_targetY = e.Location.Y / SQUARE;         if (DEBUG_targetX < 0) DEBUG_targetX = 0;         else if (DEBUG_targetX >= FIELD_WIDTH) DEBUG_targetX = FIELD_WIDTH - 1;         if (DEBUG_targetX < 0) DEBUG_targetY = 0;         else if (DEBUG_targetX >= FIELD_HEIGHT) DEBUG_targetY = FIELD_HEIGHT - 1;     } }  | 
MouseMoveはフィールド上でマウスを動かしている時に呼び出される処理で、ブロックを生成する座標を決めています。
最後にdebugTimerのTickイベントのコードを書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19  | private void debugTimer_Tick(object sender, EventArgs e) {     if (DEBUG_MODE)     {         switch (DEBUG_mouseClick)         {             case 1:                 field[DEBUG_targetY + FIELD_SPACE, DEBUG_targetX + FIELD_WALL] = 8;                 break;             case 2:                 if (field[DEBUG_targetY + FIELD_SPACE, DEBUG_targetX + FIELD_WALL] != 0)                 {                     field[DEBUG_targetY + FIELD_SPACE, DEBUG_targetX + FIELD_WALL] = 0;                 }                 break;         }         minoDrawing(next[0], dir);     } }  | 
debugTimerのIntervalに設定した時間が経過する度に実行される処理です。Intervalは「10」に設定したので、10ミリ秒=0.01秒ごとに実行されます。
 これだけ短い間隔にすればリアルタイムっぽい編集になるでしょう。
ここではMouseDownで取得した「押されたマウスのボタン」からブロックを生成するか消去するかを決めて処理し、最後に描画しています。
 生成・消去する座標はMouseMoveで取得しています。
できました。自由にマウスで盤面をいじれるので、Tスピンのテストにもってこいですね。
とりあえずこれだけ作っておけばだいぶ楽できそうです。いちいちTミノが出るまでリセットしなくて済みます。
無効化するのもDEBUG_MODEの値をfalseにするだけなので切り替えは簡単ですね。
というわけで自己満足のデバッグモードでした。
 #9 ゲームオーバー+α編へ続く。

