情報学部 菅沼ホーム Java 目次 索引

ゲーム枠の作成

  1. クラスとメソッド

      住所録を作成する際のように,氏名,住所,電話番号などをまとめて一つのデータとして扱いたい場合があります.数学の例で言えば,複素数などが相当します.複素数は,実数部と虚数部という 2 つの実数データの組からなっており,それらのデータは常に一緒に扱われます.それを可能にするのがクラスです.

      クラスの内部には,データだけではなく,そのデータを処理するためのメソッドも定義できます.メソッドは,データを受け取り,何らかの処理を行い,その結果を返す手続きの集まりです.メソッド内では,与えられた情報(引数リスト)に基づき何らかの処理を行い,その結果を戻り値( 1 つの値)として返します.

      クラスの定義方法を一般的に記述すれば以下のようになります.
    [クラス修飾子] クラス識別子(クラス名) {
    	クラス本体(変数やメソッドの定義)
    }			
      クラス修飾子とは,クラスに対する参照方法や参照制限を規定するものです.データ(変数)やメソッドに対しても,同様の指定を行うことができます.詳細については,「 Java と C/C++ 」を参照して下さい.

  2. 全体構造

      ゲームを作成する際(他のプログラムでも同様ですが),クラスをほとんど使用せず,すべての動作を一つ又は少数のクラス内に記述することも可能です.しかしながら,そのような方法で作成すると,プログラムは非常に長くなり,かつ,読みにくいものになってしまいます.プログラムの一部だけを修正する際にも,プログラム全体をある程度理解せざるを得ず,非常に困難な作業になってしまいます.そこで,ここでは,似たような対象を一つのクラスにまとめ,それらを適当に組み合わせることによってゲームを作成していきます.このようにすることによって,各クラスに対するプログラムは短く,かつ,読みやすくなります.また,ゲームによって共通するクラスは,他のゲームを作成する際にも利用可能になります.

      ゲームプログラムのほとんどは,表紙,ゲーム実行画面(複数の場合もある),終了画面など,そこに描かれている内容は異なりますが,一般に複数の画面(パネル)から構成されています.ゲームの進行と共に,それらのパネルが切り替えられていきます.この方式は,ほとんどのゲームで共通だと思います.そこで,ここでは,以下に示すような方法で画面の切り替えを制御します.まず,ゲーム全体を表す Game クラスの contentPain に,パネル切り替えの制御を行う MainPanel クラスを追加します.MainPanel クラスは,ゲームの進行状況によって,StartPanel クラス(表紙),GamePanel クラス(ゲームの実行),GameClearPanel クラス(ゲームのクリア),及び,GameOverPanel クラス(ゲームの終了)を切り替えていきます.これらは,すべて,JPanel クラス(白紙に相当する)の子であり,ゲームによってそれらのパネルに描かれる内容が異なってきます.
      以下,こららのパネルの詳細について説明していきますが,説明を読む前に,各パネルがどのようにして切り替わるかを見て下さい.最初に,StartPanel が表示されます.「遊び方」ボタンをクリックすると,別タブ( Window )に,遊び方を説明したページ(何も記述されていませんが)が,表示されます.また,「 s 」キーを押すとゲームの実行に入ります( GamePanel の表示).ゲームの実行画面は,レベルの異なる 3 つの画面から構成されています.この段階では,ゲーム自身の作成を行うわけではありませんので,ゲームクリアやゲームオーバーの状態を,「ゲームクリア」,または,「ゲームオーバー」ボタンのクリックで実現しています.「ゲームクリア」を選択した場合は,GameClearPanel が表示され,「ゲーム終了」,または,「次のレベル」(レベルが 3 の場合は「最初から再開」)のいずれかを選択できるようになります.「ゲームオーバー」を選択した場合は,GameOverPanel が表示され,「終了」,「現在のレベルで再開」,または,「最初から再開」のいずれかを選択できるようになります.

  3. Game クラス

      まず,main メソッドを含んだ Game.java の内容は,以下のようになっています.
    01	/*****************************/
    02	/* ゲーム枠の作成(全体構造)*/
    03	/*   coded by Y.Suganuma     */
    04	/*****************************/
    05	import java.io.*;
    06	import java.awt.*;
    07	import java.awt.event.*;
    08	import javax.swing.*;
    09	import main.*;
    10	
    11	public class Game {
    12		public static void main (String[] args)
    13		{
    14			Win win = new Win("全体構造");
    15		}
    16	}
    17	
    18	class Win extends JFrame
    19	{
    20		/******************/
    21		/* コンストラクタ */
    22		/******************/
    23		Win(String name)
    24		{
    25						// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    26			super(name);
    27						// Windowの大きさ
    28			setSize(460, 390);   // +left+right+20, +top+bottom+20
    29	//		Insets ins = getInsets();
    30	//		System.out.println(ins.top + " " + ins.left + " " + ins.bottom + " " + ins.right);
    31						// MainPanel の大きさを決定
    32			Dimension size = getSize(); 
    33			size.width  -= 60;   // パネルの横幅
    34			size.height -= 90;   // パネルの高さ
    35						// ContentPain を取得し,設定
    36			Container CP = getContentPane();   // ContentPane を取得
    37			CP.setLayout(null);   // レイアウトマネージャを停止
    38			CP.setBackground(new Color(220, 255, 220));   // 背景色
    39						// MainPanel を追加し,設定
    40			MainPanel pn = new MainPanel(size);   // MainPanel オブジェクトの生成
    41			CP.add(pn);   // MainPanel オブジェクトを ContentPane に追加
    42			pn.setSize(size.width, size.height);
    43			pn.setLocation(10, 10);
    44						// ウィンドウを表示
    45			setVisible(true);
    46						// イベントリスナ
    47			addWindowListener(new WinEnd());
    48		}
    49	
    50		/******************************/
    51		/* 上,左,下,右の余白の設定 */
    52		/******************************/
    53		public Insets getInsets()
    54		{
    55			return new Insets(50, 20, 20, 20);
    56		}
    57	
    58		/************/
    59		/* 終了処理 */
    60		/************/
    61		class WinEnd extends WindowAdapter
    62		{
    63			public void windowClosing(WindowEvent e) {
    64				System.exit(0);
    65			}
    66		}
    67	}
    			
    01 行目~ 04 行目

      プログラム全体に対する注釈です.注釈(コメント)は,人間がプログラムを読む際,理解しやすくするためのものであり,コンパイラ等によって特別な処理はされません.プログラムを実行するのはコンピュータですが,プログラムを書いたり修正したりするのは人間です.従って,プログラムを書く際に最も注意すべきことは,如何に読み易く,かつ,分かり易いプログラムを書くかという点です.できるだけ多くの注釈を入れておいて下さい.そのことにより,他の人がプログラムを理解しやすくなると共に,プログラム上のエラーも少なくなるはずです.注釈は,/* と */ によって囲む(複数行も可),または,// から行末まで,と言った形で記述できます.20 行目~22 行目,25 行目,27 行目等も注釈であり,プログラムの該当する部分における処理内容を説明しています.

    09 行目

      作成するプログラムをすべて同じディレクトリ(フォルダ)に入れても構わないのですが,プログラム数が多くなった場合,あるプログラムがどのような機能を持つものかといったようなことを特定する場合などに,多くのプログラムリストから探す必要があり,プログラム管理上面倒になります.そこで,ここでは,同じ目的を持ったプログラムを同じパッケージpackage )に入れて管理する方法を採用します.Game クラスでは,main パッケージ(ディレクトリ ./main/ )にある MainPanel クラスを必要としますので,そのクラスを読み込んでいます.

    11 行目~ 16 行目

      main メソッド( 12 行目~ 15 行目)を含む Game クラスです.main メソッドは,特別なメソッド(後に説明)であり,一つのプログラムに必ず一つだけ存在しなければなりません.この main メソッドでは,Win クラスのインスタンス(オブジェクト)を生成しているだけです( 14 行目).

    18 行目

      JFrame クラスを親に持つ Win クラスの定義の開始です.その内容は,18 行目~ 67 行目です.

    23 行目

      クラス Win に対するコンストラクタの開始です.その内容は,23 行目~ 48 行目です.コンストラクタは,クラスと同じ名前を持つ特別なメソッドであり,14 行目のように,クラスのインスタンスを生成すると最初に呼ばれ,クラスに対する初期設定等を行います.

    28 行目

      Window の大きさ(幅と高さ)を設定しています.

    32 行目~ 34 行目

      32 行目では,JFrame クラスのメソッド getSize を使用して,Window の大きさを取得し,変数 size に代入しています.getSize は,メソッドの戻り値として Dimension クラスのオブジェクトを返します.Dimension クラスのフィールド(メンバー変数) width には幅,height には高さの値がピクセル値で入っていますので,Window の幅は size.width,高さは size.height によって参照できます.勿論,この例の場合は,28 行目で設定した値が入ります.

      33 行目~ 34 行目では,ゲームを表示する画面を後に述べる contentPane の大きさ( Window の大きさから上下左右の余白を引いた大きさ)より小さくするために,Window の幅及び高さから (左右上下の余白 + 20 ピクセル) を引いています.このプログラムでは,53 行目~ 56 行目において,上,左,下,右の余白を設定しています.このような設定を行わなければ,自動的に余白が設定されます.その値は,29 行目のような方法で得ることができます.
      33,34 行目は,
    	size.width  = size.width - 60;
    	size.height = size.height - 90;				
    のように記述することもでき,例えば 33 行目は,現在の size.width の値から 60 だけ引き,その結果を size.width に代入することを意味しています.つまり,「 -= 」は,代入演算子と加減乗除 +,-,*,/ などの演算を行う算術演算子を結合した演算子です.なお,似たような演算子として,「 -= 」以外に,「 += 」,「 *= 」,「 /= 」など,多くの演算子が存在します.

    36 行目

      contentPane を取得しています.contentPane は コンテナの一種であり,以後,ボタンなど様々な要素を contentPane に配置していくことになります.

    37 行目

      ボタンの大きさやその配置位置を個々に設定していくことは非常に面倒な作業です.Window の大きさが変更されたような場合は,その Window に合うように再配置してやる等の作業も必要になります.そこで,要素の配置をある程度機械的に行ってくれるレイアウトマネージャというものが存在し,コンテナの種類毎にデフォルトのレイアウトマネージャが設定されています.この行では,Container クラスのメソッド setLayout によって,contentPane のデフォルトのレイアウトマネージャを無効にしています.なお,代表的なレイアウトマネージャとしては,以下に示すようなものが存在します.

      1. BorderLayout: コンテナ(画面)を5つの領域(上,下,左,中,右)に分けて,コンポーネントを配置
      2. CardLayout: 同じ領域に複数のコンポーネントを重ねて配置
      3. FlowLayout: コンポーネントを可能な限り横1行に配置
      4. GridLayout: コンポーネントを格子状に配置
      5. GridBagLayout: 格子状のセルにコンポーネントを柔軟に配置(配置方法は,GridBagConstraints クラスのメソッドで指定)
      6. BoxLayout: コンポーネントを縦,または,横には位置

    38 行目

      contentPane の背景色を設定しています.

    40,41 行目

      MainPanel クラスのオブジェクトを生成し,contentPane CP に追加しています.

    42 行目

      MainPanel の大きさを,33 行目~ 34 行目によって計算した大きさに設定しています.

    43 行目

      MainPanel の表示位置を設定しています.MainPanel は,contentPane より,幅及び高さが 20 ピクセルだけ小さいため,contentPane の (10, 10) の位置に MainPanel を配置することによって,MainPanel の周りに,10 ピクセル幅の Color(220, 255, 220) 色の帯が表示されます.なお,座標系は,contentPane の左上を原点とし,右方向を x 座標の正方向,下方向を y 座標の正方向としています.

    45 行目

      追加した部品等を可視化しています.

    47 行目

      × マークによって Window を閉じるため,内部クラス WinEnd ( 61 行目~ 66 行目)を利用した Window リスナを 追加しています.

      マウスがクリックされた,ボタンが押された,Window が開始された,等のことをイベント(事象)と言います.このようなイベントが発生したとき行う処理をイベント処理といいます.イベントは,イベントの種類毎に,クラスに分類されています. イベントが発生すると,イベントソースは対応するイベントを記述するイベントオブジェクトを生成します.これを,イベントリスナに送出して対応する処理を行うわけですが,そのためには,イベントリスナがそのイベントを「聞く」準備ができていなくてはなりません.その準備を行うのがイベント登録メソッド(イベント削除メソッドもあります)です.

      イベントの送出は,リスナのインタフェース(または,イベントアダプタ)に定義されているイベント応答メソッド(ハンドラメソッド)の内1つを呼び出し,引数としてイベントソースが生成したイベントオブジェクトを渡すといった方法で行われます.イベントリスナはインタフェースとして提供されています.これは,各イベントの処理がアプリケーションによって異なる場合が多いからです.しかし,インタフェースを実装する際には,インタフェースに定義されているすべてのメソッド(ハンドラメソッド)を実装しない限り,抽象クラスとなり,インスタンスを生成できなくなります.実際にすべてのメソッドを必要とする場合は問題ありませんが,以下の例に示すように,一部のメソッドだけを使用したい場合においても,必要としないメソッドまですべて定義しなければならず,余分な作業が必要になります.そのため,この例のように,WindowAdapter クラス( Window に関する全てのイベント対応処理をデフォルトとして所有)を継承した 内部クラス(この例では,WinEnd )を利用する方法が良く用いられます.

  4. MainPanel クラス

      パネル切り替えの制御を行うクラスです.このクラスでは,100 ms 毎に,フィールド(メンバー変数) state の値によってゲームの状態を監視し,その値が変化した場合は対応する状態に(表示するパネルを)切り替えています.初期状態では,ゲームの表紙に対応する StartPanel を表示します.そのソースファイル( MainPanel.java )は以下に示すとおりです.

    01	package main;
    02	
    03	import java.io.*;
    04	import java.awt.*;
    05	import java.awt.event.*;
    06	import javax.swing.*;
    07	import start.*;
    08	import game.*;
    09	import clear.*;
    10	import over.*;
    11	
    12	public class MainPanel extends JPanel implements Runnable
    13	{
    14		Dimension size;   // パネルの大きさ
    15		boolean in_game = true;   // ゲーム実行中はtrue
    16		public int state = 0;   // ゲーム状態(0:表紙,1:ゲーム,2:クリア,3:オーバー,4:終了)
    17		public int level = 1;   // ゲームレベル
    18		int old_state = 0;   // 直前のゲーム状態
    19		StartPanel sp;
    20		GamePanel gp;
    21		GameClearPanel gcp;
    22		GameOverPanel gop;
    23		Thread td;
    24				// コンストラクタ
    25		public MainPanel(Dimension size1)
    26		{
    27			size = size1;
    28						// グリッドレイアウト
    29			setLayout(new GridLayout(1, 1, 0, 0));
    30						// ゲームパネルの生成
    31			sp = new StartPanel(size, this);   // スタート(タイトル)
    32			add(sp);
    33						// スレッドの生成
    34			td = new Thread(this);
    35			td.start();
    36		}
    37				// ゲームの状態を変更
    38		public void run()
    39		{
    40			while (in_game) {
    41				try {
    42					td.sleep(100);   // 100 ms 毎の実施
    43				}
    44				catch (InterruptedException e) {}
    45				if (state != old_state) {
    46								// 前のパネルの削除
    47					if (old_state == 0)
    48						remove(sp);
    49					else if (old_state == 1)
    50						remove(gp);
    51					else if (old_state == 2)
    52						remove(gcp);
    53					else
    54						remove(gop);
    55								// 新しいパネルの追加
    56					if (state == 4)   // ゲーム終了
    57						in_game = false;
    58					else {
    59						if (state == 0) {   // StartPanel
    60							sp = new StartPanel(size, this);
    61							this.add(sp);
    62							sp.repaint();
    63						}
    64						else if (state == 1) {   // GamePanel
    65							gp = new GamePanel(size, this);
    66							add(gp);
    67						}
    68						else if (state == 2) {   // GameClearPanel
    69							gcp = new GameClearPanel(size, this);
    70							add(gcp);
    71						}
    72						else {   // GameOverPanel
    73							gop = new GameOverPanel(size, this);
    74							add(gop);
    75						}
    76						validate();
    77						old_state = state;
    78					}
    79				}
    80			}
    81		}
    82	}
    			
    01 行目

      MainPanel クラス及びその関連クラスが入るパッケージの名前です.MainPanel 関連のクラスは,この宣言により,Game クラスが存在するディレクトリから見た相対アドレス「 ./main/ 」に存在する必要があります.

    07 行目~ 10 行目

      MainPanel クラスでは,start パッケージにある StartPanel クラス,game パッケージにある GamePanel クラス,clear パッケージにある GameClearPanel クラス,及び,over パッケージにある GameOverPanel クラスとそれらの関連クラスを使用しますので,これらの宣言が必要になります.

    12 行目

      JPanel クラスの子として MainPanel クラスを宣言しています.このクラスは他のパッケージからも利用されますので public というクラス修飾子が必要になります.なお,implements 以降に関しては後ほど説明します.

    14 行目~ 23 行目

      以前述べましたように,Java においても,数学と同じように,定数や様々な値を保持するための変数を使用可能です.ただし,すべての変数には「」があり,その「型」に一致した値だけをその変数に代入可能です.従って,使用する変数に対しては,その変数を使用する前にその変数の型を宣言しておく必要があります.また,関数内で宣言された変数の有効範囲は,宣言された文以降で,かつ,その変数が宣言されたブロック内(一般に,「 { 」と「 } 」で囲まれた部分)に限定されます.先に述べた Game クラス内においても,size,CP,pn のような変数が定義されていますが,それらの変数が使用されているのは,Win メソッド内のそれらの変数が宣言された文以降です.

      しかしながら,14 行目~ 23 行目において宣言されている変数は,同じクラス内のメソッド,または,他のクラスからも参照されます.そこで,これらのように,クラスのフィールド(クラスを特徴付ける変数)として,メソッドの外側で宣言しておく必要があります.ただし,メソッドの外側で,かつ,クラス宣言の内部であれば,どこで宣言しても構いません.フィールドは,C/C++ におけるメンバー変数に相当します.

      フィールド(メンバー変数) in_game は,ゲームの実行中は true,ゲームを完全に終了すると false になります.今までの説明で現れた変数は,すべてクラスのオブジェクトを代入するための変数でしたが,in_game,state,level,old_state は,整数や実数のような基本データ型を代入するための変数です.state は,ゲームの状態を表すフィールド(メンバー変数)であり,値によって以下のような意味を持っています.

      • state : 0  表紙の表示( StartPanel )
      • state : 1  ゲーム画面の表示( GamePanel ).フィールド level がゲームのレベル
      • state : 2  ゲームクリア画面の表示( GameClearPanel )
      • state : 3  ゲームオーバー画面の表示( GameOverPanel )
      • state : 4  ゲームの終了

      フィールド(メンバー変数) state と level の前には,public という修飾子が記述されていますが,これは,これらのフィールド(メンバー変数)が他のパッケージからも参照できるようにするためです.なお,old_state は,直前の state の値を保存するためのフィールド(メンバー変数)です.

    25 行目

      MainPanel クラスのコンストラクタです(その内容は,25 行目~ 36 行目).Game クラスの 40 行目が実行されると,直ちに実行されます.40 行目に記述した 1 つの引数(括弧内の変数)と,この行に記述した 1 つの引数は,引数の数,順序,型が完全に一致している必要があります(変数名は一致する必要は無い).

    27 行目

      引数として渡された情報(変数)をコンストラクタ内だけで使用するのであれば,このような文は必要ありませんが,他のメソッドやクラスで使用できるように,フィールド(メンバー変数)に代入しています.

    29 行目

      レイアウトマネージャを 1 行 1 列の GridLayout に変更しています.この結果,MainPanel に新しい要素を追加すると,その要素は MainPanel と同じ大きさで,全体に表示されます.

    31,32 行目

      StartPanel クラス(ゲームの表紙)のオブジェクトを生成し,MainPanel に貼り付けています.なお,this は,自分自身を指すキーワードであり,ここでは,MainPanel クラスのオブジェクトを指します.

    34,35 行目

      Thread クラスのオブジェクトを生成し,Thread クラスのメソッド start を使用して,その実行を開始しています.スレッドとは,プログラム内で実行する別のプログラムです.Java では,並列に複数のプログラムを実行することができます.スレッドを生成し実行するには,2 つの方法があります.一つは,あるクラスを Thread クラスの子として定義する方法であり,また,他の一つは,Runnable インタフェースを利用する方法です

      Java では,C++ とは異なり,複数の親を継承することができません.12 行目に示すように,MainPanel クラスは既に JPanel クラスを継承していますので,Thread クラスを継承できません.このような問題を解決するのがインターフェースです.クラスは,複数のインタフェースを継承可能です( implements の後ろに複数のインタフェースをカンマで区切って並べることができます).ただし,継承可能なものは,メソッドの宣言と static な変数(フィールド)だけであり,継承したクラスが抽象クラスでない限り,インタフェース内で宣言されたメソッドの内容を再定義オーバーライド)してやる必要があります.ここでは,12 行目において,Runnable インタフェースを継承し,38 行目~ 81 行目において,そのメソッド run をオーバーライドしています.

    38 行目

      Runnable インタフェース内のメソッド run をオーバーライドしています(その内容は,38 行目~ 81 行目).

    40 行目

      while 文は,繰り返し文の一種であり,同じような手順を何回も繰り返したいとき使用します.while 文は,
    	while (論理式) {
    		文(複数の文も可)
    	}				
    のような形式で記述され,論理式(演算の結果として,真 -true-,または,偽 -false- を返す文)の値が真である限り,文の実行が繰り返されることになります.似たような文として,do while 文が存在し,
    	do {
    		文(複数の文も可)
    	} while (論理式) ;				
    のように記述されます.while 文と do while 文の違いは,論理式の評価が,文を実行する最初に行われるか,または,後で行われるかの違いです.do while 文では,論理式の評価が後で行われるため,do while 文の開始時に論理式が偽であっても,文が少なくとも 1 回は実行されることになります.なお,繰り返される文が 1 文だけの時は,「 { 」と「 } 」は必要ありません.ここでは,in_game が真( true )である限り,41 行目から 79 行目までが繰り返されることになります.

      同じような目的の文として,for 文が存在します.for 文は,
    	for (式1; 式2; 式3) {
    		文(複数の文も可)
    	}				
    のような形式で記述されます.for 文に入ると,まず,式 1 が実行されます.式 1 は,通常,for 文の繰り返し回数を制御するため等の初期設定を行う式であり,for 文の最初に 1 回だけ実行されます.次に,式 2 (論理式)の値が評価され,もし真であれば文が実行されます.そして,式 3 が実行されます.再び,式 2 が評価され,その値が真である限り,文と式 3 の実行が繰り返されます.式 2 の値が偽になると,文と式 3 は実行されず,for 文の次の文が実行されることになります.また,while 文等と同様に,繰り返される文が 1 文だけの時は,「 { 」と「 } 」は必要ありません.

      while 文と for 文はほぼ等価の文であり,for 文を while 文で記述すれば,
    	式1;
    	while (式2) {
    		文(複数の文も可)
    		式3;
    	}				
    のようになります.従って,40 行目は,
    	for (; in_game; ) {				
    のようにも記述できます.

    41 行目~ 44 行目

      try ブロックcatch ブロックで構成されるブロックを例外処理と呼びます.try ブロック内で何らかの例外(この例の場合は,Thread クラスのメソッド sleep の実行によって起こる可能性がある InterruptedException 例外)が起こった場合,その例外に対応する catch ブロック内でその例外に対応した何らかの処理を行います(この例の場合は,44 行目の {} 内に何も記述されていないため,何も行いません).try ブロック内で複数の例外が起こる可能性がある場合は,各例外に対応した複数の catch ブロックを記述することになります.

      sleep メソッドは,Thread クラスのメソッドであり,引数に与えられた時間(単位は ms )だけ実行を停止することを意味しています.このメソッドでは,InterruptedException 例外に対応する処理を必ず記述する必要があるため,この例のような記述になっています.sleep メソッドの実行により,45 行目から 79 行目までの処理が,100 ms 毎に行われることになります.つまり,画面の切り替えには,最大 100 ms かかることになります.

    45 行目

      if 文は,そのときの状況により実行の順序を変化させるための文であり,
    	if (論理式) {
    		文1(複数の文も可)
    	}
    	else {
    		文2(複数の文も可)
    	}
    	・・・・・・				
    のような形式で記述されます.まず,論理式が評価され,その結果が真であれば,文1が実行され,次に,「・・・」以降に書かれた文が実行されます.この場合は,文2は実行されないことになります.また,偽である場合は,文1は実行されず,文2が実行された後,真の場合と同様,「・・・」以降に書かれた文が実行されます.なお,if 文において,真に対応する部分(文1)は必ず必要ですが,偽に対応する部分( else 以下)は,必ずしも必要ありません.また,while 文などと同様,文1や文2が 1 文だけの時は,「 { 」と「 } 」は必要ありません.

      論理式は,計算結果としてまたはtrue または false )を返す文であり,関係演算子,等値演算子,論理演算子などが使用されます.ここでは,state の値が state_old の値に等しくないとき(つまり,状態が変化したとき),47 行目から 78 行目までが実行されます.

      なお,Java で使用できる関係演算子には,以下のようなものがあります.
    	>  より大きい  a > b   式 a の値が式 b の値より大きいとき真
    	<  より小さい  a < b   式 a の値が式 b の値より小さいとき真
    	>= 以上     a >= b  式 a の値が式 b の値以上のとき真
    	<= 以下     a <= b  式 a の値が式 b の値以下のとき真				
    また,等値演算子には,以下のようなものがあります.
    	== 等しい    a == b  式 a の値と式 b の値が等しいとき真
    	!= 等しくない  a != b  式 a の値と式 b の値が等しくないとき真				
      論理演算子は,論理演算(論理和,論理積,否定)を行うための演算子であり,以下のようなものがあります.
    	|| 論理和  x || y  式 x が真か,または,式 y が真のとき真
    	&& 論理積  x && y  式 x が真で,かつ,式 y が真のとき真
    	!  否定    ! x      式 x が偽のとき真				
    47 行目~ 54 行目

      この部分は,if 文の変形である else if 文を使用して書かれており,ゲームの旧状態( old_state の値)で表示されていたパネルを JPanel クラスのメソッド remove を利用して削除しています.もし,通常の if 文を使用すれば,以下のようになります.
    	if (old_state == 0)
    		remove(sp);
    	else {
    		if (old_state == 1)
    			remove(gp);
    		else {
    			if (old_state == 2)
    				remove(gcp);
    			else
    				remove(gop);
    		}
    	}				

    57 行目

      ゲームを完全に終了させます.40 行目の while 文の外に出るため in_game を false に設定し,run メソッドの実行を終了し,スレッドが停止します.

    59 行目~ 75 行目

      ゲームの新状態( state の値)に合わせて,対応するパネルを貼り付けています.62 行目( StartPanel を再描画)を記述しないと,StartPanel が表示されません.

    76 行目

      コンポーネントが有効な配置であることを,JPanel クラスのメソッド validate を利用して確認しています.

    77 行目

      状態が変化したことをチェックするため,変更した現在の状態を,フィールド(メンバー変数) old_state に保存しています.

  5. StartPanel クラス

      ゲームの表紙(ゲームのタイトル)を表示させるためのクラスです.「遊び方」ボタンをクリックすると,遊び方を説明したダイアログが表示され,「 s 」キーを押すとゲームを開始します.そのソースファイル( StartPanel.java )は以下に示すとおりです.

    001	package start;
    002	
    003	import java.io.*;
    004	import java.awt.*;
    005	import java.awt.event.*;
    006	import javax.swing.*;
    007	import main.*;
    008	
    009	public class StartPanel extends JPanel implements ActionListener, KeyListener
    010	//public class StartPanel extends JPanel implements ActionListener
    011	{
    012		boolean in_game = true;
    013		Dimension size;   // パネルの大きさ
    014		MainPanel mp;
    015		JButton bt;
    016				// コンストラクタ
    017		public StartPanel(Dimension size1, MainPanel mp1)
    018		{
    019			size = size1;
    020			mp   = mp1;
    021			setSize(size.width, size.height);
    022						// レイアウトマネージャの停止
    023			setLayout(null);
    024						// 背景色の設定
    025			setBackground(Color.white);
    026						// ボタンの配置
    027			Font f = new Font("SansSerif", Font.BOLD, 20);
    028			FontMetrics fm = getFontMetrics(f);
    029			String str = "遊び方";
    030			int w = fm.stringWidth(str) + 40;
    031			int h = fm.getHeight() + 10;
    032			bt = new JButton(str);
    033			bt.setFont(f);
    034			bt.addActionListener(this);
    035			bt.setSize(w, h);
    036			bt.setLocation(size.width/2-w/2, 5);
    037			add(bt);
    038						// キーリスナの追加
    039			addKeyListener(this);
    040	//		addKeyListener(new Key());
    041		}
    042				// 描画
    043		public void paintComponent(Graphics g)
    044		{
    045			super.paintComponent(g);   // 親クラスの描画
    046			FontMetrics fm;
    047			Font f;
    048			String str1, str = "Game Title";
    049			int w, h;
    050	
    051			f    = new Font("Serif", Font.BOLD, 25);
    052			fm   = g.getFontMetrics(f);
    053			str1 = str + " (Serif)";
    054			w    = fm.stringWidth(str1);
    055			h    = fm.getHeight() + 60;
    056			g.setFont(f);
    057			g.drawString(str1, size.width/2-w/2, h);
    058	
    059			f    = new Font("SansSerif", Font.BOLD, 25);
    060			fm   = g.getFontMetrics(f);
    061			str1 = str + " (SansSerif)";
    062			w    = fm.stringWidth(str1);
    063			h    = h + fm.getHeight() + 5;
    064			g.setFont(f);
    065			g.drawString(str1, size.width/2-w/2, h);
    066	
    067			f    = new Font("Dialog", Font.BOLD, 25);
    068			fm   = g.getFontMetrics(f);
    069			str1 = str + " (Dialog)";
    070			w    = fm.stringWidth(str1);
    071			h    = h + fm.getHeight() + 5;
    072			g.setFont(f);
    073			g.drawString(str1, size.width/2-w/2, h);
    074	
    075			f    = new Font("DialogInput", Font.BOLD, 25);
    076			fm   = g.getFontMetrics(f);
    077			str1 = str + " (DialogInput)";
    078			w    = fm.stringWidth(str1);
    079			h    = h + fm.getHeight() + 5;
    080			g.setFont(f);
    081			g.drawString(str1, size.width/2-w/2, h);
    082	
    083			f    = new Font("Serif", Font.PLAIN, 20);
    084			fm   = g.getFontMetrics(f);
    085			str1 = "ゲーム開始:「 s 」キー";
    086			w    = fm.stringWidth(str1);
    087			h    = size.height - fm.getHeight() - 10;
    088			g.setFont(f);
    089			g.drawString(str1, size.width/2-w/2, h);
    090						// この Component が入力フォーカスを取得することを要求
    091			requestFocusInWindow();
    092		}
    093				// ボタンがクリックされたときの処理
    094		public void actionPerformed(ActionEvent e)
    095		{
    096			if (e.getSource() == bt) {
    097				Method db = new Method();
    098				db.setVisible(true);
    099				requestFocusInWindow();
    100			}
    101		}
    102				// キーが押されたときの処理(イベントリスナ)
    103		public void keyPressed(KeyEvent ke)
    104		{
    105			if (ke.getKeyCode() == 83)   // 「s」キー
    106				mp.state = 1;
    107		}
    108		public void keyReleased(KeyEvent ke) {}
    109		public void keyTyped(KeyEvent ke) {}
    110				// キーが押されたときの処理(イベントアダプタ)
    111	/*
    112		class Key extends KeyAdapter {
    113			public void keyPressed(KeyEvent ke)
    114			{
    115				if (ke.getKeyCode() == 83)   // 「s」キー
    116					mp.state = 1;
    117			}
    118		}
    119	*/
    120	}
    121	
    122	/******************/
    123	/* ゲームの遊び方 */
    124	/******************/
    125	class Method extends JDialog
    126	{
    127				// コンストラクタ
    128		Method()
    129		{
    130			setTitle("ゲームの遊び方");
    131					// ContetPain
    132			Container cp = getContentPane();
    133			cp.setLayout(new FlowLayout(FlowLayout.CENTER));
    134			cp.setBackground(new Color(220, 255, 220));   // 背景色
    135			Font f = new Font("MS 明朝", Font.PLAIN, 20);
    136			setSize(550, 160);
    137					// TextArea の追加
    138			JTextArea ta = new JTextArea(5, 50);
    139			ta.setFont(f);
    140			ta.setEditable(false);
    141			ta.setLineWrap(true);
    142			ta.setText("遊び方\n");
    143			ta.append(" 遊び方(その1)\n");
    144			ta.append(" 遊び方(その2)\n");
    145			ta.append(" 遊び方(その3)\n");
    146			ta.append(" 遊び方(その4)\n");
    147			ta.append(" 遊び方(その5)\n");
    148			JScrollPane scroll = new JScrollPane(ta);
    149			cp.add(scroll);
    150					// Window を閉じるため
    151			addWindowListener(new WinEnd());
    152		}
    153					// 終了処理
    154		class WinEnd extends WindowAdapter
    155		{
    156			public void windowClosing(WindowEvent e) {
    157				setVisible(false);
    158			}
    159		}
    160	}
    			
    009 行目

      ここでは,ボタンをクリックしたときの処理(アクションイベント),及び,キーを操作したときの処理(キーイベント)を行うため,009 行目に示すように,ActionListener インターフェースKeyListener インタフェースを継承しています.そして,036 行目においてボタンに ActionListener を登録し,また,039 行目においてこのパネルに KeyListener を登録しています.ボタンがクリックされると,094 行目から 101 行目に記述されたイベント応答メソッドが実行されます.また,キー入力が行われると,103 行目から 107 行目に記述されたイベント応答メソッドが実行されます.実際に必要なメソッドは keyPressed だけですが,KeyListener がインターフェースであるため,keyReleased( 108 行目),及び,keyTyped( 109 行目)に対する定義も行っておかなければなりません.

      イベントアダプタクラスを利用すれば,この煩わしさを避けることが可能です.イベントアダプタは,すべてのメソッドに対する標準的処理を実装しているため,このクラスのサブクラスとしてクラスを定義すれば,必要なメソッドの実装だけで済むことになります.しかし,Java においては多重継承が許されませので,内部クラスを定義してイベント処理を行うのが通常の方法です.ここでは,キーイベントに対してイベントアダプタを使用しています.そのため,009 行目を 010 行目,また,039 行目を 040 行目に置き換える必要があります.さらに,103 行目から 107 行目の記述を,KeyAdapter クラスを継承したクラス Key に対する記述に置き換える必要があります( 112 行目~ 118 行目).

    027 行目

      フォント名として論理フォント名を使用し,Font クラスのオブジェクトを生成しています.論理フォント名の場合,インストールされているフォントの内,最も近いものが選択されます.

    028 行目

      JPanel クラスのメソッド getFontMetrics を使用して,Font f の FontMetrics クラスのオブジェクトを取得しています.FontMetrics クラスは,特定の画面における特定のフォントに関する描画情報を得るためのクラスです.ここでは,文字列「遊び方」の幅( stringWidth )と標準の高さ( getHeight )を得るために使用しています( 030 行目と 031 行目).

    029 行目

      「遊び方」という値(文字列)を持った String クラスのオブジェクトを生成しています.このように,Java においては,文字列を String クラスのオブジェクトとして扱います.

    032 行目

      JButton クラスのオブジェクト(ボタン)を生成しています.

    033 行目

      ボタンのフォントを 031 行目で生成したフォント f に設定しています.

    034 行目

      ボタンをクリックしたときの処理を行うため,ボタンに ActionListener を登録しています( addActionListener ).

    035,036 行目

      030,031 行目で得た文字列の幅と高さを元にボタンのサイズを決定し,横方向の中央に配置しています.

    037 行目

      ボタンをパネルに追加しています.

    039 行目

      キーが押されたときの処理を行うため,このパネルに KeyListener を登録しています( addKeyListener ).

    043 行目~ 092 行目

      JPanel クラスのメソッドである paintComponent をオーバーライドしています.paintComponent は,このクラス( StartPanel クラス)のオブジェクトが生成されると自動的に呼ばれ,その中に記述された内容が描画されます.

    045 行目

      引数として渡された Graphics クラスのオブジェクトによって,親オブジェクトの描画を行っています(常に,この行は必要).super というキーワードは親オブジェクトを表しています.

    051 行目~ 081 行目

      4 つの論理フォント名( SerifSansSerifDialogDialogInput )を使用して(フォントの設定:setFont ),ゲームのタイトルを表示しています(文字列の描画:drawString ).4 つの論理フォントを使用した理由は,単に,フォントの比較のためだけです.

    091 行目

      JPanel クラスのメソッド requestFocusInWindow によって,このコンポーネント(パネル)が入力フォーカスを取得することを要求しています.キーイベントを処理するために必要です.

    094 行目~ 100 行目

      ボタンがクリックされたときに対するイベント応答メソッド actionPerformed をオーバーライドしています.096 行目において,ActionEvent の源が JButton クラスのオブジェクト bt であることを確認していますが,このプログラムではボタン bt だけに ActionListener が付加されていますので,実際上,この if 文は必要ありません.

      097 行目において,遊び方を表示するため,Method クラス( 125 行目~ 160 行目)のオブジェクトを生成し,098 行目においてそれを可視化しています.また,099 行目は,キーイベントを処理するため,フォーカスを現在のパネルに戻しています.

    103 行目~ 107 行目

      キーが押されたときに対するイベント応答メソッド keyPressed をオーバーライドしています.「 s 」キー(キーコード:83 )が押されると,MainPanel のフィールド(メンバー変数) state の値が 1 に設定されます( 115,116 行目).その結果,ゲームが開始されます.

    125 行目

      説明を記述するための Method クラス( 125 行目~ 160 行目)に対する定義の始まりです.JDialog クラスを継承しており,ダイアログとして表示されます.

    138 行目~ 141 行目

      JTextArea クラスのオブジェクトを生成しています.JTextArea クラスのフォントの設定する( 139 行目),編集できないようにする( 140 行目),文字列を右端で折り返す( 141 行目)処理を行っています.

    142 行目~ 147 行目

      JTextArea に文字列を表示しています.

    148 行目

      JScrollPane クラスを使用して,表示行数が多くなったとき,スクロールバーを表示するようにしています.

  6. GamePanel クラス

      ゲームを実際に実行するパネルです.この例では,ゲームは 3 つのレベルから構成されているとしていますが,実際のゲームを実行できるわけではありません.そのため,ボタンをクリックすることによって,ゲームクリアやゲームオーバーの状態に移動するように作成されています.その内容は,単に,3 つの円がランダムな位置に表示されるだけであり,レベルが上がるに従って表示間隔が短くなります.そのソースファイル( GamePanel.java )は以下に示すとおりです.

    001	package game;
    002	
    003	import java.awt.*;
    004	import java.awt.event.*;
    005	import javax.swing.*;
    006	import java.util.Random;
    007	import main.*;
    008	
    009	public class GamePanel extends JPanel implements ActionListener, Runnable
    010	{
    011		Dimension size;   // パネルの大きさ
    012		MainPanel mp;
    013		JButton bt1, bt2;
    014		Image b_image;
    015		Random rand;
    016		Thread td;
    017		boolean in_game = true;
    018		int x[], y[];   // 円の位置
    019				// コンストラクタ
    020		public GamePanel(Dimension size1, MainPanel mp1)
    021		{
    022			size = size1;
    023			mp   = mp1;
    024						// レイアウトマネージャの停止
    025			setLayout(null);
    026						// 画像の読み込み
    027			if (mp.level == 1)
    028				b_image = getToolkit().getImage("image/game1.jpg");
    029			else if (mp.level == 2)
    030				b_image = getToolkit().getImage("image/game2.jpg");
    031			else
    032				b_image = getToolkit().getImage("image/game3.jpg");
    033						// ボタンの配置
    034			Font f = new Font("SansSerif", Font.BOLD, 20);
    035			FontMetrics fm = getFontMetrics(f);
    036			String str1 = "ゲームクリア";
    037			int w1 = fm.stringWidth(str1) + 40;
    038			int h1 = fm.getHeight() + 10;
    039			bt1 = new JButton(str1);
    040			bt1.setFont(f);
    041			bt1.addActionListener(this);   // アクションリスナ
    042			bt1.setSize(w1, h1);
    043	
    044			String str2 = "ゲームオーバー";
    045			int w2 = fm.stringWidth(str2) + 40;
    046			int h2 = fm.getHeight() + 10;
    047			bt2 = new JButton(str2);
    048			bt2.setFont(f);
    049			bt2.addActionListener(this);   // アクションリスナ
    050			bt2.setSize(w2, h2);
    051	
    052			int w = size.width / 2 - (w1 + w2 + 5) / 2;
    053			bt1.setLocation(w, size.height-h1-20);
    054			add(bt1);
    055			bt2.setLocation(w+w1+5, size.height-h1-20);
    056			add(bt2);
    057						// ランダム変数の初期化,初期位置の決定
    058			rand = new Random();
    059			x    = new int [3];
    060			y    = new int [3];
    061			for (int i1 = 0; i1 < 3; i1++) {
    062				x[i1] = rand.nextInt(size.width - 40);
    063				y[i1] = rand.nextInt(size.height - 40);
    064			}
    065						// スレッドの生成
    066			td = new Thread(this);
    067			td.start();
    068		}
    069				// スレッドの実行
    070		public void run()
    071		{
    072			while (in_game) {
    073				try {
    074					if (mp.level == 1)
    075						td.sleep(500);
    076					else if (mp.level == 2)
    077						td.sleep(100);
    078					else
    079						td.sleep(20);
    080				}
    081				catch (InterruptedException e) {}
    082				for (int i1 = 0; i1 < 3; i1++) {
    083					x[i1] = rand.nextInt(size.width - 40);
    084					y[i1] = rand.nextInt(size.height - 40);
    085				}
    086				repaint();
    087			}
    088		}
    089				// 描画
    090		public void paintComponent(Graphics g)
    091		{
    092			super.paintComponent(g);   // 親クラスの描画
    093			g.drawImage(b_image, 0, 0, this);
    094			for (int i1 = 0; i1 < 3; i1++) {
    095				if (i1 == 0)
    096					g.setColor(Color.red);
    097				else if (i1 == 1)
    098					g.setColor(Color.green);
    099				else
    100					g.setColor(Color.gray);
    101				g.fillOval(x[i1], y[i1], 40, 40);
    102			}
    103		}
    104				// ボタンがクリックされたときの処理
    105		public void actionPerformed(ActionEvent e)
    106		{
    107			in_game = false;
    108			if (e.getSource() == bt1)   // ゲームクリア
    109				mp.state = 2;
    110			else   // ゲームオーバー
    111				mp.state = 3;
    112		}
    113	}
    			
    027 行目~ 032 行目

      GamePanel クラスでは,setBackground メソッドによって背景色を設定する方法ではなく,背景に画像を設定しています.Toolkit クラスのメソッド getImage によってその画像を読み込み,Image クラスのオブジェクト b_image に代入しています(ゲームレベルによって異なる画像).

    034 行目~ 056 行目

      JButton クラスのオブジェクトを 2 つ生成し,このパネルに追加しています.また,各ボタンには,ActionListener を登録しています.

    058 行目

      Random クラスのオブジェクトを生成しています.062 行目,及び,063 行目では,Random クラスのメソッド nextInt を使用して,[ 0, size.width - 40 ] 区間,及び,[ 0, size.height - 40 ] 区間のランダムな整数(整数乱数)を生成しています.

    059,060 行目

      フィールド(メンバー変数) x 及び y は,描画する 3 つの円の座標を保存するためのものです.今まで述べてきた方法を使用するとすれば,3 つの円の座標は各々異なるため,x 及び y 座標を保存するために,例えば,
    	int x1, x2, x3;
    	int y1, y2, y3;				
    のように,各々 3 つの変数が必要です.しかし,このようにすると,061 行目~ 064 行目のような繰り返し文も使用できません.円の数が 3 つ程度の場合はそれほど問題になりませんが,円の数が多くなればほとんど処理ができなくなります.

      そこで,ここでは,配列array )を使用しています.配列変数は,
    	型 変数名[] = new 型 [ n ];
    	int 変数名[] = { 1, 2, 3, ・・・ };   // 初期設定したい場合(型が int の場合)				
    のように,new 演算子を使用して(上の例で示すように,初期設定する場合は必要ない),指定した型の変数を保存できる場所を n 個だけ確保するといった形で宣言します.このプログラムの場合は,018 行目,059 行目,及び,060 行目によって,int 型の変数を 3 つ保存できる場所が,フィールド(メンバー変数) x 及び y に対して確保されます.それらの 3 つの場所は,各々,x[0],x[1],x[2] のように括弧と添え字によって参照することができます.その際,添え字の値は,確保した数が n である場合,0 ~ n-1 であることに注意して下さい.

    061 行目~ 064 行目

      for 文を使用して繰り返しの処理を行っています.変数 i1 の値は最初 0 に設定され( i1 = 0 ),062 行目,063 行目が実行された後 1 だけ増加します( i1++ ).i1 の値が 3 未満である場合は( i1 < 3 ),062 行目,063 行目が繰り返し実行されます.従って,062 行目,063 行目は,各々 3 回実行されることになります.その結果,x[i], y[i] ( i = 0, 1, 2 ) には,ランダムに決定された整数値が代入されます.なお,「 ++ 」は,インクリメント演算子と呼ばれ,1 だけ増加させる演算子であり,
    	i1 += 1;
    	i1 = i1 + 1;				
    などと同じ結果が得られます.同様に,1 だけ減少させる演算子「 -- 」(デクリメント演算子)も存在します.今後,頻繁に使用されますので,算術演算子や代入演算子に関しても良く理解しておいて下さい.

      最後に,変数 i1 について説明しておきます.変数 i1 は,ここで( for 文の中で),型宣言が行われています.そのため,61 行目で型宣言された変数 i1 の有効範囲は for ブロック( 61 行目~ 64 行目)の中だけになります.また,for 文の中で for 文が使用されたような場合( for 文のネスト,多重ループ),外側のブロックで型宣言された変数はその内側のブロック内でも有効になりますが,内側のブロックにおいて型宣言された変数は,内側のブロック内だけで有効になります.以上の点は,if ブロックに関しても同様です.なお,C/C++ とは異なり,ブロックの外側で型宣言された変数と同じ名前の変数を,その内側のブロックで型宣言することは許されません.ただし,メソッドの外側で型宣言された変数と同じ名前の変数を,メソッド内で型宣言することは許されます(「変数の有効範囲」参照)
    070 行目~ 088 行目

      Thread クラスのメソッド run をオーバーライドしています.レベルによって指定された間隔で( 074 行目~ 079 行目),各円の位置を再計算した後( 082 行目~ 085 行目),再描画しています( 086 行目,repaint ).この例に示すような方法で,図形を少しずつ異なった位置に描画すればアニメーションanimation )になる分けです.

    090 行目~ 103 行目

      paintComponent メソッドのオーバーライドです.093 行目で背景画像を描画( drawImage )した後,円毎に描く色を変え( 095 行目~ 100 行目,setColor ),( x[i1], y[i1] ) で指定された位置に,直径 40 ピクセルの円を描いています( 101 行目,fillOval ).

    105 行目~ 112 行目

      ボタンがクリックされたときに対するイベント応答メソッドをオーバーライドしています.スレッドを停止( 107 行目)した後,MainPanel のフィールド(メンバー変数) state の値を,ボタン bt1 (ゲームクリア)の場合は 2 に( 109 行目),また,ボタン bt2 (ゲームオーバー)の場合は 3 に( 111 行目)設定しています.

  7. GameClearPanel クラス

      ゲームクリアを表すパネルです.表示されたボタンによって,ゲームを終了するか,または,次のレベルに進む(レベル 3 の場合は,最初から再開)かを選択します.そのソースファイル( GameClearPanel.java )は以下に示すとおりです.

      61 行目~ 77 行目において,ボタンがクリックされたときに対するイベント応答メソッドをオーバーライドしています.「ゲーム終了」ボタンをクリックした場合( 63 行目~ 67 行目)は,ゲームを終了する( 64 行目)と共に,ボタンをクリックできないようにしています( 65,66 行目,setEnabled ).「次のレベル」ボタンをクリックした場合( 68 行目~ 76 行目)は,レベルを 1 だけアップし,ゲームを継続します( 69 行目,75 行目).ただし,レベルが 3 を超えた場合は,レベルを 1 に設定しスタート画面に戻ります( 70 行目~ 83 行目).

    01	package clear;
    02	
    03	import java.awt.*;
    04	import java.awt.event.*;
    05	import javax.swing.*;
    06	import main.*;
    07	
    08	public class GameClearPanel extends JPanel implements ActionListener
    09	{
    10		Dimension size;   // パネルの大きさ
    11		MainPanel mp;
    12		JButton bt1, bt2;
    13				// コンストラクタ
    14		public GameClearPanel(Dimension size1, MainPanel mp1)
    15		{
    16			size = size1;
    17			mp   = mp1;
    18						// レイアウトマネージャの停止
    19			setLayout(null);
    20						// 背景色の設定
    21			setBackground(Color.white);
    22						// ボタンの配置
    23			Font f = new Font("SansSerif", Font.BOLD, 20);
    24			FontMetrics fm = getFontMetrics(f);
    25			String str1 = "ゲーム終了";
    26			int w1 = fm.stringWidth(str1) + 40;
    27			int h1 = fm.getHeight() + 10;
    28			bt1 = new JButton(str1);
    29			bt1.setFont(f);
    30			bt1.addActionListener(this);   // アクションリスナ
    31			bt1.setSize(w1, h1);
    32			bt1.setLocation(size.width/2-w1-5, size.height-h1-20);
    33			add(bt1);
    34	
    35			String str2;
    36			if (mp.level == 3)
    37				str2 = "最初から再開";
    38			else
    39				str2 = "次のレベル";
    40			int w2 = fm.stringWidth(str2) + 40;
    41			int h2 = fm.getHeight() + 10;
    42			bt2 = new JButton(str2);
    43			bt2.setFont(f);
    44			bt2.addActionListener(this);   // アクションリスナ
    45			bt2.setSize(w2, h2);
    46			bt2.setLocation(size.width/2+5, size.height-h2-20);
    47			add(bt2);
    48		}
    49				// 描画
    50		public void paintComponent(Graphics g)
    51		{
    52			super.paintComponent(g);   // 親クラスの描画
    53			Font f = new Font("SansSerif", Font.BOLD, 40);
    54			FontMetrics fm = g.getFontMetrics(f);
    55			String str = "Game Clear!";
    56			int w = fm.stringWidth(str);
    57			g.setFont(f);
    58			g.drawString(str, size.width/2-w/2, size.height/2);
    59		}
    60				// ボタンがクリックされたときの処理
    61		public void actionPerformed(ActionEvent e)
    62		{
    63			if (e.getSource() == bt1) {
    64				mp.state = 4;
    65				bt1.setEnabled(false);
    66				bt2.setEnabled(false);
    67			}
    68			else {
    69				mp.level++;
    70				if (mp.level > 3) {   // 最初からゲーム再開
    71					mp.state = 0;
    72					mp.level = 1;
    73				}
    74				else   // レベルアップ
    75					mp.state = 1;
    76			}
    77		}
    78	}
    			

  8. GameOverPanel クラス

      ゲームオーバーを表すパネルです.表示されたボタンによって,ゲームを終了,現在のレベルでゲーム再開,または,最初から再開するかを選択します.そのソースファイル( GameOverPanel.java )は以下に示すとおりです.

    01	package over;
    02	
    03	import java.awt.*;
    04	import java.awt.event.*;
    05	import javax.swing.*;
    06	import main.*;
    07	
    08	public class GameOverPanel extends JPanel implements ActionListener
    09	{
    10		Dimension size;   // パネルの大きさ
    11		MainPanel mp;
    12		JButton bt1, bt2, bt3;
    13				// コンストラクタ
    14		public GameOverPanel(Dimension size1, MainPanel mp1)
    15		{
    16			size = size1;
    17			mp   = mp1;
    18						// レイアウトマネージャの停止
    19			setLayout(null);
    20						// 背景色の設定
    21			setBackground(Color.white);
    22						// ボタンの配置
    23			Font f = new Font("SansSerif", Font.BOLD, 10);
    24			FontMetrics fm = getFontMetrics(f);
    25			String str1 = "終了";
    26			int w1 = fm.stringWidth(str1) + 40;
    27			int h1 = fm.getHeight() + 10;
    28			bt1 = new JButton(str1);
    29			bt1.setFont(f);
    30			bt1.addActionListener(this);   // アクションリスナ
    31			bt1.setSize(w1, h1);
    32	
    33			String str2 = "現在のレベルで再開";
    34			int w2 = fm.stringWidth(str2) + 40;
    35			int h2 = fm.getHeight() + 10;
    36			bt2 = new JButton(str2);
    37			bt2.setFont(f);
    38			bt2.addActionListener(this);   // アクションリスナ
    39			bt2.setSize(w2, h2);
    40	
    41			String str3 = "最初から再開";
    42			int w3 = fm.stringWidth(str3) + 40;
    43			int h3 = fm.getHeight() + 10;
    44			bt3 = new JButton(str3);
    45			bt3.setFont(f);
    46			bt3.addActionListener(this);   // アクションリスナ
    47			bt3.setSize(w3, h3);
    48	
    49			int w = size.width / 2 - (w1 + w2 + w3 + 10) / 2;
    50			bt1.setLocation(w, size.height-h1-20);
    51			add(bt1);
    52			w += (w1 + 5);
    53			bt2.setLocation(w, size.height-h1-20);
    54			add(bt2);
    55			w += (w2 + 5);
    56			bt3.setLocation(w, size.height-h1-20);
    57			add(bt3);
    58	
    59		}
    60				// 描画
    61		public void paintComponent(Graphics g)
    62		{
    63			super.paintComponent(g);   // 親クラスの描画
    64			Font f = new Font("SansSerif", Font.BOLD, 40);
    65			FontMetrics fm = g.getFontMetrics(f);
    66			String str = "Game Over!";
    67			int w = fm.stringWidth(str);
    68			g.setFont(f);
    69			g.drawString(str, size.width/2-w/2, size.height/2);
    70		}
    71				// ボタンがクリックされたときの処理
    72		public void actionPerformed(ActionEvent e)
    73		{
    74			if (e.getSource() == bt1) {   // ゲーム終了
    75				mp.state = 4;
    76				bt1.setEnabled(false);
    77				bt2.setEnabled(false);
    78				bt3.setEnabled(false);
    79			}
    80			else if (e.getSource() == bt2)   // 現在のレベルでゲーム再開
    81				mp.state = 1;
    82			else {   // 最初からゲーム再開
    83				mp.state = 0;
    84				mp.level = 1;
    85			}
    86		}
    87	}
    			

情報学部 菅沼ホーム Java 目次 索引