今回は右の図のように
プレーヤーを選択するメニュー、
音声の出力を制御するチェックボックス、
ゲームを開始/中断するボタンを追加します。
初期状態では、メニューと「Start」ボタンを表示します。
ゲーム中は、メニューを隠し、「Stop」ボタンを表示します。
JDK1.0.2には以下のGUIコンポーネントが用意されています。
これらのコンポーネントは操作したときにイベントを発生します。
Button
クラス
ACTION_EVENT
が発生する。
Checkbox
クラス
boolen
の値を持っている。
選択状態が変化したときにACTION_EVENT
が発生する。
複数のチェックボックスを排他的に選択状態にするためには
CheckboxGroup
クラスを使用する。
Choice
クラス
ACTION_EVENT
が発生する。
List
クラス
ACTION_EVENT
が発生する。
TextField
クラス
ACTION_EVENT
が発生する。
他にもMenuBar
、ScrollBar
などのクラスがあります。詳細はJDKのドキュメントを参照して下さい。
今回はChoice
、Checkbox
、Button
を使用します。
下の図の階層でコンポーネントを配置します。
今までUpperHandPlayerView
を配置していた部分に
Panel
を配置し、
CradLayout
クラスを使ってChoice
と
UpperHnadPlayerView
を切り替えられるようにします。
右下の空いていた部分には、Checkbox
と、
2つのButton
を切り替えるPanel
を配置します。
まず、UpperHand
クラスのインスタンス変数に
GUIコンポーネントを示す変数を追加します。
//----------------------------------------------------------------------------- // インスタンス変数定義 //----------------------------------------------------------------------------- ... // プレーヤー選択メニュー private Choice playerChoice[] = new Choice[2]; // プレーヤー/メニュー表示切り替え用パネル private Panel playerPanel[] = new Panel[2]; // 音声モード切り替えチェックボックス private Checkbox soundBox; // ゲーム開始ボタン private Button startButton; // ゲーム中断ボタン private Button stopButton; // ボタン切り替え用パネル private Panel buttonPanel;
次に、コンポーネントを作成します。
プレーヤーの名前はクラス変数playerName
に
あらかじめ定義しておきました。
//----------------------------------------------------------------------------- // private void createComponent() // コンポーネントを作成する。 //----------------------------------------------------------------------------- private void createComponent() { // すべてのコンポーネントを取り除く。 removeAll(); // ゲーム盤を表示するコンポーネントを作成する。 boardView = new UpperHandBoardView(game, this); for (int p = UpperHandGame.FIRST; p <= UpperHandGame.SECOND; p++) { // プレーヤーを表示するコンポーネントを作成する。 playerView[p] = new UpperHandPlayerView(game, p); // 持ち玉を表示するコンポーネントを作成する。 ballView[p] = new UpperHandBallView(game, p, this); // プレーヤー選択メニューを作成する。 playerChoice[p] = new Choice(); for (int i = 0; i < playerName.length; i++) { // プレーヤーの名前をメニューに追加する。 playerChoice[p].addItem(playerName[i]); } // プレーヤー/メニュー切り替え用パネルを作成する。 playerPanel[p] = new Panel(); } // プレーヤー選択メニューの選択肢を初期状態にする。 playerChoice[UpperHandGame.FIRST].select(0); playerChoice[UpperHandGame.SECOND].select(1); // 音声モード切り替えチェックボックスを作成する。 soundBox = new Checkbox("Sound"); soundBox.setState(true); // チェックされた状態にする。 // ゲーム開始ボタンを作成する。 startButton = new Button("Start"); // ゲーム中断ボタンを作成する。 stopButton = new Button("Stop"); // ボタン切り替え用パネルを作成する。 buttonPanel = new Panel(); }
最後に、コンポーネントを配置します。
//----------------------------------------------------------------------------- // private void layoutComponent() // コンポーネントを配置する。 //----------------------------------------------------------------------------- private void layoutComponent() { GridBagLayout layout = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); // レイアウトマネージャを設定する。 setLayout(layout); // ゲーム盤を表示するコンポーネントを配置する。 c.gridheight = GridBagConstraints.REMAINDER; c.fill = GridBagConstraints.NONE; layout.setConstraints(boardView, c); add(boardView); // 以降のコンポーネントの配置方法を設定する。 c.gridheight = 1; c.gridwidth = GridBagConstraints.REMAINDER; c.anchor = GridBagConstraints.NORTH; for (int p = UpperHandGame.FIRST; p <= UpperHandGame.SECOND; p++) { // プレーヤー/メニュー表示切り替えパネルに、プレーヤーを表示する // コンポーネント、プレーヤー選択メニューを配置する。 playerPanel[p].setLayout(new CardLayout()); playerPanel[p].add("MENU", playerChoice[p]); playerPanel[p].add("VIEW", playerView[p]); // プレーヤー/メニュー表示切り替えパネルを配置する。 c.fill = GridBagConstraints.HORIZONTAL; layout.setConstraints(playerPanel[p], c); add(playerPanel[p]); // 持ち玉を表示するコンポーネントを配置する。 c.fill = GridBagConstraints.NONE; layout.setConstraints(ballView[p], c); add(ballView[p]); } // 以降のコンポーネントの配置方法を設定する。 c.gridheight = GridBagConstraints.REMAINDER; c.gridwidth = 1; // 音声モード切り替えチェックボックスを配置する。 c.anchor = GridBagConstraints.NORTHWEST; layout.setConstraints(soundBox, c); add(soundBox); // ボタン切り替えパネルに、ゲーム開始ボタン、ゲーム中断ボタンを配置する。 buttonPanel.setLayout(new CardLayout()); buttonPanel.add("START", startButton); buttonPanel.add("STOP", stopButton); // ボタン切り替えパネルを配置する。 c.anchor = GridBagConstraints.NORTHEAST; layout.setConstraints(buttonPanel, c); add(buttonPanel); }
ボタンが押されるとACTION_EVEVT
が発生し、
action()
メソッドが呼び出されます。
Button
クラス、Panel
クラスには
action()
メソッドが定義されていないので、
イベントはコンテナに伝播します。
UpperHand
クラスにaction()
メソッドを定義し、
イベントを処理することにします。
//----------------------------------------------------------------------------- // public boolean action(Event evt, Object arg) // 他のボタンからのイベントを処理する。 //----------------------------------------------------------------------------- public boolean action(Event evt, Object arg) { if (evt.target == startButton) { // ゲーム開始ボタンが押されたとき playSound(START_GAME_SOUND); startGame(); // ゲームを開始する。 return true; } else if (evt.target == stopButton) { // ゲーム中断ボタンが押されたとき playSound(STOP_GAME_SOUND); stopGame(); // ゲームを終了状態にする。 return true; } return super.action(evt, arg); // スーパークラスに処理を任せる。 }
UpperHand
クラスのmouseDown()
メソッドで
ゲームを再開していた処理は不要になったので削除しました。
//----------------------------------------------------------------------------- // public boolean mouseDown(Event evt, int x, int y) // マウスのボタンが押されたときの処理を行う。 //----------------------------------------------------------------------------- public boolean mouseDown(Event evt, int x, int y) { playSound(BAD_MOVE_SOUND); // 不正な着手のときの音を出す。 return true; }
UpperHand
クラスに、現在ゲーム中か否かを示すインスタンス変数
isStopGame
を追加し、
startGame()
、stopGame()
で
コンポーネントとともに切り替えます。
//----------------------------------------------------------------------------- // インスタンス変数定義 //----------------------------------------------------------------------------- ... // ゲームが終了状態のときtrue private boolean isStopGame;
//----------------------------------------------------------------------------- // public void startGame() // ゲームを開始する。 //----------------------------------------------------------------------------- public void startGame() { // ゲーム中の表示に切り替える。 for (int p = UpperHandGame.FIRST; p <= UpperHandGame.SECOND; p++) { Panel panel = playerPanel[p]; ((CardLayout)panel.getLayout()).show(panel, "VIEW"); // プレーヤー選択メニューを隠す。 } ((CardLayout)buttonPanel.getLayout()).show(buttonPanel, "STOP"); // ゲーム中断ボタンを表示する。 initPlayer(); // プレーヤーを初期化する。 game.init(); // ゲームを初期化する。 isStopGame = false; // ゲーム中にする。 nextMove(); // 次の一手に進む。 }
//----------------------------------------------------------------------------- // public void stopGame() // ゲームを終了状態にする。 //----------------------------------------------------------------------------- public void stopGame() { isStopGame = true; // ゲーム終了状態にする。 // ゲーム終了状態の表示に切り替える。 for (int p = UpperHandGame.FIRST; p <= UpperHandGame.SECOND; p++) { Panel panel = playerPanel[p]; ((CardLayout)panel.getLayout()).show(panel, "MENU"); // プレーヤー選択メニューを表示する。 } ((CardLayout)buttonPanel.getLayout()).show(buttonPanel, "START"); // ゲーム開始ボタンを表示する。 }
initPlayer()
では、
プレーヤー選択メニューの状態にしたがいプレーヤーを初期化するようにしました。
人間同士、コンピュータ同士の対戦も可能です。
//----------------------------------------------------------------------------- // private void initPlayer() // プレーヤー選択メニューの状態にしたがいプレーヤーを初期化する。 //----------------------------------------------------------------------------- private void initPlayer() { for (int p = UpperHandGame.FIRST; p <= UpperHandGame.SECOND; p++) { // プレーヤーを初期化する。 if (playerChoice[p].getSelectedIndex() == 0) { // 人間が選択されているとき player[p] = null; } else { // コンピュータが選択されているとき // コンピュータのプレーヤーを生成する。 player[p] = new UpperHandPlayer(); } // プレーヤーの名前を初期化する。 playerView[p].playerName(playerChoice[p].getSelectedItem()); } }
Appletがロードされたときはゲームが終了状態になっている必要があるので、
init()
の最後でstopGame()
を呼び出すようにしました。
//----------------------------------------------------------------------------- // public void init() // Appletの初期化処理を行う。 //----------------------------------------------------------------------------- public void init() { ... // ゲームを終了状態にする。 stopGame(); }
コンピュータ同士の対戦のとき、目に見えないくらいの早さで着手してしまうので、
nextMove()
に0.5秒休む処理を追加しました。
//----------------------------------------------------------------------------- // private void nextMove() // 次の一手に進む。 // 次のプレーヤーがコンピュータのときは、次に一手をコンピュータに選択 // させ、着手する。次のプレーヤーが人間のときは何もせずにリターンし、 // マウスの入力を待つ。 //----------------------------------------------------------------------------- private void nextMove() { int p = game.nextPlayer(); if (player[p] != null) { // 次のプレーヤーがコンピュータのとき try { Thread.sleep(500); // 0.5秒休止する。 } catch (InterruptedException e) {} // 次の一手をコンピュータに選択させ、着手する。 makeMove(player[p].selectMove(game)); } }
makeMove()
に
ゲームが終了状態のときは着手を行わないようにする処理を追加しました。
//----------------------------------------------------------------------------- // public void makeMove(int p) // 位置pに着手する。 //----------------------------------------------------------------------------- public void makeMove(int p) { if (isStopGame) { // ゲームが終了状態のとき playSound(BAD_MOVE_SOUND); // 不正な着手のときの音を出す。 return; // 着手は行わない。 } game.makeMove(p); // 着手する。 if (game.isFinish()) { // ゲームが終わったとき finishGame(); // 終局時の処理を行う。 stopGame(); // ゲームを終了状態にする。 } else { playSound(PUT_BALL_SOUND); // 玉を置いた音を出す。 nextMove(); // 次の一手に進む。 } }
finishGame()
には、
コンピュータ同士、人間同士の対戦の場合の処理を追加しました。
//----------------------------------------------------------------------------- // private void finishGame() // 終局時の処理を行う。 //----------------------------------------------------------------------------- private void finishGame() { if (player[UpperHandGame.FIRST] == null && // コンピュータ同士の player[UpperHandGame.SECOND] == null // 対戦のとき || player[UpperHandGame.FIRST] != null && // 人間同士の player[UpperHandGame.SECOND] != null) // 対戦のとき { playSound(PUT_BALL_SOUND); } else { // コンピュータの手番を取得する。 int p = (player[UpperHandGame.FIRST] != null) ? UpperHandGame.FIRST : UpperHandGame.SECOND; // ゲームの勝者を取得する。 int winner = game.winner(); if (winner == UpperHandGame.NUTRAL) { // 引き分け playSound(PUT_BALL_SOUND); } else if (winner == p) { // コンピュータの勝ち playSound(APP_WIN_SOUND); } else { // コンピュータの負け playSound(APP_LOST_SOUND); } } }
playSound()
ではチェックボックスの状態を参照し、
音声を出力する状態のときのみ音声を出力するようにしました。
//----------------------------------------------------------------------------- // private void playSound(int id) // idで指定された音声を出力する。 //----------------------------------------------------------------------------- private void playSound(int id) { if (soundBox.getState()) { // 音声を出力する状態のとき sound[id].play(); // 音声を出力する。 } }
Javaソースコード (Ver. 1.1a10)