こんにちは、あんみんどうふです。
今回はスコアを実装します。やはりゲームにはこれが無いとやりがいがありませんね!
前回の記事はこちら。
全編見たい方はタグからどうぞ。
11-0.テトリスのスコアについて
作品によって異なりますが、本家テトリスには各消し方の得点が決められています。今回は「ぷよぷよテトリス」を基準にして作ります。
各種消し方の獲得スコアは以下の通りです。
名称 | 基本スコア |
---|---|
シングル(1列消し) | 100 |
ダブル(2列消し) | 300 |
トリプル(3列消し) | 500 |
テトリス(4列消し) | 800 |
Tスピンのみ | 400 |
Tスピンシングル | 800 |
Tスピンダブル | 1200 |
Tスピントリプル | 1600 |
REN(連続消し) | 50×REN数(最大1000) |
これらに加え、パーフェクトクリアやTスピンミニ、Back To Backのボーナスが加算されます。追加スコアは以下の通りです。
名称 | 追加スコア |
---|---|
パーフェクトクリア・シングル(1列消し) | +800 |
パーフェクトクリア・ダブル(2列消し) | +1000 |
パーフェクトクリア・トリプル(3列消し) | +1800 |
パーフェクトクリア・テトリス(4列消し) | +2000 |
Tスピンミニ | +100 |
Back To Back | ×1.5 |
(参考:Puyo Puyo Tetris – Hard Drop – Tetris Wiki)
ちなみに、Back To Backの乗算ボーナスはテトリスや各種Tスピンのみに適用されるので、追加スコアには乗算されません。
まとめると「((基本スコア+RENスコア)×BackToBack+追加スコア)」といった計算式になります。
また、消し方の他にもソフトドロップ、ハードドロップにも獲得スコアがあり、1マス進める度に2点獲得します。
こうやって見ると結構複雑ですが、前作ったスーパーローテーションよりはマシですね。今回はこれを実装します。
11-1.スコアを表示する
コードを書く前にスコアを表示させる領域が必要なので、デザイナーを開きLabelを適当な位置に配置します。nameは「scoreValue」にしました。
プロパティですが、BorderStyleをFixedSingleにして領域をわかりやすくし、AutoSizeをfalseにしてちょうどいいぐらいの大きさに変え、右揃えにするためにTextAlignをMiddleRightにしています。
ここがスコアだよ~、と一目でわかるように”SCORE”と書かれたLabelも置きました。
nameは「scoreStr」にしましたが、これには特にコードを書く予定はないのでお好みで。
コードに戻り、グローバル変数として以下の変数を追加します。
1 | int score = 0; // 現在のスコア |
scoreは現在のスコアを表します。そのまんま。
そしてフラグ関連の変数も追加します。これはuseTSpinとか書いている所にまとめておくとわかりやすいです。
1 2 | bool BackToBack = false; // BackToBackフラグ bool perfect; // パーフェクトクリアフラグ |
BackToBackはその名の通り、BackToBackを行ったかどうかのフラグです。
perfectもその名の通り、パーフェクトクリアのフラグです。そのまんま。
次に、獲得した点数をスコアに適用させるためのメソッド「scoring」を新規で作成し、以下のコードを入力します。
1 2 3 4 5 6 7 8 9 | /** * スコア処理 */ void scoring(int resultScore) { score += resultScore; if (score > 999999999) score = 999999999; // 上限 scoreValue.Text = score.ToString(); } |
スコアに引数resultScoreの値が加算され、合計が現在のスコアとしてされます。また、上限(99999999)を上回らないようにする処理も兼ねてます。
試しにスコアを獲得させてみます。
さっき作ったscoringの引数に「880」と入れてmainForm_Loadに追記して実行させます。
1 | scoring(880); |
880点がスコアとして入ってます!これでスコアを獲得する処理は出来上がりです。
ちなみに880という数字は私の一番好きなポケモン「パッチラゴン」の図鑑番号です。
11-2.ソフトドロップ・ハードドロップでスコア獲得
ソフトドロップ・ハードドロップで1マス移動する度に2点入るようにします。
ハードドロップですが、今のwhile文のままだと実装は難しいのでちょこっといじります。
while文を使わずに真下へ移動させるため、ゴースト描画で使ったminoGhostメソッドを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /** * ゴーストの高さを決定 * ------------------------------ * ret returnする値の切り替え * false = ゴーストの高さを返す * true = ゴーストまでの距離を返す */ int minoGhost(bool ret = false) { int result; int ghostY = 0; while (minoMoveCheck(2, 1, 0, ghostY)) ghostY++; if (!ret) result = ghostY + y; else result = ghostY; return result; } |
今まではゴーストの高さ(ゴースト自体のY座標)を返すだけでしたが、引数がtrueの場合には現在位置からゴースト位置の差分を返すようにしました。
次にKeyDownイベントのKeys.Upを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /* ミノ操作(KeyDown) */ private void mainForm_KeyDown(object sender, KeyEventArgs e) { if (gameStart) { switch (e.KeyCode) { case Keys.Up: int hDrop = minoGhost(true); // ハードドロップ移動量 int point = 2 * (hDrop + 1); // 獲得スコア minoMoving(2, hDrop); scoring(point); minoMoving(2, 1); break; |
変数hDropに差分の値を入れることでそのまま移動量として使えます。再利用できていいですね~。
獲得スコアは1マスにつき2点で、下移動を行った回数分乗算されます。
続けてKeys.Downも修正します。こちらは移動成功する度に2点加算させるだけでOKです。
1 2 3 4 5 6 | case Keys.Down: if(minoMoveCheck(2, 1) { minoMoving(2, 1); scoring(2); } break; |
ハードドロップで18マス移動しているので、2×(18+1)=38点入りました!
11-3.消し方によって獲得スコアを変える
最初に解説した各種消し方のスコアを獲得できるようにします。ついでにTスピン等の文字表示もここでやっちゃいます。
その前に、前回minoDropに追記したTスピン文字表示関連のコードは消しておきます。
新規メソッド「lineClear」を作成し以下のコードを入力します。
(コード中ではBackToBackをBTBと略しています。長いので・・・)
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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | /** * 各種消し方処理 * ------------------------------ * line 消去ライン数 */ void lineClear(int line) { string message = ""; int point = 0; bool btb = false; // BTB継続する消し方フラグ // Tスピン if (useTSpin) { message = "T-Spin\n"; switch (line) { case 0: point = 400; break; case 1: message += "Single"; point = 800; break; case 2: message += "Double"; point = 1200; break; case 3: message += "Triple"; point = 1600; break; } if (useTSpinMini) { message += "\nMini"; point += 100; } if(line != 0) btb = true; } // ライン消し else { switch(line) { case 1: point = 100; break; case 2: point = 300; break; case 3: point = 500; break; case 4: // テトリス message = "TETRIS"; point = 800; btb = true; break; } btb = false; } // BTBが継続中に対応した消し方をしたらボーナス if (BackToBack && btb) point = (int)Math.Round(point * 1.5); // それ以外の消し方をしたらBTB終了 else if (!btb) BackToBack = false; // REN if (line > 0) { if (ren > 0) { ren++; if (message == "") message = ren.ToString() + " REN"; } else ren++; point += 50 * ren; } else ren = 0; // パーフェクトクリアをチェック perfect = true; for(int i = 0; i < FIELD_HEIGHT && perfect; i++) { for(int j = 0; j < FIELD_WIDTH && perfect; j++) { if (field[i + FIELD_SPACE, j + FIELD_WALL] != 0) { perfect = false; } } } if(perfect) { message = "PERFECT\nCLEAR"; switch(line) { case 1: point += 800; break; case 2: point += 1000; break; case 3: point += 1800; break; case 4: point += 2000; break; } } // BTBしたならメッセージに追記 if(BackToBack && btb) message += "\n+\nBack To\nBack"; // BTB系の消し方かREN継続かパーフェクトクリアならメッセージ表示 if(btb || perfect || ren > 1) statusView(); statusMsg = message; scoring(point); // BTBがfalseの時に対応した消し方をしたならtrue if(!BackToBack && btb) BackToBack = true; } |
「Tスピン→ライン消し(もしくはテトリス)→BTBボーナス→REN→パーフェクトクリア→スコア反映」の順に処理しています。
Back To Backのボーナスは先述の通り、Tスピンとテトリスにしか反映されないのでそれらの後に行います。
また、スコアに反映した後にBack To Back対象の消し方(Tスピンorテトリス)をしていた場合にフラグを立たせています。このフラグがtrueの時にTスピンかテトリスを行うと、Back To Backのボーナスを獲得できます。
あとはminoDropに追記して・・・
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 | void minoDrop() { if (gameStart) { // フィールドに描画 minoDrawing(next[0], dir); // NEXTを進める nextToNext(); // ライン消去(ライン消去数取得) int line = minoDelete(); // 文字描画処理&スコア獲得 lineClear(line); statusView(); // 座標・向きの初期化 x = INITIAL_X; y = INITIAL_Y; dir = 0; // 特殊消しフラグを初期化 useTSpin = false; useTSpinMini = false; // ホールドフラグの初期化 usedHold = false; // 自由落下リセット if(gameStart && !DEBUG_MODE) { freeFall.Stop(); freeFall.Start(); } // この時点でミノが重複していればゲームオーバー if (!minoDuplicateCheck()) GameOver(); } } |
できました!ハイスコアを目指す遊び方ができるので、ゲームとしてまた一つ進歩しました。
やりたいことはまだまだあるので続きます。次はDAS、ARRの実装です。
特殊テクニックとかそういうのではなく、テトリスの細かい仕様です。詳しくはまた次回。
#12 DAS・ARR編へ続く。