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

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

新規登録して質問してみよう
ただいま回答率
85.48%
JavaFX

JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

2回答

5257閲覧

JavaFXでTimeline処理による描画の処理落ちについて

3_pyon

総合スコア12

JavaFX

JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2017/10/26 15:27

編集2017/10/26 17:50

###前提・実現したいこと
こちらの質問でCanvasへの再描画についてお聞きした時と最終的に実現したいことは同じです.
再描画についての基本的な部分はTimelineを使うことで実装できたのですが,繰り返しの間隔を短くした時,描画が追いつかず,固まったような状態になります.

できたら繰り返しの処理を下記のように実装したいと思っています.

1.物体の座標の再計算
2.その座標を用いての描画処理
3.【描画処理が終わった後】に規定の空白時間を設け,1に戻る.

このように繰り返しの処理を行いたいのですが,なにか良い方法があればご教授願いたいです…

現状のTimelineでの処理だと,厳密に「上記の1や2の処理が終わった後」での繰り返しになるわけではなく,計算処理量が大きくなるにつれ,「1,2の処理中」に繰り返し処理が起こる可能性があるため,処理落ちのような状態が発生していると思うのですが…

上記の処理をJavaFXで実装するためのヒントや,あるいは処理落ちが起こらない別の方策がございましたらお力をお貸しくださいお願いします.

###発生している問題・エラーメッセージ
間隔を短くした時,表示が追いつかない状態になる.

###該当のソースコード
こちらの質問で示したソースコードに,教えて頂いたTimelineの処理を実装したものです.

Java

1//FXTest2.fxml 2 3<?xml version="1.0" encoding="UTF-8"?> 4 5<?import javafx.scene.canvas.Canvas?> 6<?import javafx.scene.layout.AnchorPane?> 7 8<AnchorPane xmlns="http://javafx.com/javafx/9" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sim.FXTest2Controller"> 9 <children> 10 <AnchorPane layoutX="-98.0" layoutY="-90.0" prefHeight="500.0" prefWidth="500.0"> 11 <children> 12 <Canvas fx:id="Canvas" height="348.0" layoutX="115.0" layoutY="109.0" width="270.0" AnchorPane.leftAnchor="115.0" AnchorPane.topAnchor="109.0" /> 13 </children> 14 </AnchorPane> 15 </children> 16</AnchorPane> 17

Java

1//FXTest2Controller.java 2 3package sim; 4 5 6import javafx.fxml.FXML; 7import javafx.fxml.Initializable; 8import java.net.URL; 9import java.util.ResourceBundle; 10import javafx.scene.paint.Color; 11import javafx.scene.canvas.GraphicsContext; 12import javafx.scene.canvas.Canvas; 13 14public class FXTest2Controller implements Initializable { 15 @FXML Canvas Canvas; 16 GraphicsContext gc; 17 18 public void initialize(URL location, ResourceBundle resources) { 19 20 gc = Canvas.getGraphicsContext2D(); 21 22 Timeline timeline = new Timeline( 23 new KeyFrame( 24 Duration.millis(30)), 25 (event) -> { 26 27 //ここで物体の座標計算をしたとみてください 28 29 draw(); 30 }) 31 ); 32 timeline.setCycleCount(Timeline.INDEFINITE); // 無限に繰り返す 33 timeline.start(); // 開始 34 } 35 36 public void draw() { 37 38 //ここで描画処理を行っています 39 40 } 41} 42 43

Java

1//Main.java 2 3package sim; 4 5import javafx.application.Application; 6import javafx.stage.Stage; 7import javafx.scene.Scene; 8import javafx.scene.canvas.Canvas; 9import javafx.scene.canvas.GraphicsContext; 10import javafx.scene.layout.AnchorPane; 11import javafx.scene.paint.Color; 12import javafx.fxml.FXMLLoader; 13import javafx.fxml.FXML; 14 15public class Main extends Application { 16 17 @Override 18 public void start(Stage primaryStage) { 19 20 try { 21 AnchorPane root = (AnchorPane)FXMLLoader.load(getClass().getResource("FXTest2.fxml")); 22 Scene scene = new Scene(root,400,400); 23 scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); 24 primaryStage.setScene(scene); 25 primaryStage.show(); 26 } catch(Exception e) { 27 e.printStackTrace(); 28 } 29 } 30 31 public static void main(String[] args) { 32 launch(args); 33 } 34}

###試したこと
ひとまずTimelineのループ時間を十分長く取れば処理落ちは起こりませんでした.
ただ,プログラム内部のループ時間によって処理落ちが発生するのはあまり望ましくないと思っていますので,この方法以外での手法をお聞きしたいです…

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

JavaFXのスレッドで時間のかかる処理を行うと、JavaFXの画面描画処理が止まってしまいます。

今回の処理の中では、時間のかかる物理計算をJavaFXのスレッドで実行しています。(Timelineに渡すKeyFrameのコンストラクタ引数のラムダ式)
この物理計算を別なスレッドを用意してそこで実行するのが、改善案の一つになります。

他にも、物理計算が実行前に確定しているのであれば、が前提となりますが、表示の前に予め時系列データとして計算しきってしまうという改善案もあります。

別スレッドで計算する場合、JavaFXには、ServiceとTaskというAPIが用意されています。
Oracle JavaFXドキュメントより、同時実行性およびスレッドの使用

投稿2017/10/28 02:29

boochnich

総合スコア194

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KSwordOfHaste

2017/10/28 04:25 編集

> 時間のかかる物理計算をJavaFXのスレッドで実行 後から提示されたコードを見るとアニメーションフレーム間の時間間隔が30msになっているので、自分の推測とは関係なくboochnichさんのご指摘のとおり「1フレームの更新処理に時間がかかりすぎている」ということがあるかも知れませんね。30ms間隔でのアニメーションでも処理落ちがあるとすれば相当の計算量&描画量であると思います。 処理落ちが起きないとき、起きるときの具体的なフレームレートはどうなのでしょうか?>質問者さん --- 「後から提示されたコード」なんていってしまいましたが、編集時間をみると自分が編集に気づいてないだけだったですね。失礼しました>質問者さん
3_pyon

2017/10/28 04:14

150msなら問題はありません。 100msから時間経過で少し怪しくなって、50msですと初っぱなから画面が固まります。
KSwordOfHaste

2017/10/28 04:18

あらら・・それだとアニメーションとしては時間かけすぎ(または描画量が多すぎ)だと思います。100msというと相当量の計算ができますが、どんな計算をしているのか計算量が分かるような説明があったらよかったと思います。(自分は四角形を数個程度動かすものかと思ってました) 計算そのものは単純でも描画対象が多すぎる(例えば10000個とかそれ以上とか)だと自分の回答にあるようにPrismが処理しきれない可能性もあると思います。
3_pyon

2017/10/28 07:53

説明不足で申し訳ありませんでした… 研究で用いているため、あまり具体的なことは書けないと思い、簡略化しすぎて質問してしまいました。 計算量についても今後記述するよう気をつけます。
KSwordOfHaste

2017/10/28 07:57

なるほど。そういうときは計算量の目安さえ閲覧者にわかるように書けばよいと思います。例えば、O(N^2)の計算量でN=1000ぐらいとか・・・GraphcsContextへ描画命令を一度にどのくらい書き込んでいるとか。
guest

0

###JavaFXの再描画メカニズム

自分はこの辺り感覚的に捉えていたので少しドキュメントを読み直してみました。

GraphicsContextのAPIリファレンスには以下のように書かれています。

必要なパラメータがバッファにプッシュされ、その後、パルスの最後に、レンダリング・スレッドによってCanvasノードのイメージ上にそれらのパラメータがレンダリングされます。

またJavaFXのアーキテクチャーの解説(以下)を見ますと・・・

https://docs.oracle.com/javase/jp/8/javafx/get-started-tutorial/jfx-architecture.htm

JavaFXアプリケーションスレッドで行ったシーングラフの変更(GraphicsContextのメソッドによるCanvasの内容の変更もこれに当たります)は、Prismレンダー・スレッドによりレンダリングが行われ、パルスによってJavaアプリケーションスレッドとPrismレンダー・スレッドが同期されること、パルスが最大で1秒当たり60フレーム(fps)に制限されることなどが書かれています。

###処理落ちの原因(推測)

繰り返しの間隔を短くした時,描画が追いつかず,固まったような状態になります.

これは、アニメーションフレームの間隔と、シーングラフの変更量(Canvasへの更新量)の関係で、Prismのレンダリングスピードに見合わない速度でシーングラフの内容を変更しているためではないかと思います。

###描画完了後一定時間待つことは可能か?

本件の要点は処理落ちなので「【描画処理が終わった後】」の正確な意味は

(1)JavaFXアプリケーションスレッド上でCanvasへの描画をやり終える
(2)次に発生するパルスでPrismレンダー・スレッドが(1)の変更をレンダリングし終える

の(2)が終わった後ということになると思います。しかしながら(2)がいつかを意識するのはJavaFXの設計意図(※1)から離れてしまう考え方であるように思え、多分できない(あるいはすべきでない)ことではないかと思います。

###では処理落ちに対する対処は?

アニメーションをする際にはフレームレートで決まる時間間隔(最大60fps)で「シーングラフの更新方法をどの方式とするか」「その方法によるレンダリング負荷がどの程度なのか」を踏まえた上で方式を設計する必要があるのではないでしょうか。

本件は多分四角形の移動アニメーションだと思いますが、Canvasへ描画する他にjavafx.scene.shape.Polygonなどを用いても実現できると思います。性能的にどの方式が有利かは実際に試すと見えてくる気がします。また、特定の方式でフレームレートの限界がどの程度になるかを見極めることも重要と思います。そのあたりが解になると思います。


補足:

※1: JavaFXの設計意図?

昔のAWT(swing)アプリケーションでは一応「再描画」を意識した設計をしていました。一方JavaFXアプリケーションはシーングラフを変更することが主たる関心事となり、再描画がどう動くかはPrismにおまかせで基本的に意識しないと思います。再描画についてのAPIも見当たりません。多分GUI処理の最適化がアプリケーションプログラマーに意識できるほど単純には行えないためそうしているのだと思います。

投稿2017/10/27 04:22

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問