JavaFXとSceneBuilderで簡単なじゃんけんゲーム

夏にやるのかAdventCalendar第10回目です

#はじめに
今回はタイトルの通シンプルなじゃんけんゲームを作りたいと思います。
私が以前投稿させていただいた、JavaFX導入の記事の続きくらいの認識で読んでいただけると幸いです。
-[はじめてのJavaFX ~簡単な導入Hello world的なGUI作成まで~](https://qiita.com/Kei_22/items/f54e6ba7cd4bf71b14c8)　

###環境
-macOS Sierra 10.12.6
-Eclipse Oxygen 4.7.3a(プラグインでe(fx)clipse導入済み)
-Scene Builder2.0

#実際の作成過程

今回は、前回の記事と違う部分として、application以外のパッケージを作る、画像を使う要素が含まれます。

###作りたいモノのイメージ
今回作成するゲームの中の流れは以下のようなものを想定しています。

1. 待機画面
2. ゲーム開始(「さいしょはグー、じゃんけん...」で入力待機)
3. プレイヤーの入力を受けてじゃんけんのリザルト画面へ
4. 1の待機画面へ

###じゃんけんで用意する画像
今回はフリーの素材サイトから拝借しました。

-[じゃんけん・グーのイラスト](https://www.sozai-library.com/sozai/9344)
-[じゃんけん・チョキ・ピースのイラスト](https://www.sozai-library.com/sozai/9356)
-[じゃんけん・パーのイラスト](https://www.sozai-library.com/sozai/9357)

###プロジェクト内のパッケージ構成
- application: FXのGUI関係のソースがここに入る
- game: 今回のじゃんけんの処理がここに入る
- pic: じゃんけんの手の画像を入れておく。グー:Rock.png, チョキ:Scissors.png, パー:Papaer.png

###SceneBuilder内の操作
SceneBuilder内で操作をします。
前準備として, 前回と同じ手順でeclipseでJavaFXプロジェクトの作成をしてください。
プロジェクト名は「JankenFX」にします。(任意のものでも構いません)
プロジェクトが生成されたら、applicationパッケージのMain.javaを一部書き換えます。
Scene scene = new Scene(root,400,400);
となっている行を
Scene scene = new Scene(root, **500** ,400);
に書き換えます。前準備は以上です。
<br>
前準備が終わりましたら、Form.fxmlをSceneBuilderにて開いてください。
最初は中央には何もない状態ですので、画面左下のBorderPaneをクリック、画面右のLayoutでPref widthを500、Pref Heightを400に設定します。ここまでで、画面中央に白いパネルが出てくると思います。

今回のScene Builderでの作業が終わった段階の状態の画像を先に貼ります。
![スクリーンショット 2018-08-22 20.42.53.png](https://qiita-image-store.s3.amazonaws.com/0/281151/30dc67e3-e2ef-6773-74f3-caba3be47591.png)

BorderPaneの場所ごとに行っている操作は下記の通りです
1. Top
 - Label: タイトルや進行状況に応じた文を表記する。fxidに"label_text"を書いています。
 
2. Center
    -HBox: HBox内に入ってるパーツは水平に等しく並べられる。propaties内のAlignmentをCENTERにしています。
HBox内
 - ImageView(左): 自分のじゃんけんの手を描写します。fxidに"image_player"を書いています。
 - ImageView(右): 相手のじゃんけんの手を描写します。fxidに"image_enemy"を書いています。

3. Left
 - Label: 自分の手だとわかるように、"あなた"と propatiesのtextに書いています。

4. Right
 - Label: 相手の手だとわかるように、"あいて"と propatiesのtextに書いています。

5. Bottom
 - HBox:説明省略
HBox内
 - Button (OK): ゲーム画面を進めるためのボタンです。fxidは"button_OK"で、On Actionは"onOKClicked"を書いています。
 - Button (グー): じゃんけんで自分の手を出すためのボタンです。fxidは"button_Rock"で、On Actionは"onRockClicked"を書いています。
 - Button (チョキ): じゃんけんで自分の手を出すためのボタンです。fxidは"button_Scissors"で、On Actionは"onScissorsClicked"を書いています。
 - Button (パー): じゃんけんで自分の手を出すためのボタンです。fxidは"button_Paper"で、On Actionは"onPaperClicked"を書いています。

ここまでの操作、記入を終えたForm.fxmlは下記のとおりです。

``` Form.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.image.*?>
<?import javafx.scene.text.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.BorderPane?>

<BorderPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="500.0" 
xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="application.FormController">
   <bottom>
      <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" BorderPane.alignment="CENTER">
         <children>
            <Button fx:id="button_OK" mnemonicParsing="false" onAction="#onOKClicked" text="OK" />
            <Button fx:id="button_Rock" mnemonicParsing="false" onAction="#onRockClicked" text="グー" />
            <Button fx:id="button_Scissors" mnemonicParsing="false" onAction="#onScissorsClicked" text="チョキ" />
            <Button fx:id="button_Paper" mnemonicParsing="false" onAction="#onPaperClicked" text="パー" />
         </children>
      </HBox>
   </bottom>
   <top>
      <Label fx:id="label_text" alignment="CENTER" prefHeight="80.0" prefWidth="400.0" text="じゃんけんゲーム" BorderPane.alignment="CENTER">
         <font>
            <Font size="20.0" />
         </font>
      </Label>
   </top>
   <center>
      <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" BorderPane.alignment="CENTER">
         <children>
            <ImageView fx:id="image_player" fitHeight="188.0" fitWidth="200.0">
               <image>
                  <Image url="@../pic/Rock.png" />
               </image>
            </ImageView>
            <ImageView fx:id="image_enemy" fitHeight="188.0" fitWidth="200.0">
               <image>
                  <Image url="@../pic/Rock.png" />
               </image>
            </ImageView>
         </children>
      </HBox>
   </center>
   <right>
      <Label text="あいて" BorderPane.alignment="CENTER">
         <font>
            <Font size="15.0" />
         </font>
      </Label>
   </right>
   <left>
      <Label text="あなた" BorderPane.alignment="CENTER">
         <font>
            <Font size="15.0" />
         </font>
      </Label>
   </left>
</BorderPane>
```

###Eclipseでのソース作成
作成するプログラムは、FormController.javaの追記とgameパッケージにBattle.javaを作成する２つになります。
今回作成したソースは、筆者の経験量の浅さもあり、**無駄のあるソースとなっている**と思います。ご了承ください。

####FormController.java
書き換えたものが下記になります。

```FormController.java
package application;

import java.util.Random;
import game.Battle;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

public class FormController {
	@FXML private Button button_OK;
	@FXML private Button button_Rock;
	@FXML private Button button_Scissors;
	@FXML private Button button_Paper;
	@FXML private Label label_text;
	@FXML private ImageView image_player;
	@FXML private ImageView image_enemy;

	private Battle battle= new Battle();
	private final Image ROCK= new Image("pic/Rock.png");
	private final Image SCISSORS= new Image("pic/Scissors.png");
	private final Image PAPER= new Image("pic/Paper.png");

	private int hand_player;
	private int hand_enemy;
	private int result; //じゃんけんの結果をintで保有

	private Random rand= new Random(); //相手の出す手をランダムに生成


//	進行状況1(じゃんけんの入力待機)以外のときに、進行状況を進める
	@FXML
	public void onOKClicked() {
		if(battle.getPhase() != 1){
		battle.nextPhase();
		draw();
		}
	}
	@FXML
	public void onRockClicked() {
		if(battle.getPhase() == 1) {
		hand_player= 1;
		hand_enemy= rand.nextInt(3)+ 1;
		result= battle.battle(hand_player, hand_enemy, battle.getPhase());
		battle.nextPhase();
		draw();
		}
	}
	@FXML
	public void onScissorsClicked() {
		if(battle.getPhase() == 1) {
		hand_player= 2;
		hand_enemy= rand.nextInt(3)+ 1;
		result= battle.battle(hand_player, hand_enemy, battle.getPhase());
		battle.nextPhase();
		draw();
		}
	}
	@FXML
	public void onPaperClicked() {
		if(battle.getPhase() == 1) {
		hand_player= 3;
		hand_enemy= rand.nextInt(3)+ 1;
		result= battle.battle(hand_player, hand_enemy, battle.getPhase());
		battle.nextPhase();
		draw();
		}
	}

//	進行状況に応じて描画の更新をする
	public void draw() {
		switch (battle.getPhase()) {
		case 0:
			label_text.setText("じゃけんゲーム");
			image_player.setImage(ROCK);
			image_enemy.setImage(ROCK);
			break;
		case 1:
			label_text.setText("さいしょはグー、じゃんけん...");
			break;
		case 2:
			image_player.setImage(changeImage(hand_player));
			image_enemy.setImage(changeImage(hand_enemy));
			switch (result) {
			case 1:
				label_text.setText("あなたの勝ちです！！");
				break;
			case 2:
				label_text.setText("あなたの負けです...");
				break;
			case 3:
				label_text.setText("あいこです！");
			default:
				break;
			}
			break;

		default:
			break;
		}
	}

//	じゃんけんの手の画像の更新をする
	private Image changeImage(int hand) {
		switch (hand) {
		case 1:
			return ROCK;
		case 2:
			return SCISSORS;
		case 3:
			return PAPER;
		default:
			return ROCK; //例外
		}
	}
}
```

<br>
続いて、じゃんけんの処理をするBattle.javaです。

```Battle.java
package game;

// じゃんけんの処理を行うクラス
public class Battle {

	private int phase; //ゲームの進行状況を保有する

//	オブジェクトの生成時に進行状況0:最初の待機画面で初期化する
	public Battle() {
		this.phase= 0;
	}

//	じゃんけんの処理を行う。自分の勝ちなら1, 負けなら2, あいこなら3をかえす
	public int battle(int p, int e, int phase) { //p:自分の手, e,相手の手
		if(phase==1) {  //進行状況1: 入力待ちのときのみ作動
//			じゃんけんの手は、グー:1, チョキ:2, パー:3
			if(p==1) {
				if(e==1) return 3;
				else if(e==2) return 1;
				else if(e==3) return 2;
			}

			else if(p==2) {
				if(e==1) return 2;
				else if(e==2) return 3;
				else if(e==3) return 1;
			}

			else if(p==3) {
				if(e==1) return 1;
				else if(e==2) return 2;
				else if(e==3) return 3;
			}
		}
		return -1; //例外
	}

	//進行状況を次の段階へ進める。phaseが3になったら、0へ更新
	public void nextPhase() {
		phase++;
		if(phase>2)phase= 0;
	}

	//進行状況のゲッター
	public int getPhase() {
		return phase;
	}
}
```
以上ふたつのクラスの作成と、じゃんけんの手の画像をpicパッケージにコピーアンドペーストが完了すれば、完成になります。お疲れ様です。

#実際に動かす
1. 初期画面![スクリーンショット 2018-08-22 21.26.09.png](https://qiita-image-store.s3.amazonaws.com/0/281151/426cae40-d493-3de0-328e-80fccf3eed35.png)

2. 入力待ち画面
![スクリーンショット 2018-08-22 21.26.13.png](https://qiita-image-store.s3.amazonaws.com/0/281151/16fdaa33-f530-a35a-8b4d-0be410d9b13d.png)

 
3. 結果　OKボタンを押すと1に戻る
![スクリーンショット 2018-08-22 21.26.21.png](https://qiita-image-store.s3.amazonaws.com/0/281151/f8d66dac-c5d5-08ae-9f4b-16085a883ed8.png)

#おわりに
今回は、前回のハローワールドからの延長線というポジションで、別パッケージのクラスを利用したり、画像読み込みをしてじゃんけんのゲームを作成しました。
利用する画像を変えたり、SceneBuilderでレイアウトをいじったりして、練習材料にしていただけたら幸いです。

ここまで読んでいただきありがとうございました。