Javaでゲームを作ってみたいけど、何から始めればいいのか分からない。
そんな方に向けて、この記事では「テトリス」を題材に、Javaプログラミングの基礎から実践までを丁寧に解説します。
Swingを使ったGUIの構築、ブロックの回転・落下処理、行の削除判定、ゲームオーバーの条件、さらにはスコア管理まで、初心者がつまずきやすいポイントを段階的に分かりやすく説明。
1ファイルで完結するシンプルなテトリスコードをベースに、ゲームロジックの全体像を学べる内容となっています。
この記事を読み終えるころには、Javaでオリジナルのテトリスが作れる自信がきっと芽生えるでしょう。
Javaテトリス開発の全体像

まず、Javaでテトリスを作るための要素を整理してみましょう。
こうして並べてみると複雑そうですが、実はひとつずつ分解していけばそれほど難しくありません。
初心者の方でも、まずは「動くブロックが表示される」だけの状態からスタートすればOKです。
JavaでGUIを作るには?

テトリスの画面を作るには、Javaの「Swing」ライブラリを使うのがおすすめです。
JFrame frame = new JFrame("Mini Tetris");
MiniTetris game = new MiniTetris();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(game);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);上記のコードで、テトリスのベースとなるウィンドウを表示することができます。
ブロックの描画は paintComponent メソッドをオーバーライドして Graphics オブジェクトを使って行います。
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (int y = 0; y < ROWS; y++) {
for (int x = 0; x < COLS; x++) {
if (board[y][x] == 1) {
g.setColor(Color.CYAN);
g.fillRect(x * BLOCK, y * BLOCK, BLOCK, BLOCK);
}
}
}
g.setColor(Color.GREEN);
for (int y = 0; y < currentPiece.length; y++) {
for (int x = 0; x < currentPiece[y].length; x++) {
if (currentPiece[y][x] == 1) {
g.fillRect((px + x) * BLOCK, (py + y) * BLOCK, BLOCK, BLOCK);
}
}
}
}このようにして、1マスずつ描画していくことでテトリスのフィールドを構成していきます。
ブロックのデータ構造を考えよう

テトリスのブロックは、基本的に「4つのマスから成る図形」です。これを2次元配列で表現するのが定番です。
private final List<int[][]> pieces = Arrays.asList(
new int[][] {
{0, 0, 0, 0},
{1, 1, 1, 1},
{0, 0, 0, 0},
{0, 0, 0, 0}
},
new int[][] {
{1, 1},
{1, 1}
},
new int[][] {
{0, 1, 0},
{1, 1, 1},
{0, 0, 0}
},
new int[][] {
{0, 1, 1},
{1, 1, 0},
{0, 0, 0}
},
new int[][] {
{1, 1, 0},
{0, 1, 1},
{0, 0, 0}
},
new int[][] {
{1, 0, 0},
{1, 1, 1},
{0, 0, 0}
},
new int[][] {
{0, 0, 1},
{1, 1, 1},
{0, 0, 0}
}
);このような配列で、ブロックの形状を定義しておきます。ブロックの回転も、この配列を回転させることで実現できます。
キーボード操作でブロックを動かす
プレイヤーの入力に応じてブロックを左右・下に移動、もしくは回転させる必要があります。これは KeyListener を使って実装します。
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
if (canMove(-1, 0)) px--;
break;
case KeyEvent.VK_RIGHT:
if (canMove(1, 0)) px++;
break;
case KeyEvent.VK_DOWN:
if (canMove(0, 1)) py++;
break;
case KeyEvent.VK_UP:
rotatePiece();
break;
}
repaint();
}この処理で、ユーザーのキーボード操作がテトリスのプレイに反映されるようになります。
行削除の処理
行が揃ったかどうかのチェックは「盤面の横一列がすべて埋まっているか?」を判定するだけです。
全て埋まっていればその行を削除し、上の行を1段ずつ下にずらす処理を行います。
private void clearLines() {
for (int y = ROWS - 1; y >= 0; y--) {
boolean full = true;
for (int x = 0; x < COLS; x++) {
if (board[y][x] == 0) full = false;
}
if (full) {
// 上の行を下にずらす
for (int row = y; row > 0; row--) {
board[row] = board[row - 1].clone();
}
board[0] = new int[COLS]; // 一番上は空に
y++; // 同じ行をもう一度確認(複数ライン消し対応)
}
ゲームオーバーとスコア管理

ブロックを固定した際に、すでに最上段に他のブロックが存在している場合はゲームオーバーとします。
if (!canMove(0, 0)) {
timer.stop();
JOptionPane.showMessageDialog(this, "Game Over!");
}スコア管理は、削除した行数に応じて点数を加算していく形式が一般的です。行数によって倍率を変えると、ゲーム性が高まります。
完成コード:Javaで作るシンプルテトリス

以下がJavaで作成するミニマムなテトリスの全体コードです。1ファイルで動作するように構成しています。
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class MiniTetris extends JPanel implements ActionListener, KeyListener {
// フィールドサイズ
private final int ROWS = 20, COLS = 10;
// 1ブロックのピクセルサイズ(30x30)
private final int BLOCK = 30;
// タイマー(定期的に actionPerformed を呼び出す)
private Timer timer;
// ゲームボードの状態を表す2次元配列(0: 空, 1: 固定ブロック)
private int[][] board = new int[ROWS][COLS];
// 現在操作中のブロック形状(2次元配列)
private int[][] currentPiece;
// 現在操作中ブロックの座標(左上基準)
private int px = 3, py = 0;
// ランダムブロック生成用
private Random rand = new Random();
// ブロックパターン(I, O, T, S, Z, J, L)
private final List<int[][]> pieces = Arrays.asList(
new int[][] { // I
{0, 0, 0, 0},
{1, 1, 1, 1},
{0, 0, 0, 0},
{0, 0, 0, 0}
},
new int[][] { // O
{1, 1},
{1, 1}
},
new int[][] { // T
{0, 1, 0},
{1, 1, 1},
{0, 0, 0}
},
new int[][] { // S
{0, 1, 1},
{1, 1, 0},
{0, 0, 0}
},
new int[][] { // Z
{1, 1, 0},
{0, 1, 1},
{0, 0, 0}
},
new int[][] { // J
{1, 0, 0},
{1, 1, 1},
{0, 0, 0}
},
new int[][] { // L
{0, 0, 1},
{1, 1, 1},
{0, 0, 0}
}
);
// コンストラクタ(初期化処理)
public MiniTetris() {
setPreferredSize(new Dimension(COLS * BLOCK, ROWS * BLOCK)); // パネルサイズ指定
setBackground(Color.BLACK); // 背景黒
setFocusable(true); // キー入力受け付け
addKeyListener(this); // キーイベント登録
spawnPiece(); // 最初のブロック出現
timer = new Timer(500, this); // 0.5秒ごとに落下処理
timer.start(); // タイマースタート
}
// ランダムにブロックを出現させる
private void spawnPiece() {
currentPiece = deepCopy(pieces.get(rand.nextInt(pieces.size()))); // パターンからランダム取得
px = 3;
py = 0;
// 初期位置で置けない場合はゲームオーバー
if (!canMove(0, 0)) {
timer.stop();
JOptionPane.showMessageDialog(this, "Game Over!");
}
}
// 2次元配列のディープコピー
private int[][] deepCopy(int[][] shape) {
int[][] copy = new int[shape.length][];
for (int i = 0; i < shape.length; i++) {
copy[i] = Arrays.copyOf(shape[i], shape[i].length);
}
return copy;
}
// 指定方向に移動できるかどうかチェック
private boolean canMove(int dx, int dy) {
for (int y = 0; y < currentPiece.length; y++) {
for (int x = 0; x < currentPiece[y].length; x++) {
if (currentPiece[y][x] == 1) {
int nx = px + x + dx;
int ny = py + y + dy;
// 画面外または既存ブロックと衝突していないか
if (nx < 0 || nx >= COLS || ny >= ROWS || (ny >= 0 && board[ny][nx] == 1))
return false;
}
}
}
return true;
}
// 回転後の形状で配置可能かを判定
private boolean canRotate(int[][] rotated) {
for (int y = 0; y < rotated.length; y++) {
for (int x = 0; x < rotated[y].length; x++) {
if (rotated[y][x] == 1) {
int nx = px + x;
int ny = py + y;
if (nx < 0 || nx >= COLS || ny >= ROWS || (ny >= 0 && board[ny][nx] == 1))
return false;
}
}
}
return true;
}
// ブロックの回転処理(90度右回転)
private void rotatePiece() {
int size = currentPiece.length;
int[][] rotated = new int[size][size];
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
rotated[x][size - 1 - y] = currentPiece[y][x];
}
}
if (canRotate(rotated)) {
currentPiece = rotated;
}
}
// ブロックを盤面に固定(マージ)
private void mergePiece() {
for (int y = 0; y < currentPiece.length; y++) {
for (int x = 0; x < currentPiece[y].length; x++) {
if (currentPiece[y][x] == 1) {
board[py + y][px + x] = 1;
}
}
}
clearLines(); // ライン消去
spawnPiece(); // 次のブロックを出現
}
// ラインが揃っていたら消去
private void clearLines() {
for (int y = ROWS - 1; y >= 0; y--) {
boolean full = true;
for (int x = 0; x < COLS; x++) {
if (board[y][x] == 0) full = false;
}
if (full) {
// 上の行を下にずらす
for (int row = y; row > 0; row--) {
board[row] = board[row - 1].clone();
}
board[0] = new int[COLS]; // 一番上は空に
y++; // 同じ行をもう一度確認(複数ライン消し対応)
}
}
}
// 描画処理(盤面と現在のブロック)
public void paintComponent(Graphics g) {
super.paintComponent(g);
// 固定されたブロックを描画
for (int y = 0; y < ROWS; y++) {
for (int x = 0; x < COLS; x++) {
if (board[y][x] == 1) {
g.setColor(Color.CYAN);
g.fillRect(x * BLOCK, y * BLOCK, BLOCK, BLOCK);
}
}
}
// 現在の操作中ブロックを描画
g.setColor(Color.GREEN);
for (int y = 0; y < currentPiece.length; y++) {
for (int x = 0; x < currentPiece[y].length; x++) {
if (currentPiece[y][x] == 1) {
g.fillRect((px + x) * BLOCK, (py + y) * BLOCK, BLOCK, BLOCK);
}
}
}
}
// タイマーから呼ばれる落下処理
public void actionPerformed(ActionEvent e) {
if (canMove(0, 1)) {
py++; // 1マス下に移動
} else {
mergePiece(); // 着地 → 固定化
}
repaint(); // 画面更新
}
// キー入力処理
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
if (canMove(-1, 0)) px--;
break;
case KeyEvent.VK_RIGHT:
if (canMove(1, 0)) px++;
break;
case KeyEvent.VK_DOWN:
if (canMove(0, 1)) py++;
break;
case KeyEvent.VK_UP:
rotatePiece(); // 回転
break;
}
repaint();
}
// 他のキーイベントは使用しない
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
// メインメソッド:ゲーム起動処理
public static void main(String[] args) {
JFrame frame = new JFrame("Mini Tetris");
MiniTetris game = new MiniTetris();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(game);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}このコードで、単純な落下型テトリスが動作します。ブロック回転やスコア表示などの機能は未実装ですが、テトリスの基本構造を学ぶには最適です。
Javaでテトリスを作る楽しさと学び
実際に手を動かしてテトリスを作っていくと、以下のようなスキルが身につきます。
- JavaのGUIプログラミング
- オブジェクト指向設計(クラスの分割や再利用)
- イベントドリブン設計(ユーザー入力・タイマー処理)
- 状態管理やロジックの分岐処理
そして何より「自分で作ったゲームが動く」という喜びは、プログラミング学習のモチベーションを大きく引き上げてくれます。
まとめ:まずは1ブロック描画から始めよう
Javaでテトリスを作成するには、GUI構築・ブロックの管理・操作処理・描画ロジック・ゲーム状態の判定など、複数の要素を組み合わせる必要があります。
ですが、本記事では1ファイル完結のシンプルな構成により、初心者でも着実に学習を進められるように工夫しています。Javaの基礎力を伸ばしたい方や、自作ゲームを通じて成長したい方に最適な内容です。
【要点まとめ】
- JavaのSwingでウィンドウと描画処理を構築する
- テトリスのブロック形状は2次元配列で表現する
- paintComponentで盤面とブロックを描画する
- KeyListenerでプレイヤー操作を実装する
- ブロックの回転は配列の回転処理で行う
- canMoveメソッドで移動可能かどうかを判定する
- 一列が埋まれば削除してスコア加算する
- ゲームオーバーは初期位置で配置できないときに発生する
- Timerを使って自動的に落下を進行させる
- 最終的に1ファイルで構成された実用的なコードが完成する
Javaでゲーム開発を体験することで、GUI構築やイベント処理、状態管理の理解が深まります。
まずはブロックを動かすところから始めて、少しずつ機能を加えていけば、誰でも自分だけのテトリスを作れるようになります。