広告
広告
↓発売日:2017年04月04日↓
Java 第2版 入門編 ゼロからはじめるプログラミング (プログラミング学習シリーズ) 新品価格 |
今回はブロックの種類を増やすのと横一列に揃ったブロックを消去していきます。
ブロックの種類を増やします。
private int blocks[][] = { {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,2,2,0,0,0,2,0,0,2,0,0,0,2,2,0}, {0,0,2,0,0,2,2,0,0,2,2,0,0,2,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,3,3,0,0,3,3,0,0,0,3,0,0,3,0,0}, {0,3,0,0,0,0,3,0,0,3,3,0,0,3,3,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,4,0,4,0,0,4,4,0,0,0,0,0,4,4,0}, {0,4,4,4,0,0,4,0,0,4,4,4,0,0,4,0}, {0,0,0,0,0,0,4,4,0,4,0,4,0,4,4,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,5,0,0,0,0,0,0,0,5,0,0,0,5,0,0}, {0,5,5,0,5,5,5,0,5,5,0,0,5,5,5,0}, {0,5,0,0,0,5,0,0,0,5,0,0,0,0,0,0}, {0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0}, {0,6,0,0,6,6,6,6,0,6,0,0,6,6,6,6}, {0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0}, {0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0} };
そのまま2次元配列を拡張するカタチでブロックの回転状態とともにブロックの種類を6種類に増やしてみました。
という事で今回からこのブロックの元の2次元配列からランダムにブロックを取り出してみたいと思います。
void my_make_block(){ if(make_block_flag == 1){ block_id = rnd.nextInt(6); for(int y=0;y<BLOCK_HEIGHT;y++){ for(int x=0;x<BLOCK_WIDTH;x++){ block[y][x] = blocks[(block_id * BLOCK_HEIGHT) + y][x]; } } make_block_flag = 0; } }
ブロック生成の「my_make_block()」を変更しました。
現在のブロックの種類を表す変数
private int block_id;
を用意しておきます。
ランダムにブロックを発生させるので乱数を使います。
import java.util.Random;
こちらをインポートした上で
private Random rnd;
乱数のオブジェクトを用意
block_id = rnd.nextInt(発生させたい乱数の範囲);
のように使います。
「rnd.nextInt(10)」なら「0~9」までの乱数、「rnd.nextInt(50)」なら「0~49」までの乱数が発生します。
block_id = rnd.nextInt(6);
今回6種類のブロックになるので発生させる値は「0~5」までの6つになりますね。
そして発生させた乱数値を「block_id」に保存します。
あとは「block_id」の値を使ってさきほどの2次元配列「blocks」からブロックを読み出すだけです。
for(int y=0;y<BLOCK_HEIGHT;y++){ for(int x=0;x<BLOCK_WIDTH;x++){ block[y][x] = blocks[(block_id * BLOCK_HEIGHT) + y][x]; } }
回転状態を表す「x」の位置はそのままでブロックの種類を表す「y」の位置だけ「ブロックの種類番号 × ブロックの高さ」という計算でその特定の場所からブロック情報を読み出す事ができます。
これでランダムにブロックを発生させる事ができました。
続いて今回のメインテーマ、ブロック消去処理をやっていきます。
まずは消去すべき横一列にブロックが揃った列を探します。
void my_search_line(){ for(int i=0;i<STAGE_HEIGHT - 3;i++){ clear_line_point[i] = 0; } for(int i=0;i<STAGE_HEIGHT - 3;i++){ for(int j=3;j<STAGE_WIDTH - 3;j++){ if(stage[i][j] == 0){ clear_line_point[i] = 1; break; } } } for(int i=0;i<STAGE_HEIGHT - 3;i++){ if(clear_line_point[i] == 0){ clear_flag = 1; break; } } }
横一列ブロックが揃っている列を探す「my_search_line()」です。
揃った列の場所を入れる為の変数
private int clear_line_point[] = new int[STAGE_HEIGHT - 3];
とクリア状態かどうかのフラグ
private int clear_flag;
を用意します。
「clear_line_point」は「stage」の内側だけを調べるので要素数は「20」となっております。
まずは「clear_line_point」を初期化
for(int i=0;i<STAGE_HEIGHT - 3;i++){ clear_line_point[i] = 0; }
そして「stage」の内側だけを調べるようなカタチで横一列ブロックが揃っている列を探します。
for(int i=0;i<STAGE_HEIGHT - 3;i++){ for(int j=3;j<STAGE_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(int i=0;i<STAGE_HEIGHT - 3;i++){ if(clear_line_point[i] == 0){ clear_flag = 1; break; } }
これでクリアすべき列の探索はおしまいです。
ブロック固定の「my_fix_block()」にこちらの関数を加えます。
void my_fix_block(){ 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()」によりブロック座標等を元に戻しておりましたが今回からクリア処理が入るので少し違った分岐をします。
広告
クリアすべき列がわかったのですぐにクリア処理に入りたいトコロですが列が消えている事がわかるように少しの間がほしいと思います。
そしてそのクリアしている間というのはキー入力処理やブロック再生成処理など、もろもろの処理を禁止にしなければいけません。
なのでゲームループにて通常時とクリア状態時の処理を分岐します。
@Override public void handle(long time){ gc.clearRect(0,0,640,480); key.calc_key_count(); switch(game_state){ case 0: game_state = 5; break; case 5: if(clear_flag == 0){ my_make_block(); my_gameover(); my_move_block(); my_draw_back(); my_draw_variable(); my_draw_block(); my_draw_stage(); my_fix_block(); my_fall_block(); } else{ my_clear_line(); my_draw_back(); my_draw_variable(); my_draw_stage(); } if(gameover_flag == 1){ game_state = 10; } break; case 10: my_draw_back(); my_draw_block(); my_draw_stage(); my_ed(); break; default: break; } }
さきほどの「clear_flag」を使って分岐させております。
それではクリア状態時の処理を見ていきます。
揃った列をクリアして入れ替え処理を行う「my_clear_line()」になります。
ちょっと長いので前半、後半に分けて説明します。
まずは揃った列の部分をクリアして表示したいので、その列を空白を表す「0」に置き換えます。
void my_clear_line(){ int remain_line_point[] = new int[20]; int remain_line_index = 0; if(clear_count < 12){ for(int i=0;i<STAGE_HEIGHT - 3;i++){ if(clear_line_point[i] == 0){ stage[i][clear_count + 3] = 0; } } clear_count++; } else{ /*クリアした場所の入れ替え処理*/ } }
さきほどクリアすべき列は「clear_line_point」に保存したのでこれを調べます。
for(int i=0;i<STAGE_HEIGHT - 3;i++){ if(clear_line_point[i] == 0){ stage[i][clear_count + 3] = 0; } }
「clear_line_point[i] == 0」の場所がクリアすべき列になりますね。
そこを空白を表す「0」に置き換えます。
今回は列の端から一つずつ消えるようにしてみました。
そしてクリアしている時間をカウントする為の「clear_count」を用意してお好みの間クリア状態にします。
この時のクリアしている間のゲームループの描画は「my_draw_stage()」のみになるので注意です。
あとは後始末としてクリアした場所の入れ替え処理を行っていきます。
さきほどの「else」部分のみ抜粋です。
for(int i=STAGE_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(int i=STAGE_HEIGHT - 4;i >= 0;i--){ for(int j=3;j<STAGE_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」に保存します。
for(int i=STAGE_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」に保存しなおしているような作業をしております。
残す列「clear_line_point[i] != 0」の列が見つかれば「remain_line_point」に保存、「remain_line_index++」と添え字数を一つ進めます。
残す列が「remain_line_point」に全て保存されたトコロで入れ替えていきます。
remain_line_index = 0; for(int i=STAGE_HEIGHT - 4;i >= 0;i--){ for(int j=3;j<STAGE_WIDTH - 3;j++){ stage[i][j] = stage[remain_line_point[remain_line_index]][j]; } remain_line_index++; }
「remain_line_point」には残すべき列の番号が頭から保存されているので少しややこしいですが「stage」の内側の下の方から入れ替えていけば入れ替え処理は完了です。
これでクリア状態は終了したので必要なフラグ等を元に戻して、
clear_flag = 0; clear_count = 0; my_init_var2();
クリア状態を解除します。
クリア!!!
ここまでの中間ソースになります。
次回は回転処理を加えたいと思います。
広告
↓発売日:2017年04月18日↓
Java本格入門 ~モダンスタイルによる基礎からオブジェクト指向・実用ライブラリまで 新品価格 |
↓発売日:2016年06月25日↓
新品価格 |
↓発売日:2016年08月31日↓
新品価格 |
↓発売日:2018年11月21日↓
新品価格 |
↓発売日:2016年12月15日↓
新品価格 |
↓発売日:2016年09月28日↓
新品価格 |