C++でつくったテトリスの完成品

これまではテトリミノが着地したらランダムで次のテトリミノのタイプを決めていました。しかしガイドラインではそうではなく7種類1つずつ合計7個でひとつのセット(これをバッグという)をつくり、バッグ内をランダムにシャッフルする方法をとっています。

だから同じ種類のテトリミノが3回連続して落ちてくることはありません。2回連続して同じタイプのテトリミノが落ちてきた場合は、そこにバッグの境界線があるということになります。またどのタイプのテトリミノであってもその後13回以内に同じものが落ちてくることが保証されています。

ではそのように作りかえてみましょう。

バッグをつくる

バッグをつくりましょう。これはゲームが開始されたとき、TetrisクラスのInit関数でやってしまうのがいいのではないでしょうか?

vectorクラスで対応

まずヘッダーファイルTetris.hに

と書きたいのですが、この場合はTetris.hをincludeするcppファイルであれば先にvectorをincludeしなければなりません。

を書かないといけません。

これ一行includeするだけでOKというall.hを作りましたが、そこに

とやっておけばよいでしょう。

バッグをつくるために必要な変数と関数

ゲームの初期化の処理をする関数です。InitFixedBlocksが長いのでInit関数全体が長くならないように独立させました。

Init関数内で呼び出しているCreateBag関数とNewTetrimino関数ですが、以下のようになっています。

CreateBag関数は7種類1個ずつを集めたもののなかからランダムでひとつずつ選んだものをm_vectorTetriminoTypesに格納していきます。

ストックが7個以下になったら新しいバッグをつくる

次に出現するテトリミノのタイプはm_vectorTetriminoTypesから最初の要素が取り出されてなくなっていきます。残りが7個以下になったら新しいバッグをつくってm_vectorTetriminoTypesに追加します。

Init関数内でNewTetrimino関数を呼び出しています。こうすることでゲームの最初に現れるテトロミノも固定されたものではなくランダムになります。

SetNewBlockType関数はInit関数によって呼ばれるだけでなく、テトリミノが着地したあとでも呼び出されます。次に降らせるテトリミノをm_CurTetriminoTypeにセットする関数です。m_vectorTetriminoTypesから先頭のものを取り出して使います。

これで同じ種類のテトリミノが3個以上連続で落ちてくることはなくなりました。

次に落ちてくるテトリミノをいくつか表示させる

次の課題ですが、次に落ちてくるNextブロックを右側に表示させます。4つくらい表示させればよいのではないかと考えています。

ヘッダーファイルに追加する関数を記述します。

ShowNext関数

まず次に落ちてくるNextブロックを描画するための関数をつくり、Tetris::Draw関数に追加します。

ShowNext関数は一番上に「NEXT」と表示し、次の4つ分のテトリミノを表示させる関数です。

DrawNextBlock関数

DrawNextBlock関数は左上の座標とテトリミノの種類を指定すればその位置にテトリミノを表示させる関数です。

GetColumnsRowsNextBlock関数

表示させるテトリミノはブロックが集まって構成されていますが、そのそれぞれのブロックを4×4のどの部分に表示させればいいのでしょうか? これを取得するのがGetColumnsRowsNextBlock関数です。

GetTetriminoColor関数

最後にテトリミノの色を取得しなければなりません。引数なしのGetTetriminoColor関数は現在落下中のテトロミノの色を取得することができますが、いまはこの関数は使えないので引数付きの似たような関数をつくりました。

テトリミノの初期座標の変更とこれにともなう変更

それから新しく落ちてくるテトロミノの初期位置ですが、いまは(0, 0)の位置にしていますが、普通のテトリスではX軸では真ん中あたり、Y軸ではもっと上になっています。

しかしこれではバグります。移動できるか回転できるかを判定するときにm_FixedBlocksのブロックがある部分と衝突していないかを調べているのですが、このとき配列の添え字に負数を指定するとエラーが発生します。

CanMove関数とCanRotate関数の変更

Tetris::CanRotate(BOOL isRight, int x, int y)関数でも似たようなところがあるので同じようにしてエラーを回避します。

フィールドよりも上にあるテトリミノは描画しない

もうひとつの問題はフィールドよりも上の部分は特別な場合を除き再描画の対象となっていません。対象に含めるために関数を書き直してもいいのですが、ブロックのうち、pos[i].m_rowが負数の場合はそもそも描画しないという方法で対応します。

それから着地したときにテトリミノを固定する処理でもyが負数のときにm_FixedBlocks[y][x]にpBlockをセットするとバグがおきます。そこで配列 m_FixedBlocksにpBlockをセットするときに確認の処理を追加しています。

あとはホールドの機能ですが、これは次回、ホールドの機能を実装する 猫並みの知能を得るためにC++でテトリスをつくる(6)でおこないます。