広告
↓発売日:2018年09月22日↓
新品価格 |
今回はブロックの種類を増やすのと横一列に揃ったブロックを消去していきます。
ブロックの種類を増やします。
int blocks[BLOCK_HEIGHT * 6][BLOCK_WIDTH * 4] = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0}, {0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,0,0,0,1,0,0,1,0,0,0,1,1,0}, {0,0,1,0,0,1,1,0,0,1,1,0,0,1,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,0,0,1,1,0,0,0,1,0,0,1,0,0}, {0,1,0,0,0,0,1,0,0,1,1,0,0,1,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,0,1,0,0,1,1,0,0,0,0,0,1,1,0}, {0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0}, {0,0,0,0,0,0,1,1,0,1,0,1,0,1,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0}, {0,1,1,0,1,1,1,0,1,1,0,0,1,1,1,0}, {0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0}, {0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0}, {0,1,0,0,1,1,1,1,0,1,0,0,1,1,1,1}, {0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0}, {0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0} };
そのまま2次元配列を拡張するカタチでブロックの回転状態とともにブロックの種類を6種類に増やしてみました。
という事で今回からこのブロックの元の2次元配列からランダムにブロックを取り出してみたいと思います。
void my_make_block(){ int x,y; if(make_block_flag == 1){ block_id = (rand() % 6); for(y=0;y<BLOCK_HEIGHT;y++){ for(x=0;x<BLOCK_WIDTH;x++){ block[y][x] = blocks[(block_id * BLOCK_HEIGHT) + y][x]; } } make_block_flag = 0; } }
ブロック生成の「my_make_block()」を変更しました。
グローバル変数として現在のブロックの種類を表す変数
int block_id;
を用意しておきます。
ランダムにブロックを発生させるので乱数を使います。
block_id = (rand() % 6);
今回6種類のブロックになるので発生させる値は「0~5」までの6つになりますね。
乱数の種を作る「srand((unsigned)time(NULL));」文は初期化の「my_init_var()」に設置しました。
そして発生させた乱数値を「block_id」に保存します。
あとは「block_id」の値を使ってさきほどの2次元配列「blocks」からブロックを読み出すだけです。
for(y=0;y<BLOCK_HEIGHT;y++){ for(x=0;x<BLOCK_WIDTH;x++){ block[y][x] = blocks[(block_id * BLOCK_HEIGHT) + y][x]; } }
回転状態を表す「x」の位置はそのままでブロックの種類を表す「y」の位置だけ「ブロックの種類番号 × ブロックの高さ」という計算でその特定の場所からブロック情報を読み出す事ができます。
これでランダムにブロックを発生させる事ができました。
続いて今回のメインテーマ、ブロック消去処理をやっていきます。
まずは消去すべき横一列にブロックが揃った列を探します。
void my_search_line(){ int i,j; for(i=0;i<FIELD_HEIGHT - 3;i++){ clear_line_point[i] = 0; } for(i=0;i<FIELD_HEIGHT - 3;i++){ for(j=3;j<FIELD_WIDTH - 3;j++){ if(stage[i][j] == 0){ clear_line_point[i] = 1; break; } } } for(i=0;i<FIELD_HEIGHT - 3;i++){ if(clear_line_point[i] == 0){ clear_flag = 1; break; } } }
横一列ブロックが揃っている列を探す「my_search_line()」です。
グローバル変数に揃った列の場所を入れる為の変数
int clear_line_point[20];
とクリア状態かどうかのフラグ
int clear_flag;
を用意します。
「clear_line_point」は「stage[23][18]」の内側だけを調べるので要素数は「20」となっております。
まずは「clear_line_point[20]」を初期化
for(i=0;i<FIELD_HEIGHT - 3;i++){ clear_line_point[i] = 0; }
そして「stage[23][18]」の内側だけを調べるようなカタチで横一列ブロックが揃っている列を探します。
for(i=0;i<FIELD_HEIGHT - 3;i++){ for(j=3;j<FIELD_WIDTH - 3;j++){ if(stage[i][j] == 0){ clear_line_point[i] = 1; break; } } }
ここで横一列が揃っている状態というのは逆に言えば空白を表す「0」がその列に一つもない状態の事なので「0」が一つでもあれば「clear_line_point[i] = 1;」というように目印をつけて次の列へというように全ての列を探索していきます。
結果「clear_line_point[i] = 0」の列が消去すべき列になるというワケです。
そしてその結果を調べてクリア状態に入るかどうかを「clear_flag」へ格納します。
for(i=0;i<FIELD_HEIGHT - 3;i++){ if(clear_line_point[i] == 0){ clear_flag = 1; break; } }
これでクリアすべき列の探索はおしまいです。
ブロック固定の「my_fix_block()」にこちらの関数を加えます。
void my_fix_block(){ int x,y; my_collision_bottom(); if(collision_flag != 0){ my_save_block(); my_search_line(); if(clear_flag == 0){ my_init_var2(); } } }
「my_save_block()」の後に加えるトコロに注意です。
そして前回まではそのまま「my_init_var2()」によりブロック座標等を元に戻しておりましたが今回からクリア処理が入るので少し違った分岐をします。
広告
クリアすべき列がわかったのですぐにクリア処理に入りたいトコロですが列が消えている事がわかるように少しの間がほしいと思います。
そしてそのクリアしている間というのはキー入力処理やブロック再生成処理など、もろもろの処理を禁止にしなければいけません。
なので「int main()」にて通常時とクリア状態時の処理を分岐します。
int main(){ my_init_var(); while(gameover_flag == 0){ my_clear_field(); if(clear_flag == 0){ my_make_block(); my_gameover(); my_get_key(); my_make_field(); my_fix_block(); my_fall_block(); } else{ my_clear_line(); my_make_field2(); } my_draw_field(); my_timer(); } printf("gameover\n"); return 0; }
さきほどの「clear_flag」を使って分岐させております。
それではクリア状態時の処理を見ていきます。
揃った列をクリアして入れ替え処理を行う「my_clear_line()」になります。
ちょっと長いので前半、後半に分けて説明します。
まずは揃った列の部分をクリアして表示したいので、その列を空白を表す「0」に置き換えます。
void my_clear_line(){ int i,j; int remain_line_point[20] = {0}; int remain_line_index = 0; if(clear_count < 2){ for(i=0;i<FIELD_HEIGHT - 3;i++){ if(clear_line_point[i] == 0){ for(j=3;j<FIELD_WIDTH - 3;j++){ stage[i][j] = 0; } } } clear_count++; } else{ /*クリアした場所の入れ替え処理*/ } }
さきほどクリアすべき列は「clear_line_point」に保存したのでこれを調べます。
for(i=0;i<FIELD_HEIGHT - 3;i++){ if(clear_line_point[i] == 0){ for(j=3;j<FIELD_WIDTH - 3;j++){ stage[i][j] = 0; } } }
「clear_line_point[i] == 0」の場所がクリアすべき列になりますね。
そこを空白を表す「0」に全て置き換えます。
そしてクリアしている時間をカウントする為の「clear_count」を用意してお好みの間クリア状態にします。
「int main()」の方ではクリアした「stage[23][18]」の状態を「field[23][18]」に反映させなくてはいけないので「my_make_field()」の「stage[23][18]」のみを「field[23][18]」に重ねるバージョン
void my_make_field2(){ int i,j; for(i=0;i<FIELD_HEIGHT;i++){ for(j=0;j<FIELD_WIDTH;j++){ field[i][j] = stage[i][j]; } } }
で「stage[23][18]」の状態を毎回「field[23][18]」に反映させます。
ちなみにさきほどクリアすべき場所を「0」で置き換えた時に上手く別の数字で置き換えてその数字に対応する絵柄など用意しておけば少し違ったクリア処理ができるかもしれません。
では最後の後始末としてクリアした場所の入れ替え処理を行っていきます。
さきほどの「else」部分のみ抜粋です。
for(i=FIELD_HEIGHT - 4;i >= 0;i--){ if(clear_line_point[i] != 0){ remain_line_point[remain_line_index] = i; remain_line_index++; } } remain_line_index = 0; for(i=FIELD_HEIGHT - 4;i >= 0;i--){ for(j=3;j<FIELD_WIDTH - 3;j++){ stage[i][j] = stage[remain_line_point[remain_line_index]][j]; } remain_line_index++; } clear_flag = 0; clear_count = 0; my_init_var2();
今度は残すべき列を用意した変数「int remain_line_point[20]」に保存します。
for(i=FIELD_HEIGHT - 4;i >= 0;i--){ if(clear_line_point[i] != 0){ remain_line_point[remain_line_index] = i; remain_line_index++; } }
どういう事かと言いますと例えば次の列番号があったとして
[0][1][2][3][4][5][6][7][8][9]
この中の[1]と[4]が消去すべき列だとしてそれを消去して詰めた状態
[0][2][3][5][6][7][8][9]
になるように「remain_line_point[20]」に保存しなおしているような作業をしております。
残す列「clear_line_point[i] != 0」の列が見つかれば「remain_line_point」に保存、「remain_line_index++」と添え字数を一つ進めます。
残す列が「remain_line_point」に全て保存されたトコロで入れ替えていきます。
remain_line_index = 0; for(i=FIELD_HEIGHT - 4;i >= 0;i--){ for(j=3;j<FIELD_WIDTH - 3;j++){ stage[i][j] = stage[remain_line_point[remain_line_index]][j]; } remain_line_index++; }
「remain_line_point」には残すべき列の番号が頭から保存されているので少しややこしいですが「stage[23][18]」の内側の下の方から入れ替えていけば入れ替え処理は完了です。
これでクリア状態は終了したので必要なフラグ等を元に戻して、
clear_flag = 0; clear_count = 0; my_init_var2();
クリア状態を解除します。
クリア!!!
ここまでの中間ソースになります。
次回は回転処理を加えたいと思います。
広告
↓発売日:2016年02月29日↓
12歳からはじめる ゼロからのC言語 ゲームプログラミング教室 新品価格 |
↓発売日:2018年06月22日↓
新品価格 |
↓発売日:2018年03月09日↓
新品価格 |
↓発売日:2017年06月14日↓
新品価格 |
↓発売日:2018年05月21日↓
新品価格 |
↓発売日:2017年12月07日↓
新品価格 |
↓発売日:2017年02月08日↓
新・明解C言語で学ぶアルゴリズムとデータ構造 (明解シリーズ) 新品価格 |
↓発売日:2017年09月26日↓
新品価格 |