JavaFXで会話っぽく文字を出力する

#概要
JavaFXでゲームを作成した際に、`Label`に会話イベントを表示させたい場面があったので、そのことについて書きます。
##環境
- java 13.0.2
- JavaFX 13.0.2	
- zsh 5.7.1 (x86_64-apple-darwin19.0.0)
- MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports)

##実行例
![text.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520217/4730436b-f2ea-5663-9c82-f2ce465f6d7e.gif)


# 実装
## 考え方
今回やろうとしていることは、会話文を一文字ずつ公開範囲を広げつつその度に出力させれば実装できると考えました。
## 手順
1. fxmlファイルの作成(今回は[SceneBuilder](https://gluonhq.com/products/scene-builder/)を利用)
2. 起動するためのファイルを作成
3. 画面をコントロールするためのファイルを作成(ここに出力の処理を加えます)
4. 実行する

##fxmlファイルの作成
`SceneBuilder`で画面を作成します。今回は文字を出力させるスペースの`Label`と、`Button`を一つずつ設置しました。
コントローラを設定するところがあるので、今回は`TestController`としました。
完成したら、`表示`または`View`から`サンプル・コントローラ・スケルトン`もしくは`Show Sample Controller Skelton`を選びます。
コントローラのスケルトンコードが表示されたら、右下の`FULL`にチェックを入れて、全体をコピーします。
このfxmlを今回は`Test.fxml`という名前で保存します。

```fxml:Test.fxml
<?xml version="1.0" encoding="UTF-8"?>    
    
<?import javafx.scene.control.Button?>    
<?import javafx.scene.control.Label?>    
<?import javafx.scene.layout.Pane?>    
   
    
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="TestController">        
   <children>    
      <Label fx:id="textLabel" layoutX="148.0" layoutY="102.0" prefHeight="120.0" prefWidth="305.0" />    
      <Button fx:id="button" layoutX="260.0" layoutY="292.0" mnemonicParsing="false" onAction="#buttonOnAction" prefHeight="48.0" prefWidth="82.0" />        
   </children>    
</Pane>   
```
## 起動ファイルの作成
JavaFXを起動するためのファイルを作成します。`Start.java`とします。

```java:Start.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.*;
import javafx.scene.Scene;
import javafx.scene.Parent;

public class Start extends Application {
    private Scene startScene;

    @Override
    public void start(Stage primaryStage) throws Exception {
	    primaryStage.setTitle("test");
	    startScene = new Scene(FXMLLoader.load(getClass().getResource("Test.fxml")));
	    primaryStage.setScene(startScene);
	    primaryStage.setResizable(false);
	    primaryStage.show();
    }

    public static void main(String args[]) {
	    launch(args);
    }
}
```

##画面をコントロールするためのファイルを作成
最後に画面をコントロールするためのファイルを作成します。
先述の考えを`Timeline`を使って実現します。

```java:TestController.java
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;

import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.util.Duration;

public class TestController {
    private int i;

    @FXML
    private ResourceBundle resources;

    @FXML
    private URL location;

    @FXML
    private Label textLabel;

    @FXML
    private Button button;

    @FXML
    void buttonOnAction(ActionEvent event) {
        showText("ボタンが押されました");
    }

    void showPerText(String showText, int i){
        textLabel.setText(showText.substring(0, i));
    }

    void showText(String showText){
        i = 0;
        Timeline timeline = new Timeline(
                new KeyFrame(
                        new Duration(100),
		    /*
		       new EventHandler<ActionEvent>(){
		       @Override
		       public void handle(ActionEvent event){
		       i+=1;
		       showPerText(showText, i);
		       }
		       }
		       */
                        (event) -> {
                            i+=1;
                            showPerText(showText, i);
                        }
                )
        );
        timeline.setCycleCount(showText.length());
        timeline.play();
    }

    @FXML
    void initialize() {
        assert textLabel != null : "fx:id=\"textLabel\" was not injected: check your FXML file 'Test.fxml'.";
        assert button != null : "fx:id=\"button\" was not injected: check your FXML file 'Test.fxml'.";
        textLabel.setText("ボタンを押してください");

    }
}
```
ソースファイルは[こちら](https://github.com/Okupedia/conversation_events_JavaFX)からも見ていただけます。

##実行する
コンパイルする時は`*.java`でディレクトリ内の全てのjavaファイルを指定すると楽です。
コンパイルをしたら`java Start`で実行できます。