Processingでシンプルなテトリスを作る

投稿者: | 6月 12, 2026

主な仕組み

ミノの定義(落下中のミノ)と、落下し終わったブロックが入ってる背景の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(); // 落下中のミノの描画
  }
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)