ブロックを消していくと、次のようになる事があります。
この図では、1列しか空いていませんがブロックの並びによっては一気に数列空く事もあります。
この空いた列は右から左へ詰める必要があります。
「Samegame.h」を開き、必要なprivateメンバ関数を追加します。
//ブロックを落とす void FallBlock(); //空いた列を詰める void SlideLine(); //ブロックが存在する列を探す int SearchExistBlockLine(const int targetLine) const; //指定した列を入れ替える void ChangeLine(const int destLine, const int sourLine); }; |
3つの関数で処理を作ります。
「Samegame.cpp」の「FallBlock関数」の下に「SlideLine関数」を追加します。
//空いた列を詰める
void Samegame::SlideLine()
{
for (int j = 0; j < COL - 1; j++) {
if (m_field[ROW - 1][j].m_color == NO_BLOCK) {
int existBlockLine = SearchExistBlockLine(j);
if (existBlockLine) {
ChangeLine(j, existBlockLine);
}
}
}
}
|
まずは、添え字「j」を使って横方向に配列の左端から右端−1まで調べます。
「空いている列」は一番下のブロックを調べれば分かります。
ブロックが残っていれば下に落ちているはずですからね。
「空いている列」が見つかったら、「SearchExistBlockLine関数」を呼び出します。
「SearchExistBlockLine関数」は空いている列より右側にある、ブロックが存在している列を探します。
ブロックが存在している列が見つかったら、列を丸ごと入れ替えます。
「Exist」とは「存在している」と言う意味です。
引数として「空いている列」の添え字を受け取ります。
上でも説明したとおり、空いている列より右側にある、ブロックが存在している列を探します。
「SlideLine関数」の下に「SearchExistBlockLine関数」を追加します。
//ブロックが存在する列を探す
int Samegame::SearchExistBlockLine(const int targetLine) const
{
for (int j = targetLine + 1; j < COL; j++) {
if (m_field[ROW - 1][j].m_color != NO_BLOCK) {
return j;
}
}
return 0;
}
|
受け取った列の添え字の右隣から、右端まで調べていきます。
ここでもブロックがある列は一番下の列さえ調べれば分かります。
ブロックのある列が見つかったら、添え字を返します。
見つからない場合「0」を返しています。
「0」は左端の添え字ですが、左端にブロックが無くなると言う事は「全てのブロックが消えた」状態ですからブロックが存在する列は無いと言う事になります。
「SlideLine関数」の呼び出しを再度確認します。
int existBlockLine = SearchExistBlockLine(j); if (existBlockLine) { ChangeLine(j, existBlockLine); } |
「SearchExistBlockLine関数」の戻り値が「0」以外であれば「ChangeLine関数」を呼び出します。
「空いている列」と「ブロックが存在している列」を入れ替えます。
「SearchExistBlockLine関数」の下に「ChangeLine関数」を追加します。
//指定した列を入れ替える
void Samegame::ChangeLine(const int destLine, const int sourLine)
{
Block work;
for (int i = ROW - 1; i >= 0; i--) {
if (m_field[i][sourLine].m_color == NO_BLOCK) {
break;
}
work = m_field[i][destLine];
m_field[i][destLine] = m_field[i][sourLine];
m_field[i][sourLine] = work;
}
}
|
引数は「空いている列」と「ブロックが存在している列」の添え字です。
入れ替えも最下段から最上段に向けて行います。
ブロックは下に集まっているはずですから、途中で「空のブロック」が出たら入れ替え終了です。
※無駄な処理は極力省くよう工夫しましょう。
「DeleteBlock関数」に関数呼び出しを追加します。
//ブロックを消す void Samegame::DeleteBlock() { if (m_selectBlockNum <= 1) { return; } DeleteSelectBlock(); FallBlock(); SlideLine(); } |
では実行してみましょう。
<実行結果 クライアント領域のみ>
「空いている列」が左にスライドすればOKです。
今回は3つの関数に分けて作りましたが、これは重要な事なので説明を追加しておきます。
「SlideLine関数」を1つの関数で書こうとすると四重ループになってしまいます。
※展開してみれば分かります。
プログラムは機能ごとに関数に分ける、なるべく分かりやすい書き方をする、と言うのはプログラムの基本です。
二重ループを超えた、三重ループや四重ループはプログラムの可読性(読みやすさ)を下げる要因だと思います。
あまりにループが重なるようであれば、躊躇せず関数に分けるべきです。