質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.12%

Java FXでの”待つ”という振る舞いの実装の仕方について

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 6,188

ReiLeiLei1025

score 232

JavaFXを使ってカードゲームを作っています。カードをImageViewで作成し、TranslateTransitionクラスを使ってアニメーションさせました。全員にカードが行きわたったところで、1秒間、間をおいてゲームを始めるという振る舞いをさせたいのですがうまくいきません。Transitionを行った後、Thread.sleep()やwait()などで1秒間スレッドを止めると、スレッドを再開した時、1秒後の偏移状態が表現されてしまいます。(カクっとなったり、すでにアニメーションが終わっていたり。)
なので、まず、Transitionクラスのplayメソッドが終わってからスレッドを止めればいいんだと考えたのですが、それがうまくいきません。ImageViewではありませんが下記のようなクラスを作ってみました。
package jp.ocn;

import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class TransitionTestMain extends Application {
    BooleanProperty flagProperty = new SimpleBooleanProperty(false);
    int abs = 1;

    @Override
    public void start(Stage primaryStage) throws Exception {

        Button start_button = new Button("Transition Start");
        Label status_label = new Label("Ready");
        Label transition_label = new Label("Transition Label");

        start_button.setLayoutX(250);
        start_button.setLayoutY(100);
        status_label.setLayoutX(270);
        status_label.setLayoutY(50);

        AnchorPane root = new AnchorPane();
        root.getChildren().add(start_button);
        root.getChildren().add(transition_label);
        root.getChildren().add(status_label);

        start_button.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                
                status_label.setText("runnning");

                TranslateTransition transition = new TranslateTransition(
                        new Duration(500), transition_label);
                transition.setToX(500*abs);
                transition.setToY(375*abs);

                transition.setOnFinished(new EventHandler<ActionEvent>() {

                    @Override
                    public void handle(ActionEvent event) {

                        flagProperty.set(true);
                    }

                });

                transition.play();
                
//                 while(!flagProperty.get()){
//               ここでスレッドの進行を止めようと考えました
//                }
                
                abs*=-1;
                status_label.setText("Succeeded");
                flagProperty.set(false);
            }
        });

        Scene scene = new Scene(root, 600, 400);
        primaryStage.setScene(scene);
        primaryStage.centerOnScreen();
        primaryStage.show();

    }

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

}
やってみて、Transitionは非同期処理ではないのだと分かったのですが、では、どのように実装をしたらいいのでしょうか?
transition.play()の後に、ボタン一つのダイアログボックスを表示させ、進行ごとにプレイヤーにクリックしてもらえば、仕様通りにはなりますが、ゲームとして不自然だと思います。
よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+3

こんな感じで、非同期スレッドとJavaFXスレッドを組み合わせるとスリープさせることができます。

※(2015-01-09追記)JavaFXの場合は、ForkJoinPoolではなく、PauseTransition (javafx.animation.PauseTransition)を使うべきとのご指摘がありました。
詳しくは、skrbさんのコメントをご参照ください。

    // 追加するimport
    // import javafx.application.Platform;
    // import java.util.concurrent.ForkJoinPool;

    // ここまでJavaFXスレッドで実行

    // ここでスレッドの進行を止めようと考えました

    ForkJoinPool.commonPool().submit(() -> {
        // 非JavaFXスレッドを非同期で実行
        try {
            Thread.sleep(2000L);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        abs*=-1;
        Platform.runLater(() -> {
            // 後でJavaFXスレッドで実行
            status_label.setText("Succeeded");
        });
        flagProperty.set(false);
    });

この場合、flagPropertyabsの状態の一貫性が崩れる可能性がありますので、同期が必要かどうかを検討してください。具体的には、transition.setToX(500*abs);の次の行でabsの符号が反転する可能性があります。
正しく同期をしないとデッドロックも有り得ますのでご注意ください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/01/15 11:17

    Skrbさん、コメントありがとうございます。

    >JavaFXなどGUIの処理では、イベントが頻繁に発生しているため、GUIを処理させるスレッド(JavaFXの場合、JavaFX Application Threadという名前です)はスリープさせたり、ブロックさせたりしないようにします。

    JavaFXの作法、勉強になりました。

    現在のコードの実装状態をお伝えさせていただくと、例えば、カードゲームのデモアプリがあるとして、ユーザーがボタンを1回押したら、後は延々とコンピュータープレーヤー同士がカードゲームをするような仕様だとすると、カードのアニメーションを置き去りにして、手番だけがどんどん進んでしまうといった感じでしょうか。
    お話によると、スレッドの停止ではなく、TransitionやTimelineクラスとJATの作りこみによって何とかするものだということなので、ヒントも頂いていることですし、もう少し、自分で考えてみようと思います。(setOnFinishedメソッドがキーポイントだと目をつけています)

    キャンセル

  • 2015/01/16 14:54

    結局、分かりませんでした。ただ、PauseTransitionを調べていたところ、PauseTransitionを使って、Widowを閉じるというコードを見つけたのでそれを改良してこのようなコードを組んでみました。

    public class TransitionTestMainOnly extends Application {
    int abs = 1;

    @Override
    public void start(Stage primaryStage) throws Exception {

    Button start_button = new Button("Transition Start");
    Label transition_label = new Label("Transition Label");

    start_button.setLayoutX(250);
    start_button.setLayoutY(50);

    AnchorPane root = new AnchorPane();
    root.getChildren().add(start_button);
    root.getChildren().add(transition_label);

    start_button.setOnAction(new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent event) {

    TranslateTransition transition = new TranslateTransition(
    new Duration(500), transition_label);
    transition.setToX(500 * abs);
    transition.setToY(375 * abs);
    PauseTransition pauseTransition=new PauseTransition(new Duration(500));
    SequentialTransition seqentialTransition=new SequentialTransition(transition,pauseTransition);

    Stage stage = new Stage();
    Label label = new Label("Running");
    AnchorPane root = new AnchorPane();
    root.getChildren().add(label);
    Scene scene = new Scene(root, 200, 60);
    stage.setScene(scene);
    stage.setMaxWidth(200);
    stage.setMaxHeight(60);
    stage.setTitle("メッセージ");
    stage.centerOnScreen();
    stage.initModality(Modality.WINDOW_MODAL);
    final Window window = stage;
    seqentialTransition.setOnFinished(new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent event) {
    window.hide();
    }

    });

    seqentialTransition.play();
    stage.showAndWait();

    abs *= -1;
    }
    });

    Scene scene = new Scene(root, 600, 400);
    primaryStage.setScene(scene);
    primaryStage.centerOnScreen();
    primaryStage.show();

    }

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

    一応、これで、自分のやりたい事は出来たのですが、絶対に、メッセージボックスが出てきてしまいます。メッセージを表示すれば雰囲気的に誤魔化せますが…。

    キャンセル

  • 2015/01/16 15:16

    続けて、レスを失礼します。
    stage.setY();で、画面外を設定すれば、表示もされなくなりますね。

    キャンセル

0

すいません。追加します。
ダイアログボックスうんぬんの話は、showAndWait()メソッドのことです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる