主な仕組み
落下中のブロック(ミノ)と、落下し終わったブロックが入ってる背景の2つのレイヤーから成り立ちます。
ミノの形の定義はBLOCKという配列に格納されており、種類と向きを指定することで落下中のミノを指定します。
ミノが下まで落ちて、ぶつかったらそこで、ミノを背景のmap配列にコピーします。コピーが終わったら、ミノの種類をランダムに決定して、ミノの位置を初期に戻します。
クラスは使用していません。
背景の配列
map[y座標][x座標]
落下中のミノ以外の情報が入っています。落下が終わって固定されたミノがこの配列にコピーされます。一番左上のグリッドがy座標、x座標ともに0となっています。
void draw_grids()で描画が行われます。
ブロック(ミノ)の配列
BLOCK[ミノの種類][向き][y座標][x座標]
基本的にはどの様な形なのかを定義する4x4の座標情報ですが、ミノの種類(moving_block)と向き(moving_block_r)の2つの要素を定義するために4次元の配列となっています。ミノのある位置が1以上(数字と色が対応)、ない部分が0となっています。mapに対するBLOCKの位置は一番左上の座標を基準としています。
void draw_moving_block()で描画が行われ、ミノの部分をmapの描画に上書きするような感じになっています。
当たり判定
int check_moving_block(int y座標, int x座標)
落下中のミノを任意の座標に指定した時にぶつかるかどうかを判定する関数です。
コード
final int COL = 10; // 列(x)の数の定義
final int HIDE_ROW = 3; // 上に隠れた3行
final int ROW = 20; // 行(y)の数の定義
int map[][] = new int [ROW + HIDE_ROW][COL]; // 落下中のブロック以外の情報が入っている
final int GRID_SIZE = 40;
// define
final int BLOCK_I = 1;
final int BLOCK_O = 2;
final int BLOCK_S = 3;
final int BLOCK_Z = 4;
final int BLOCK_J = 5;
final int BLOCK_L = 6;
final int BLOCK_T = 7;
// ブロック(ミノ)の定義
final int[/*ブロック*/][/*向き*/][/*y*/][/*x*/] BLOCK = {
{{{},{},{},{}},{{},{},{},{}},{{},{},{},{}},{{},{},{},{}}},
{
// I
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{1, 1, 1, 1},
{0, 0, 0, 0}
},{
{0, 1, 0, 0},
{0, 1, 0, 0},
{0, 1, 0, 0},
{0, 1, 0, 0}
},{
{0, 0, 0, 0},
{1, 1, 1, 1},
{0, 0, 0, 0},
{0, 0, 0, 0}
},{
{0, 0, 1, 0},
{0, 0, 1, 0},
{0, 0, 1, 0},
{0, 0, 1, 0}
}
}, {
// O
{
{0, 0, 0, 0},
{0, 2, 2, 0},
{0, 2, 2, 0},
{0, 0, 0, 0}
},{
{0, 0, 0, 0},
{0, 2, 2, 0},
{0, 2, 2, 0},
{0, 0, 0, 0}
},{
{0, 0, 0, 0},
{0, 2, 2, 0},
{0, 2, 2, 0},
{0, 0, 0, 0}
},{
{0, 0, 0, 0},
{0, 2, 2, 0},
{0, 2, 2, 0},
{0, 0, 0, 0}
}
}, {
// S
{
{0, 0, 0, 0},
{0, 0, 3, 3},
{0, 3, 3, 0},
{0, 0, 0, 0}
},{
{0, 0, 0, 0},
{0, 0, 3, 0},
{0, 0, 3, 3},
{0, 0, 0, 3}
},{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 3, 3},
{0, 3, 3, 0}
},{
{0, 0, 0, 0},
{0, 3, 0, 0},
{0, 3, 3, 0},
{0, 0, 3, 0}
}
}, {
// Z
{
{0, 0, 0, 0},
{4, 4, 0, 0},
{0, 4, 4, 0},
{0, 0, 0, 0}
},{
{0, 0, 0, 0},
{0, 0, 4, 0},
{0, 4, 4, 0},
{0, 4, 0, 0}
},{
{0, 0, 0, 0},
{0, 0, 0, 0},
{4, 4, 0, 0},
{0, 4, 4, 0}
},{
{0, 0, 0, 0},
{0, 4, 0, 0},
{4, 4, 0, 0},
{4, 0, 0, 0}
}
}, {
// J
{
{0, 0, 0, 0},
{5, 0, 0, 0},
{5, 5, 5, 0},
{0, 0, 0, 0}
},{
{0, 0, 0, 0},
{0, 5, 5, 0},
{0, 5, 0, 0},
{0, 5, 0, 0}
},{
{0, 0, 0, 0},
{0, 0, 0, 0},
{5, 5, 5, 0},
{0, 0, 5, 0}
},{
{0, 0, 0, 0},
{0, 5, 0, 0},
{0, 5, 0, 0},
{5, 5, 0, 0}
}
}, {
// L
{
{0, 0, 0, 0},
{0, 0, 6, 0},
{6, 6, 6, 0},
{0, 0, 0, 0}
},{
{0, 0, 0, 0},
{0, 6, 0, 0},
{0, 6, 0, 0},
{0, 6, 6, 0}
},{
{0, 0, 0, 0},
{0, 0, 0, 0},
{6, 6, 6, 0},
{6, 0, 0, 0}
},{
{0, 0, 0, 0},
{6, 6, 0, 0},
{0, 6, 0, 0},
{0, 6, 0, 0}
}
}, {
// T
{
{0, 0, 0, 0},
{0, 7, 0, 0},
{7, 7, 7, 0},
{0, 0, 0, 0}
},{
{0, 0, 0, 0},
{0, 7, 0, 0},
{0, 7, 7, 0},
{0, 7, 0, 0}
},{
{0, 0, 0, 0},
{0, 0, 0, 0},
{7, 7, 7, 0},
{0, 7, 0, 0}
},{
{0, 0, 0, 0},
{0, 7, 0, 0},
{7, 7, 0, 0},
{0, 7, 0, 0}
}
}
};
// mapの描画
void draw_grids() {
for (int x = 0; x < COL; x++) {
for (int y = HIDE_ROW; y < ROW + HIDE_ROW; y++) {
if (map[y][x] == BLOCK_I) {
fill(#4DDEED); // 水色
} else if (map[y][x] == BLOCK_O) {
fill(#EAED4D); // 黄色
} else if (map[y][x] == BLOCK_S) {
fill(#5EED4D); // 緑色
} else if (map[y][x] == BLOCK_Z) {
fill(#FF0000); // 赤色
} else if (map[y][x] == BLOCK_J) {
fill(#0000FF); // 青色
} else if (map[y][x] == BLOCK_L) {
fill(#FF8000); // オレンジ色
} else if (map[y][x] == BLOCK_T) {
fill(#9400FF); // 紫色
} else { // なにもないとき
fill(#FFFFFF); // 白
}
rect(x * GRID_SIZE, (y - HIDE_ROW) * GRID_SIZE, GRID_SIZE, GRID_SIZE);
}
}
}
// 落下中のミノの描画
void draw_moving_block() {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (BLOCK[moving_block][moving_block_r][y][x] == BLOCK_I) {
fill(#4DDEED); // 水色
} else if (BLOCK[moving_block][moving_block_r][y][x] == BLOCK_O) {
fill(#EAED4D); // 黄色
} else if (BLOCK[moving_block][moving_block_r][y][x] == BLOCK_S) {
fill(#5EED4D); // 緑色
} else if (BLOCK[moving_block][moving_block_r][y][x] == BLOCK_Z) {
fill(#FF0000); // 赤色
} else if (BLOCK[moving_block][moving_block_r][y][x] == BLOCK_J) {
fill(#0000FF); // 青色
} else if (BLOCK[moving_block][moving_block_r][y][x] == BLOCK_L) {
fill(#FF8000); // オレンジ色
} else if (BLOCK[moving_block][moving_block_r][y][x] == BLOCK_T) {
fill(#9400FF); // 紫色
}
if (BLOCK[moving_block][moving_block_r][y][x] > 0) {
rect((x + moving_block_x) * GRID_SIZE, (y + moving_block_y - HIDE_ROW) * GRID_SIZE, GRID_SIZE, GRID_SIZE);
}
}
}
}
final int MOVE_TIME = 60; // 60フレームで1マス落下
int moving_block_x = 0; // moving_blockの左上のマス
int moving_block_y = 0; // moving_blockの左上のマス
int moving_block; // ミノの種類
int moving_block_r; // ミノの向き
int time = 0; // 前回落下からのフレーム数を記録
// define
final int HIT_WALL = 1;
final int HIT_BLOCK = 2;
// blockをblock_yとblock_xにしたときに現在落下中のブロックが当たるかどうか、1は壁、2は下にブロックor床に当たった
int check_moving_block(int block_y, int block_x) {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (BLOCK[moving_block][moving_block_r][y][x] > 0) {
if (block_x + x >= COL) { // 右端
return HIT_WALL;
} else if (block_x + x < 0) { // 左端
return HIT_WALL;
} else if (block_y + y >= ROW + HIDE_ROW) { // 床
return HIT_BLOCK;
} else if (map[block_y + y][block_x + x] > 0) { // 重なったとき
return HIT_BLOCK;
}
}
}
}
return 0;
}
// define
final int NEW_BLOCK = 1;
final int GAME_OVER = 2;
// ミノを時間が経ったら一つ下げる、ミノ固定時に1を返す
int move_block() {
boolean gameover_sp = false; // ブロックスポーン場所にブロックを置いた場合
boolean gameover_21 = true; // 21段目(見えない上の行)にブロックを置いた場合
time++;
if (time > MOVE_TIME) { // 一定時間が経ったか?
// ミノを下げるor固定する
time = 0;
if (check_moving_block(moving_block_y + 1, moving_block_x) == HIT_BLOCK) { // 下が当たったか当たり判定
// ミノを固定する
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (BLOCK[moving_block][moving_block_r][y][x] > 0) {
// mapにミノをコピー
map[moving_block_y + y][moving_block_x + x] = BLOCK[moving_block][moving_block_r][y][x];
if (moving_block_y + y < 3 && moving_block_x + x >= 3 && moving_block_x + x < 7) {
// ブロックスポーン場所にブロックが存在
gameover_sp = true;
}
if (moving_block_y + y > 2) {
// 21段目にボロックを置いた
gameover_21 = false;
}
}
}
}
if (gameover_sp || gameover_21) {
return GAME_OVER;
} else {
return NEW_BLOCK;
}
} else if (check_moving_block(moving_block_y + 1, moving_block_x) == 0) {
moving_block_y++;
}
}
return 0;
}
// ミノの回転を行う
void Rotate() {
moving_block_r++;
if (moving_block_r > 3) {
moving_block_r = 0;
}
if (check_moving_block(moving_block_y, moving_block_x) > 0) { // 当たり判定
// 当たっていたら戻す
moving_block_r--;
if (moving_block_r < 0) {
moving_block_r = 3;
}
}
}
void keyPressed() {
if (keyCode == RIGHT) {
if (check_moving_block(moving_block_y, moving_block_x + 1) == 0) {
moving_block_x++;
}
}
if (keyCode == LEFT) {
if (check_moving_block(moving_block_y, moving_block_x - 1) == 0) {
moving_block_x--;
}
}
if (keyCode == DOWN) {
if (check_moving_block(moving_block_y + 1, moving_block_x) == 0) {
moving_block_y++;
}
}
if (keyCode == UP) {
Rotate();
}
if (key == ' ') {
while(check_moving_block(moving_block_y, moving_block_x) < 1){
moving_block_y++;
}
moving_block_y--;
}
}
// 新しいミノの生成
void new_block() {
moving_block_x = 3; // x座標を初期位置へ
moving_block_y = 0; // y座標を初期位置へ
moving_block = int(random(0.5, BLOCK_T + 0.5)); // ミノの種類をランダムで決定
moving_block_r = 0; // 向きを初期へ
if (moving_block < 1) moving_block = 1; //ランダムがおかしかったとき用
if (moving_block > BLOCK_T) moving_block = BLOCK_T; //ランダムがおかしかったとき用
}
// 一行ブロックを消す
int erase_block() {
int count = 0;
for (int y = 0; y < ROW + HIDE_ROW; y++) { // 行
for (int x = 0; x < COL; x++) { // 列
if (map[y][x] == 0) {
break;
} else if (x == (COL - 1)) { // 1行全部ブロックがあったとき
count++;
for (int k = 0; k < COL; k++) { // 列
for (int l = y; 0 < l; l--) { // 行
map[l][k] = map[l - 1][k];
}
}
}
}
}
return count;
}
int score = 0;
// ゲームオーバ画面の描画
void game_over() {
textSize(50);
fill(0);
text("Game Over", 100, 200);
text("Score:", 100, 250);
text(score, 200, 400);
}
void setup() {
size(400, 800);
draw_grids();
new_block();
}
boolean is_game_over = false;
void draw() {
if (is_game_over) {
game_over(); // ゲームオーバー画面の描画
} else {
int moving_block_result = move_block();
if (moving_block_result == NEW_BLOCK) {
new_block();
} else if (moving_block_result == GAME_OVER) {
is_game_over = true;
}
score = score + erase_block();
draw_grids(); // mapの描画
draw_moving_block(); // 落下中のミノの描画
}
}