はじめに (対象読者・この記事でわかること)

この記事は、Javaで2Dゲーム開発を行っている初心者〜中級者を対象としています。ゲームオーバー時に画面を初期状態にリセットする処理の実装方法が分からず、デバッグに時間がかかっている方に役立ちます。本文を読むと、ゲームオーバー判定から画面クリア、オブジェクトの再生成、スコアリセットまでを一連の流れで実装できるようになります。また、SwingやJavaFXといった主要な描画フレームワークでの具体例も紹介するので、実務ですぐに適用可能です。さらに、画面リセット時に発生しやすいメモリリークやスレッドの停止漏れといった典型的な落とし穴とその対策も解説します。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。
- Java(8 以上)の基本的な文法とオブジェクト指向の概念
- Swing または JavaFX での簡単な描画とイベント処理
- ゲームループ(TimerAnimationTimerScheduledExecutorService など)の基本的な構造

ゲームオーバー時の画面リセット概要

ゲームオーバー画面を表示したあと、プレイヤーが再挑戦した際に 「最初から始めた」 という感覚を提供するためには、画面だけでなくゲーム内の状態全体を初期化する必要があります。単に画面を黒く塗りつぶすだけでは、スコアやレベル、敵キャラの残存情報が残ったままになるため、正しいリセット処理が重要です。

リセットの大まかな流れは次の通りです。

  1. ゲームオーバー判定の検出
    プレイヤーのライフが 0 になる、または特定の条件を満たしたときにフラグを立てます。

  2. リセットメソッドの呼び出し
    画面描画コンポーネント、ゲームロジック、サウンド、スコアなどをまとめて初期化するメソッドを用意します。

  3. リソースのクリーンアップ
    使い終わった画像やサウンドのストリームを解放し、不要なスレッドを停止させます。

  4. 再描画とゲームループの再開
    初期化した状態で repaint()(Swing)または render()(JavaFX)を呼び出し、ゲームループを再開します。

この流れを正しく実装すれば、ユーザーは「続けてプレイ」した感覚を得られ、バグやフリーズの原因になる状態遺残を防げます。

ゲームオーバー画面の実装手順

以下では、SwingJavaFX の両方で実装できる共通パターンと、フレームワーク別のポイントを示します。コードは簡潔にするために省略可能な部分はコメントで示しています。

ステップ1 ゲームループとオーバーフラグの設定

Java
// Swing 版例 class GamePanel extends JPanel implements ActionListener { private Timer timer = new Timer(16, this); // 約60FPS private boolean gameOver = false; private int score = 0; private Player player; private List<Enemy> enemies = new ArrayList<>(); public GamePanel() { initGame(); // 初期化メソッド呼び出し timer.start(); } private void initGame() { player = new Player(); enemies.clear(); spawnEnemies(); // 初期敵配置 score = 0; gameOver = false; } @Override public void actionPerformed(ActionEvent e) { if (!gameOver) { updateGame(); // ロジック更新 repaint(); // 描画呼び出し } } private void updateGame() { player.update(); enemies.forEach(Enemy::update); checkCollision(); if (player.isDead()) { gameOver = true; onGameOver(); // ゲームオーバー処理へ遷移 } } }
Java
// JavaFX 版例 class GameScene extends Application { private AnimationTimer timer; private boolean gameOver = false; private int score = 0; private Player player; private List<Enemy> enemies = new ArrayList<>(); @Override public void start(Stage primaryStage) { initGame(); Canvas canvas = new Canvas(800, 600); GraphicsContext gc = canvas.getGraphicsContext2D(); timer = new AnimationTimer() { @Override public void handle(long now) { if (!gameOver) { updateGame(); render(gc); } } }; timer.start(); primaryStage.setScene(new Scene(new StackPane(canvas))); primaryStage.show(); } private void initGame() { /* 同上 */ } private void updateGame() { /* 同上 */ } }

ポイント
- gameOver フラグでループの進行を制御し、フラグが立った瞬間にリセット処理へ遷移させます。
- 初期化は initGame() に集約し、再利用しやすくします。

ステップ2 ゲームオーバー時のリセット処理

Java
private void onGameOver() { // 1. UI に Game Over メッセージを表示 JOptionPane.showMessageDialog(this, "Game Over! スコア: " + score); // 2. 必要ならサウンドを停止 SoundManager.stopAll(); // 3. リセットメソッド呼び出し resetGame(); }
Java
private void resetGame() { // タイマー/アニメーションの一時停止 timer.stop(); // Swing の場合 // timer.start() は reset の最後で呼び直す // 4. 画面とロジックのクリーンアップ removeAll(); // コンポーネント解除(Swing 特有) revalidate(); repaint(); // 5. ゲーム状態の再初期化 initGame(); // 前述の初期化メソッドを再利用 // 6. 再開 timer.start(); }

JavaFX 版

Java
private void onGameOver() { Platform.runLater(() -> { Alert alert = new Alert(AlertType.INFORMATION); alert.setHeaderText(null); alert.setContentText("Game Over! スコア: " + score); alert.showAndWait(); }); SoundManager.stopAll(); resetGame(); } private void resetGame() { timer.stop(); // Canvas の内容クリア GraphicsContext gc = canvas.getGraphicsContext2D(); gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight()); initGame(); timer.start(); }

ポイント
- timer.stop() でゲームループを一時停止させ、リセット中にロジックが走らないようにします。
- initGame() がすべての状態を ゼロ にリセットする唯一の場所になるため、バグ混入が減ります。
- removeAll()(Swing)や clearRect()(JavaFX)で画面上の残像や古い描画バッファを確実に消去します。

ハマった点やエラー解決

発生した問題 原因 解決策
画面がリセットされてもスコアが残る initGame()score の初期化忘れ score = 0; を必ず呼び出す
リセット後に敵が増え続ける spawnEnemies() がリセット時に二重呼び出しされる enemies.clear();initGame() の先頭で実行
サウンドが止まらない 再生スレッドが別スレッドで走り続けている SoundManager.stopAll() で全スレッドを interrupt し、 Clip.close() を呼び出す
Exception in thread "Timer-0" timer.start() 前に timer が null になる 初期化時に timer = new Timer(...); を確実に保持
JavaFX で UI 更新が遅れる onGameOver() 中に UI スレッドをブロック Platform.runLater で UI 処理を非同期化

実際のコード例(エラーハンドリング付き)

Java
private void resetGame() { try { timer.stop(); removeAll(); // Swing のみ revalidate(); repaint(); initGame(); timer.start(); } catch (Exception ex) { ex.printStackTrace(); // 必要に応じて再起動ロジックを入れる } }
Java
private void resetGameFX() { try { timer.stop(); gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight()); initGame(); timer.start(); } catch (Throwable t) { t.printStackTrace(); } }

解決策まとめ

  • 状態管理を一元化initGame() にすべての初期化ロジックを集約し、リセット時はこのメソッドだけを呼び出す。
  • タイマー/スレッドの制御:リセット前に必ずゲームループを停止し、再開後に再スタートする。
  • リソースの明示的解放:画像やサウンドは dispose()close() を呼び、GC に任せない。
  • UI スレッドの安全確保:Swing は EDT、JavaFX は Platform.runLater を使用して UI 操作を行う。

まとめ

本記事では、Java(Swing/JavaFX)でゲームオーバー後に画面とゲーム状態を完全にリセットする手順 を解説しました。
- ゲームオーバー判定からリセットフラグの切り替え
- initGame() に集約した初期化ロジック
- タイマー/スレッドの安全な停止と再開
- 残像やスコア等の残存バグを防ぐクリーンアップ

これらを適用することで、プレイヤーは「最初からやり直した」感覚で再挑戦でき、開発者はデバッグ時間を大幅に短縮できます。次回は、オブジェクトプールを活用した高速リセットマルチスレッド対応の高度なリセット戦略 について解説する予定です。

参考資料