主な仕組み
ミノの定義(落下中のミノ)と、落下し終わったブロックが入ってる背景の2つのレイヤーから成り立ちます。落下中のミノを少しずつ下げていき、落下し終わったら背景にコピーします。
背景の配列
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()
コード
final int COL = 10; // 列(x)の数の定義
final int ROW = 20; // 行(y)の数の定義
int map[][] = new int [ROW][COL]; // 落下し終わったミノと空きマスの情報が入っている
final int GRID_SIZE = 40;
// ミノの定義define
final int TYPE_I = 1;
final int TYPE_O = 2;
final int TYPE_S = 3;
final int TYPE_Z = 4;
final int TYPE_J = 5;
final int TYPE_L = 6;
final int TYPE_T = 7;
// ミノの定義
final int[/*ミノのタイプ*/][/*向き*/][/*y*/][/*x*/] MINO = {
{{{},{},{},{}},{{},{},{},{}},{{},{},{},{}},{{},{},{},{}}},
{
// 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 = 0; y < ROW; y++) {
if (map[y][x] == TYPE_I) {
fill(#4DDEED); // 水色
} else if (map[y][x] == TYPE_O) {
fill(#EAED4D); // 黄色
} else if (map[y][x] == TYPE_S) {
fill(#5EED4D); // 緑色
} else if (map[y][x] == TYPE_Z) {
fill(#FF0000); // 赤色
} else if (map[y][x] == TYPE_J) {
fill(#0000FF); // 青色
} else if (map[y][x] == TYPE_L) {
fill(#FF8000); // オレンジ色
} else if (map[y][x] == TYPE_T) {
fill(#9400FF); // 紫色
} else { // なにもないとき
fill(#FFFFFF); // 白
}
rect(x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE);
}
}
}
// 落下中のミノの描画
void draw_moving_type() {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (MINO[moving_type][moving_type_r][y][x] == TYPE_I) {
fill(#4DDEED); // 水色
} else if (MINO[moving_type][moving_type_r][y][x] == TYPE_O) {
fill(#EAED4D); // 黄色
} else if (MINO[moving_type][moving_type_r][y][x] == TYPE_S) {
fill(#5EED4D); // 緑色
} else if (MINO[moving_type][moving_type_r][y][x] == TYPE_Z) {
fill(#FF0000); // 赤色
} else if (MINO[moving_type][moving_type_r][y][x] == TYPE_J) {
fill(#0000FF); // 青色
} else if (MINO[moving_type][moving_type_r][y][x] == TYPE_L) {
fill(#FF8000); // オレンジ色
} else if (MINO[moving_type][moving_type_r][y][x] == TYPE_T) {
fill(#9400FF); // 紫色
}
if (MINO[moving_type][moving_type_r][y][x] > 0) {
rect((x + moving_type_x) * GRID_SIZE, (y + moving_type_y) * GRID_SIZE, GRID_SIZE, GRID_SIZE);
}
}
}
}
int move_time = 30; // 前回落下させてからのフレーム数を記録
int moving_type_x = 0; // moving_typeの左上のマス
int moving_type_y = 0; // moving_typeの左上のマス
int moving_type; // ミノの種類
int moving_type_r; // ミノの向き
int time = 0; // 前回落下からのフレーム数を記録
// define
final int HIT_WALL = 1;
final int HIT_MINO = 2;
// 現在のxとyとrでミノがめり込むかどうか、1は壁、2は下にミノor床にめり込んだ
int check_moving_mino() {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (MINO[moving_type][moving_type_r][y][x] > 0) {
if (moving_type_x + x >= COL) { // 右端
return HIT_WALL;
} else if (moving_type_x + x < 0) { // 左端
return HIT_WALL;
} else if (moving_type_y + y >= ROW) { // 床
return HIT_MINO;
} else if (moving_type_y + y >= 0) {
if (map[moving_type_y + y][moving_type_x + x] > 0) { // 他のミノと重なったとき
return HIT_MINO;
}
}
}
}
}
return 0;
}
// define
final int NEW_MINO = 1;
final int GAME_OVER = 2;
// ミノを時間が経ったら一つ下げる、ミノ固定時に1を返す
int move_mino() {
time++;
if (time > move_time) { // 一定時間が経ったか?
// ミノを下げるor固定する
time = 0;
moving_type_y += 1;
if (check_moving_mino() == HIT_MINO) { // 下が当たったか当たり判定
moving_type_y -= 1; // 一つ戻す
// ミノを固定する
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (MINO[moving_type][moving_type_r][y][x] > 0 && moving_type_y + y >= 0) {
// mapにミノをコピー
map[moving_type_y + y][moving_type_x + x] = MINO[moving_type][moving_type_r][y][x];
}
}
}
return NEW_MINO;
} else if (check_moving_mino() == 0) {
// ぶつからなかったとき
}
}
return 0;
}
// ミノの回転を行う
void Rotate() {
moving_type_r++;
if (moving_type_r > 3) {
moving_type_r = 0;
}
if (check_moving_mino() > 0) { // 当たり判定
// 当たっていたら戻す
moving_type_r--;
if (moving_type_r < 0) {
moving_type_r = 3;
}
}
}
void keyPressed() {
if (keyCode == RIGHT) {
moving_type_x++;
if (check_moving_mino() > 0) {
// あたっとき、もとに戻す
moving_type_x--;
}
}
if (keyCode == LEFT) {
moving_type_x--;
if (check_moving_mino() > 0) {
// あたったとき、もとに戻す
moving_type_x++;
}
}
if (keyCode == DOWN) {
moving_type_y++;
if (check_moving_mino() > 0) {
moving_type_y--;
}
}
if (keyCode == UP) {
Rotate();
}
if (key == ' ') {
while(check_moving_mino() == 0){
moving_type_y++;
}
moving_type_y--;
}
}
// 新しいミノの生成
int new_mino() {
moving_type_x = 3; // x座標を初期位置へ
moving_type_y = -2; // y座標を初期位置へ
moving_type = int(random(0.5, TYPE_T + 0.5)); // ミノの種類をランダムで決定
moving_type_r = 0; // 向きを初期へ
if (moving_type < 1) moving_type = 1; //ランダムがおかしかったとき用
if (moving_type > TYPE_T) moving_type = TYPE_T; //ランダムがおかしかったとき用
if (check_moving_mino() < 1) {
// 他のミノと重なっていないとき
return 0;
} else {
// 他のミノと重なったとき(ゲームオーバー)
return GAME_OVER;
}
}
// 一行消す
int erase_row() {
int del_count = 0; // 削除した行数
for (int y = 0; y < ROW; y++) { // 行
for (int x = 0; x < COL; x++) { // 列
if (map[y][x] == 0) {
break;
} else if (x == (COL - 1)) { // 1行揃っていたとき
del_count++;
for (int k = 0; k < COL; k++) { // 列
for (int l = y; 0 < l; l--) { // 行
map[l][k] = map[l - 1][k];
}
}
}
}
}
return del_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_mino();
}
boolean is_game_over = false;
final int A_SPEED = -1; // 落下速度の係数
final int B_SPEED = 30; // 落下速度の切片
final int MIN_SPEED = 10; // 速度の最小値
void draw() {
if (is_game_over) {
game_over(); // ゲームオーバー画面の描画
} else {
if (move_mino() == NEW_MINO) { // ミノを下げる
// ミノを固定したとき
if (new_mino() == GAME_OVER) { // 新しいミノの生成
// 新しいミノの生成ができなかったとき
is_game_over = true;
}
}
score = score + erase_row();
move_time = A_SPEED * score + B_SPEED;
if (move_time < MIN_SPEED) {
move_time = MIN_SPEED;
}
draw_grids(); // mapの描画
draw_moving_type(); // 落下中のミノの描画
}
}