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)

実行例

実装

考え方

今回やろうとしていることは、会話文を一文字ずつ公開範囲を広げつつその度に出力させれば実装できると考えました。

手順

  1. fxmlファイルの作成(今回はSceneBuilderを利用)
  2. 起動するためのファイルを作成
  3. 画面をコントロールするためのファイルを作成(ここに出力の処理を加えます)
  4. 実行する

fxmlファイルの作成

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

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を使って実現します。

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("ボタンを押してください");

    }
}

ソースファイルはこちらからも見ていただけます。

実行する

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