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

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

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

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

Java

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

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

例外処理

例外処理(Exception handling)とは、プログラム実行中に異常が発生した場合、通常フローから外れ、例外として別の処理を行うようにデザインされたプログラミング言語構造です。

Q&A

解決済

3回答

2141閲覧

マルチスレッドを用いた際の複数アニメーション描画・JavaFX(Exception in thread、duplicate children added)

magutyan0814

総合スコア2

JavaFX

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

Java

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

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

例外処理

例外処理(Exception handling)とは、プログラム実行中に異常が発生した場合、通常フローから外れ、例外として別の処理を行うようにデザインされたプログラミング言語構造です。

1グッド

0クリップ

投稿2022/01/08 11:57

編集2022/01/09 03:27

前提・実現したいこと

SceneBuilderとJavaFXで、4つの四角から円が流れていくアニメーションを作成しようとしています。
マルチスレッド機構を必ず使い、制作したいです。

発生している問題・エラーメッセージ

スレッドを2つ以上同時にstart()させると以下のようなエラーメッセージが出てきます。
また、表示されるウインドウにはアニメーションをつけている円以外(各GUI要素・四角)は表示されます。円は一つも表示されません。
duplicate children added: から、複数の図形を描画する際のgetChildren().add(machine)で追加できないような問題が起きていると思うのですが、解決方法がわかりません。

Exception in thread "Thread-4" Exception in thread "Thread-3" java.lang.IllegalArgumentException: Children: duplicate children added: parent = BorderPane@61af5816 at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:561) at javafx.base/com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:205) at application.Material.run(Material.java:55) java.lang.IllegalArgumentException: Children: duplicate children added: parent = BorderPane@61af5816 at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:561) at javafx.base/com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:205) at application.Material.run(Material.java:55)

該当のソースコード

Mainクラス、Materialクラス(アニメーションを持つ円)、Machineクラス(通過点の四角)でできています。
MaterialクラスにThreadクラスを継承させ、run()のオーバーライドでアニメーションを設定しPlay()させています。
エラー発生部分はMainクラス内のmatR.start();matB.start();の辺りと考えられます。

Java

1package application; 2import java.net.URL; 3import javafx.application.Application; 4import javafx.fxml.FXMLLoader; 5import javafx.scene.Scene; 6import javafx.scene.layout.Pane; 7import javafx.stage.Stage; 8import javafx.scene.shape.*; 9import javafx.scene.paint.*; 10 11 12public class Main extends Application { 13 14 // @FXML 15 // private Circle circle1; 16 17 public static void main(String[] args) { 18 launch(args); 19 } 20 21 @Override 22 public void start(Stage primaryStage) throws Exception { 23 //Pane setup 24 URL location = getClass().getResource( "Main.fxml" ); 25 FXMLLoader fxmlLoader = new FXMLLoader( location ); 26 Pane root = (Pane) fxmlLoader.load(); 27 final int width = 600; 28 final int height = 400; 29 30 //path setup 31 final int startRedX = 100; 32 final int startRedY = 100; 33 final int moveWidth = 350; 34 final int moveHeight = 200; 35 36 Path pathRB = new Path(); 37 pathRB.getElements().add(new MoveTo(startRedX, startRedY)); 38 pathRB.getElements().add(new LineTo(startRedX + moveWidth, startRedY)); 39 Path pathBG = new Path(); 40 pathBG.getElements().add(new MoveTo(startRedX + moveWidth, startRedY)); 41 pathBG.getElements().add(new LineTo(startRedX + moveWidth, startRedY + moveHeight)); 42 Path pathGY = new Path(); 43 pathGY.getElements().add(new MoveTo(startRedX + moveWidth, startRedY + moveHeight)); 44 pathGY.getElements().add(new LineTo(startRedX, startRedY + moveHeight)); 45 Path pathYR = new Path(); 46 pathYR.getElements().add(new MoveTo(startRedX, startRedY + moveHeight)); 47 pathYR.getElements().add(new LineTo(startRedX, startRedY)); 48 49 //machine setup 50 Machine machineRed = new Machine(); 51 Machine machineBlue = new Machine(); 52 Machine machineGreen = new Machine(); 53 Machine machineYellow = new Machine(); 54 55 machineRed.set(root,Color.RED,80,70); 56 machineBlue.set(root,Color.BLUE,440,70); 57 machineGreen.set(root,Color.GREEN,440,280); 58 machineYellow.set(root,Color.YELLOW,80,280); 59 60 machineRed.view(); 61 machineBlue.view(); 62 machineGreen.view(); 63 machineYellow.view(); 64 65 //material setup 66 //threads 67 Material matR = new Material(); 68 Material matB = new Material(); 69 Material matG = new Material(); 70 Material matY = new Material(); 71 72 matR.set(root, pathRB); matR.colorSet(Color.RED); 73 matB.set(root, pathBG); matB.colorSet(Color.BLUE); 74 matG.set(root, pathGY); matG.colorSet(Color.GREEN); 75 matY.set(root, pathYR); matY.colorSet(Color.YELLOW); 76 77 // MaterialThread matRthread = new MaterialThread(matR); 78 // MaterialThread matBthread = new MaterialThread(matB); 79 80 81 matR.start();//エラー発生? 82 matB.start(); 83 // matG.start(); 84 // matY.start(); 85 // matR.move(); 86 // matB.move(); 87 88 // matRthread.start(); 89 // matBthread.start(); 90 91 primaryStage.setScene( new Scene( root , width , height ) ); 92 primaryStage.show(); 93 } 94} 95

Java

1package application; 2import javafx.scene.shape.*; 3import javafx.animation.*; 4import javafx.util.Duration; 5import javafx.scene.layout.Pane; 6import javafx.animation.PathTransition; 7import javafx.scene.paint.*; 8 9 10 11public class Material extends Thread{ 12 protected Circle circle = new Circle(10); 13 protected Pane root; 14 protected Path path; 15 protected PathTransition animation; 16 protected Color color; 17 18 public void set(Pane root, Path path /*,double x, double y*/){ 19 this.root = root; 20 this.path = path; 21 } 22 23 public void colorSet(Color color){ 24 this.color = color; 25 circle.setFill(color); 26 } 27 28 public void move(){ 29 animation = new PathTransition(); 30 animation.setNode(circle); 31 animation.setDuration( Duration.seconds( 4 ) ); 32 animation.setPath( path ); 33 animation.setInterpolator( Interpolator.LINEAR ); 34 animation.setAutoReverse(false); 35 animation.setCycleCount(0); 36 37 root.getChildren().add(circle); 38 animation.play(); 39 } 40 41 @Override 42 public void run(){ 43 this.animation = new PathTransition(); 44 this.animation.setNode(this.circle); 45 this.animation.setDuration( Duration.seconds( 4 ) ); 46 this.animation.setPath( this.path ); 47 this.animation.setInterpolator( Interpolator.LINEAR ); 48 this.animation.setAutoReverse(false); 49 this.animation.setCycleCount(0); 50 this.root.getChildren().add(circle); 51 this.animation.play(); 52 } 53} 54// class MaterialThread extends Thread{ 55// private Material material; 56// public MaterialThread(Material material){ 57// this.material = material; 58// } 59// @Override 60// public void run(){ 61// this.material.move(); 62// } 63// }

Java

1package application; 2import javafx.scene.shape.*; 3import javafx.scene.layout.Pane; 4import javafx.scene.paint.*; 5public class Machine{ 6 7 protected int width = 80; 8 protected int height = 60; 9 protected int x,y; 10 protected Rectangle machine = new Rectangle(x,y,width,height); 11 protected Pane root; 12 protected Color color; 13 14 protected Thread t; 15 16 public void set(Pane root, Color color, int x, int y){ 17 this.x = x; 18 this.y = y; 19 this.machine = new Rectangle(x,y,width,height); 20 this.root = root; 21 this.color = color; 22 } 23 24 public void view(){ 25 machine.setFill(color); 26 root.getChildren().add(machine); 27 } 28}

追記

FXML

1<?xml version="1.0" encoding="UTF-8"?> 2 3<?import javafx.scene.control.Button?> 4<?import javafx.scene.control.Slider?> 5<?import javafx.scene.layout.AnchorPane?> 6<?import javafx.scene.layout.BorderPane?> 7<?import javafx.scene.layout.HBox?> 8 9<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Main"> 10 <top> 11 <HBox prefHeight="15.0" prefWidth="600.0" BorderPane.alignment="CENTER"> 12 <children> 13 <Button mnemonicParsing="false" text="Start" /> 14 <Button mnemonicParsing="false" text="Stop" /> 15 </children> 16 </HBox> 17 </top> 18 <bottom> 19 <Slider BorderPane.alignment="CENTER" /> 20 </bottom> 21 <center> 22 <AnchorPane prefHeight="331.0" prefWidth="600.0" BorderPane.alignment="CENTER" /> 23 </center> 24</BorderPane> 25

試したこと

・マルチスレッド機構を用いずにMaterialクラスにrun()と同じ内容のメソッドを作成したうえでそのメソッドを同じように複数呼び出すと同時にアニメーション描画することが出来ました。
・調べている中でGroupを用いると直りそうな気がしましたが、まだよくわかっていなのできちんとしたアドバイスをいただきたいです。

追記
先程、Mainクラス内で複数のstart()の間にthread.sleep(..)を挟んで実行するとうまくいきました。これがなぜなのかもわからず、、皆様の知恵を貸してくだされば大変助かります。よろしくお願い致します。

補足情報(FW/ツールのバージョンなど)

JavaFX Scene Builder 17.0.0

解決後の追記

皆さんのご協力で何とか解決いたしました!
ベストアンサーですが、追記依頼のコメント内でご指摘頂いた皆さんにも宛てるつもりでshiketaさんの回答につけさせていただきます。
本当にありがとうございました!

TN8001👍を押しています

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

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

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

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

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

TN8001

2022/01/08 15:37

普通はUIスレッド以外からUIをいじると IllegalStateException: Not on FX application thread が出るんですが確かに IllegalArgumentException: Children: duplicate children added が出ますね。。。 sleepを入れると動いちゃうほうがイレギュラーだと思います^^; とはいえ、これThreadの意味全くなくないですか? Materialのコンストラクタでplayまでしちゃっても、見た目?は何も変わらないと思うのですが。
magutyan0814

2022/01/08 15:48

コメントありがとうございます! そうなんですね、、どのスレッドからUIをいじるかでもエラーが出るのですね。勉強になります。 Threadですが、実は学校課題の条件でマルチスレッド機構を必ず使わなければならず、初期開発段階ですがどんな感じだろうと思って使ってみたらこのエラーにぶつかった形です。最終的にスレッドが使えないと困るため質問させて頂きました。 仰る通り、materialコンストラクタ内で全て済ませてもいいのです。(実際動きました)ですが、この後図形の四角に丸が当たったら丸をその色に塗り替えて別pathでアニメーションplayというようにまた開発を進めていく予定のため、threadを試したいのもありこのように作成しました。
TN8001

2022/01/08 16:23

なるほどこの後もあるわけですね。 少なくとも add(circle) はコンストラクタに移せるはずです。 それで duplicate children added はなくなると思います。 が、今度は play でエラーが出ると思います(手元ではArrayIndexOutOfBoundsExceptionが出たりしました) Platform.runLater(() -> { animation.play(); }); のようにUIスレッドに回せばエラーは出なくなりました(playがだめなのか作成もダメなのかは未確認)
jimbe

2022/01/08 17:01

> Threadですが、実は学校課題の条件でマルチスレッド機構を必ず使わなければならず マルチスレッドと動作結果(表示)以外に、課題として何が条件として決まっているのでしょうか。
magutyan0814

2022/01/09 03:21 編集

@TN8001 さん ご指摘ありがとうございます。結論から言うと確実に動くようになりました。本当にありがとうございます。 回答のshiketaさんの意見も併せて直してみたところ、IllegalArgumentExceptionのエラーはなくなりました。しかしやはり、TN8001さんと同じようにArrayIndexOutOfBoundsExceptionが出てしまっていました。(時々エラーが出ずに動いたり、全く動かなかったりします。そこの理由は本当にわかりません。) そこで、教えて頂いた Platform.runLater(() -> { animation.play(); }); をrun()内play()の代わりに使うと百発百中で動くようになりました!初めのご指摘通り別スレッドからGUIをいじるときに出るエラー文はIllegalStateExceptionらしくエラー文が食い違っていますが、別スレッドからアニメーションを動かそうとしていたのは一因だったのかもしれません。 ArrayIndexOutOfBounceは何の配列に対して出ていたか本当にわかりませんが、ひとまず動いて一安心できました。本当にありがとうございました。 もし何かこのArrayIndexOutOfBounceに関してなんとなく原因がわかるようでしたら、教えて頂けると助かります。ありがとうございました。
magutyan0814

2022/01/09 03:16

@jimbi さん 他には条件はありません!マルチスレッド同士で同期機構をつけると尚良い、という風に言われています。
jimbe

2022/01/09 04:37

> マルチスレッド同士で同期機構をつけると尚良い マルチスレッドにすることと同期機構をつけることは同じではありませんが、マルチスレッドだけで良いのでしょうか。
magutyan0814

2022/01/09 05:00

@jimbe さん 今はまだ初期開発段階で、当たり判定などある程度のところまで作ってから同期機構をつけようと思っています。 もしかしたらこのままでは同期機構で問題が出そうなところ等ありますかね、、?
jimbe

2022/01/09 05:28 編集

同期で問題といいますか、そもそも先にコメントで書かれていますように、現状スレッドの意味がありません。その上で同期させるといっても、結局コードに同期のコードが書いてあるだけでプログラムとしては意味が無いものとなり得ます。 課題が何をさせようとしているのか、何の為にスレッドを使い、何の為に同期が必要なのかをはっきりさせないと、ただ長いだけのプログラムになります。 例えば、円を移動させるのに PathTransition を使っていますが、これを自作で行うとしたらどうでしょう。 何を同期させるか(スレッドそのものか、フィールドアクセスか)ははっきりしませんが、マルチスレッドの意味は成立すると思います。
magutyan0814

2022/01/09 06:13 編集

@jimbe さん わざわざアドバイスありがとうございます泣 助かります! 私の制作テーマとして、工場生産ラインをアニメーションで再現するというものに決めてこのコードを書いています。ですので、円の移動に手をかけるというより、例えば生産ラインの処理機械に見立てた四角形が円を受け取ったとき、その四角形を何らかの処理をしているような色に差し替え、処理が終わったら元の色に戻り次の四角形へ色が変わった円をアニメーションで送り出す、というような形にしたいです。 そこで各四角形が差し変えられる色が異なっていたり、各四角形で円を受け取るタイミングが違う場合にも対応できるなどスレッドを使えば色々なことができるかなぁと思っております。(まだ作っていない者の想像ですが)また、同期処理はオプションで、必須というわけではありません。 この質問はマルチスレッド機構を使ってみようということで簡単に書いたコードです。せっかくお答えいただいたのですが、ばっさり変わってしまう可能性もあります。しかし解決しておかないと困ると思いまして質問させていただきました。
jimbe

2022/01/09 09:04

コードの背景が大分見えてきました。ご説明ありがとうございます。 ご質問自体とは離れてしまいますが、「工場生産ラインをアニメーションで再現」であれば、円の移動アニメーションの"開始"をスレッドにするのは「マルチスレッド機構を使ってみよう」という程度の意味とは言え、ちょっと意味が無かったと思います。 java はオブジェクト指向であり、オブジェクトを基本単位として処理を記述します。そのメタファーとして例えば「車」を「エンジン」「タイヤ」等の組み合わせとするなど、実際のモノを用いたりすることも良くあります。 その延長として、工場生産ラインを材料・処理機械・コンベア等のオブジェクトとして表現(表示処理も含む)し、処理機械をコンベアの端のとして接続、独自に動作するモノである処理機械とコンベアをスレッドとして動作させ、材料を最初のコンベアの端に載せるとコンベアによって運ばれて処理されていく・・・といった形が出来ます。 もちろん課題は magutyan0814 さんのお考えで達成されるものですのでこれは雑談レベルの私の案ですが、スレッドを投げっぱなしのアニメーションの開始だけでは、スレッドで難しい部分、動作中のデータの入出力・同期等に全く触れませんので、もう少し表現構造にあった形でのお試しをされたほうが良かったのではと感じました。 例えばコンベア(常時動いており、材料を乗せると一定速度で逆側に移動させていき、端に付いたら予め登録(接続)されている別のオブジェクト(=スレッド)に材料を渡す。材料が移動中でも次の材料を乗せられる)をスレッドで(動作・移動の表現・アニメーションも含めて)どう書くか、です。
magutyan0814

2022/01/09 09:51

お返事ありがとうございます。 確かに、もう少し制作テーマを考慮してマルチスレッドを試してみればよかったです。 コンベアに流れ着いた材料を別の機械に流す作業をスレッド化するということですね!並列作業になっていますし、学べる点が多くありそうです。チャレンジしてます。 今は、少し考えてMachineクラスをスレッドクラスのサブクラスにし、Cubbyholeなど使って材料が流れ着いた/ついていないの状態をなんとかMachineインスタンス間で共有(接続)できないかなと試行錯誤しているところです。流れ着いた→色を変えて次に流す の認識部分を作ろうとしています。 多くのアドバイス本当にありがとうございました。
guest

回答3

0

JavaFX で無く Swing ですが、流れるものを作って見ました。
(結構テキトウです。)

java

1package teratail_java.q377216; 2 3import java.awt.*; 4import java.awt.event.ActionEvent; 5import java.awt.event.ActionListener; 6import java.util.*; 7 8import javax.swing.*; 9 10public class Q377216 extends JFrame { 11 public static void main(String[] args) { 12 new Q377216().setVisible(true); 13 } 14 15 Q377216() { 16 super("Q377216"); 17 setDefaultCloseOperation(EXIT_ON_CLOSE); 18 setSize(600, 480); 19 setLocationRelativeTo(null); 20 21 getContentPane().setLayout(null); 22 23 Conveyor conveyor1 = new Conveyor(Conveyor.Anchor.SOUTH, Conveyor.FlowDir.LeftToRight); 24 conveyor1.setBounds(100, 300, 200, 50); 25 add(conveyor1); 26 27 Machine machine = new Machine(); 28 machine.setBounds(300, 280, 100, 100); 29 add(machine); 30 31 Conveyor conveyor2 = new Conveyor(Conveyor.Anchor.EAST, Conveyor.FlowDir.BottomToTop); 32 conveyor2.setBounds(320, 80, 50, 200); 33 add(conveyor2); 34 35 //コンベア1 から マシン を通って コンベア2 へ繋げる 36 conveyor1.setNext(machine).setNext(conveyor2); 37 38 JButton button1 = new JButton("投入"); 39 button1.addActionListener(new ActionListener() { 40 private Random random = new Random(); 41 private Color[] colors = new Color[]{ Color.PINK, Color.RED, Color.CYAN, Color.WHITE, Color.GREEN, Color.YELLOW, Color.ORANGE }; 42 @Override 43 public void actionPerformed(ActionEvent e) { 44 conveyor1.set(new Material(colors[random.nextInt(colors.length)], random.nextInt(3))); 45 } 46 }); 47 button1.setBounds(50, 50, 100, 30); 48 add(button1); 49 50 conveyor1.start(); 51 machine.start(); 52 conveyor2.start(); 53 } 54} 55 56class Material { 57 private static final int WIDTH = 20; 58 private static final int HEIGHT = 20; 59 private int x = Integer.MIN_VALUE; 60 private int y = Integer.MIN_VALUE; 61 private Color color; 62 private int count; 63 Material(Color color, int count) { 64 this.color = color; 65 this.count = count; 66 } 67 void setX(int x) { this.x = x; } 68 void setY(int y) { this.y = y; } 69 void setColor(Color color) { this.color = color; } 70 void setCount(int count) { this.count = count; } 71 72 int getX() { return x; } 73 int getY() { return y; } 74 int getWidth() { return WIDTH; } 75 int getHeight() { return HEIGHT; } 76 int getCount() { return count; } 77 78 Rectangle getRectangle() { 79 return new Rectangle(x, y, WIDTH, HEIGHT); 80 } 81 82 void paint(Graphics g) { 83 g.setColor(color); 84 g.fillOval(x, y, WIDTH, HEIGHT); 85 g.setColor(Color.BLACK); 86 g.drawOval(x, y, WIDTH, HEIGHT); 87 g.drawString(""+count, x+7, y+15); 88 } 89} 90 91interface InGate { 92 void set(Material m); 93} 94 95interface Repeater extends InGate { 96 Repeater setNext(Repeater next); 97} 98 99class Machine extends JPanel implements Repeater { 100 private ArrayList<Material> mlist = new ArrayList<Material>(); 101 private Thread thread; 102 private InGate nextGate; 103 104 Machine() { 105 super(null); 106 setBackground(Color.WHITE); 107 } 108 109 @Override 110 public Repeater setNext(Repeater next) { 111 this.nextGate = next; 112 return next; 113 } 114 115 @Override 116 public void set(Material m) { 117 synchronized(mlist) { 118 mlist.add(m); 119 mlist.notify(); 120 } 121 } 122 123 void start() { 124 stop(); //念の為 125 thread = new Thread(new Runnable() { 126 @Override 127 public void run() { 128 try { 129 while(!Thread.currentThread().isInterrupted()) { 130 Material m; 131 synchronized(mlist) { 132 while(mlist.isEmpty()) mlist.wait(); 133 m = mlist.remove(0); 134 } 135 m = work(m); 136 if(nextGate != null) nextGate.set(m); 137 } 138 } catch(InterruptedException ignore) { 139 } 140 } 141 }); 142 thread.start(); 143 } 144 void stop() { 145 if(thread != null && thread.isAlive()) { 146 thread.interrupt(); 147 try { 148 thread.join(); 149 } catch(InterruptedException e) { 150 e.printStackTrace(); 151 } 152 } 153 thread = null; 154 } 155 156 private Material work(Material m) { 157 try { 158 m.setCount(m.getCount() + 1); 159 Thread.sleep(2000); //処理に2秒掛かることにする 160 } catch(InterruptedException e) { 161 e.printStackTrace(); 162 } 163 return m; 164 } 165 166 @Override 167 protected void paintComponent(Graphics g) { 168 super.paintComponent(g); 169 170 g.setColor(Color.BLACK); 171 g.drawRect(0, 0, getWidth()-1, getHeight()-1); 172 } 173} 174 175class Conveyor extends JPanel implements Repeater { 176 private enum Axis { X_AXIS, Y_AXIS } //縦横 177 enum FlowDir { //何処から何処へ流れるか 178 BottomToTop(Axis.Y_AXIS, 0,-1), LeftToRight(Axis.X_AXIS, 1, 0), 179 TopToBottom(Axis.Y_AXIS, 0, 1), RightToLeft(Axis.X_AXIS,-1, 0); 180 final Axis axis; 181 final int sx, sy; 182 FlowDir(Axis axis, int sx, int sy) { 183 this.axis = axis; 184 this.sx = sx; 185 this.sy = sy; 186 } 187 int moveX(int x, int dx) { return x+(dx*sx); } 188 int moveY(int y, int dy) { return y+(dy*sy); } 189 } 190 enum Anchor { //どの辺に沿って描画するか 191 NORTH(Axis.X_AXIS), EAST(Axis.Y_AXIS), 192 SOUTH(Axis.X_AXIS), WEST(Axis.Y_AXIS); 193 final Axis axis; 194 Anchor(Axis axis) { 195 this.axis = axis; 196 } 197 } 198 private static final int BOARD_SIZE = 5; 199 private static final int BOARD_THICK = 2; 200 private static final int TRANSPORT_STEP = 2; 201 202 private Anchor anchor = Anchor.SOUTH; 203 private FlowDir flowdir = FlowDir.LeftToRight; 204 private ArrayList<Material> mlist = new ArrayList<Material>(); 205 private Thread thread; 206 private int offsetX = 0, offsetY = 0; 207 private InGate nextGate; 208 209 Conveyor(Anchor anchor, FlowDir flowdir) { 210 super(null); 211 if(anchor.axis != flowdir.axis) throw new IllegalArgumentException(); 212 this.anchor = anchor; 213 this.flowdir = flowdir; 214 setBackground(Color.WHITE); 215 } 216 217 @Override 218 public Repeater setNext(Repeater next) { 219 this.nextGate = next; 220 return next; 221 } 222 223 @Override 224 public void set(Material m) { 225 setPosition(m); 226 synchronized(mlist) { 227 mlist.add(m); 228 } 229 } 230 231 private void setPosition(Material m) { 232 if(flowdir == FlowDir.LeftToRight) { 233 m.setX(- m.getWidth()); 234 } else if(flowdir == FlowDir.RightToLeft) { 235 m.setX(getWidth()); 236 } else if(anchor == Anchor.WEST) { 237 m.setX(BOARD_THICK); 238 } else { //anchor == Anchor.EAST 239 m.setX(getWidth() - BOARD_THICK - m.getWidth()); 240 } 241 if(flowdir == FlowDir.TopToBottom) { 242 m.setY(- m.getHeight()); 243 } else if(flowdir == FlowDir.BottomToTop) { 244 m.setY(getHeight()); 245 } else if(anchor == Anchor.NORTH) { 246 m.setY(BOARD_THICK); 247 } else { //anchor == Anchor.SOUTH 248 m.setY(getHeight() - BOARD_THICK - m.getHeight()); 249 } 250 } 251 252 private void move() { 253 offsetX = flowdir.moveX(offsetX, TRANSPORT_STEP); 254 if(offsetX <= -BOARD_SIZE) offsetX += BOARD_SIZE*2; 255 else if(offsetX >= BOARD_SIZE) offsetX -= BOARD_SIZE*2; 256 257 offsetY = flowdir.moveY(offsetY, TRANSPORT_STEP); 258 if(offsetY <= -BOARD_SIZE) offsetY += BOARD_SIZE*2; 259 else if(offsetY >= BOARD_SIZE) offsetY -= BOARD_SIZE*2; 260 261 synchronized(mlist) { 262 for(Iterator<Material> ite=mlist.iterator(); ite.hasNext(); ) { 263 Material m = ite.next(); 264 m.setX(flowdir.moveX(m.getX(), TRANSPORT_STEP)); 265 m.setY(flowdir.moveY(m.getY(), TRANSPORT_STEP)); 266 267 if(!getRectangle().intersects(m.getRectangle())) { 268 ite.remove(); 269 if(nextGate != null) { nextGate.set(m);} 270 } 271 } 272 } 273 } 274 275 private Rectangle getRectangle() { 276 return new Rectangle(0, 0, getWidth(), getHeight()); 277 } 278 279 void start() { 280 stop(); //念の為 281 thread = new Thread(new Runnable() { 282 @Override 283 public void run() { 284 try { 285 repaint(); 286 while(!Thread.currentThread().isInterrupted()) { 287 Thread.sleep(100); 288 move(); 289 repaint(); 290 } 291 } catch(InterruptedException ignore) { 292 } 293 } 294 }); 295 thread.start(); 296 } 297 void stop() { 298 if(thread != null && thread.isAlive()) { 299 thread.interrupt(); 300 try { 301 thread.join(); 302 } catch(InterruptedException e) { 303 e.printStackTrace(); 304 } 305 } 306 thread = null; 307 } 308 309 @Override 310 protected void paintComponent(Graphics g) { 311 super.paintComponent(g); 312 313 g.setColor(Color.BLACK); 314 if(anchor.axis == Axis.X_AXIS) { 315 int y = anchor == Anchor.NORTH ? 0 : getHeight() - BOARD_THICK; 316 for(int x=offsetX; x<getWidth(); x+=BOARD_SIZE*2) { 317 g.fillRect(x, y, BOARD_SIZE, BOARD_THICK); 318 } 319 } else { 320 int x = anchor == Anchor.WEST ? 0 : getWidth() - BOARD_THICK; 321 for(int y=offsetY; y<getHeight(); y+=BOARD_SIZE*2) { 322 g.fillRect(x, y, BOARD_THICK, BOARD_SIZE); 323 } 324 } 325 326 synchronized(mlist) { 327 for(Material m : mlist) m.paint(g); 328 } 329 } 330}

投稿2022/01/09 14:24

編集2022/01/09 14:25
jimbe

総合スコア13209

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

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

magutyan0814

2022/01/10 06:36

なんと、、!!すごいですね!!本当にありがとうございます。私はまだ勉強中ですが、それくらいぱっと用意できるプログラマーになりたいです。 お返事を見てから頂いたコードとにらめっこしており、お返事が遅くなってしまいました。Java初学者の私では学べる点が本当に多くありました。恐縮なのですが、わからない点について解説頂いてもよろしいでしょうか、、? ・interfaceのRepeater/Ingate の詳しい役割・動作 ・Conveyorクラス内のpaintComponentの動作 調べながら解読してみたのですが、以上の点がどうしてもよくわからず、、全体の構造と関連付けてご説明頂けると大変助かります。どうかよろしくお願い致します。
jimbe

2022/01/10 08:31 編集

InGate は、実装するクラスが void set(Material m); メソッドを実装していることを示し。それはこのプログラムとしては "Material を受け取ることが出来るクラス(オブジェクト)" であることを示します。 実装しているのは Machine と Conveyor で、現状 "コンベア→機械→コンベア" と繋げていますが、"コンベア→コンベア" や、機械を並べて直接"機械→機械"ということも出来るということであり、別に何か工場要素として作成した場合も InGate を実装することでコンベアや機械からマテリアルを受け取ることが出来るということです。 (ただし、コンベア→コンベアは表示の整合性が出来ていません。) Repeater は実装から生まれたもので、 //コンベア1 から マシン を通って コンベア2 へ繋げる conveyor1.setNext(machine).setNext(conveyor2); という繋げた書き方(ストリーム風)をするためのものです。 プログラムとしては conveyor1.setNext(machine); machine.setNext(conveyor2); でも良いんですが、繋がって書いたほうが「機械が繋がっている風」に見える…ということで。 (void setNext(InGate next); もあったほうが良さそうですが。) コンベアの paintComponent は・・・描画しているだけですが ^^; Swing はコンポーネントを paintComponent で g を使って画面に描画します。Machine と Conveyor は Swing のコンポーネントである JPanel を継承しているため、 paintComponent で自身を描画しています。 前半がコンベアの"点々"を書く部分、後半(synchronized 内) がコンベアに乗っている材料を書く部分です。 (Material は Swing のコンポーネントにしていないため、 コンペアが描画しています。というか、コンペアが描画するので Swing のコンポーネントにする必要が無い、というべきでしょうか。) 前半がごちゃごちゃしているのは、点々をコンベアの描画領域内でどこに書くか(Anchor: 東(右)西(左)南(下)北(上))をコンストラクトパラメータで指定できるようにしたため、それにあわせて x,y 座標を計算しつつ点々を書いているためです。
magutyan0814

2022/01/10 10:37 編集

ご回答ありがとうございます! なるほど、機械やコンベアをつなげるためのインターフェイスなのですね。ありがとうございます。また、インジゲータをつけてみたサンプルも大変ありがたいです。調べながら読んでみます。 すみません、読んでいるうちにもお聞きしたいことができたのですが、 ・conveyorクラスのmove()メソッドの動作(特にoffsetX/Yの役割、当たり判定(if文内?)の動作) ・conveyorクラスのstart()メソッドの動作(repaintで何を行っているのかがよくわかりません、座標をずらして再描画し、アニメーションとしているのでしょうか?) についてお聞きしたいです。何でも聞くような形になってしまい本当に申し訳ないです。どうかよろしくお願いいたします。
jimbe

2022/01/10 11:14 編集

move メソッドは、コンベア(と乗っている材料)が一単位時間当たりどう動くかを計算しています。move メソッドで各座標 (offsetX,offsetY 及び乗っている材料の座標) を求めておき、 paintComponent でその座標に表示、となります。 offsetX と offsetY は(名前がちょっとダメですけど) コンベアの "点々" の描画開始位置です。 先のコメントで書きましたように "点々" を書く位置が4方向のいずれかであり、また "点々" は具体的には "黒い部分" と "黒くない部分" の繰り返しで、描画するのは "黒い部分" だけですので、その「 4 方向のうち描画する方向の、黒い部分の開始位置」を求めています。 if 文で書いてあるのは、例えば"黒い部分"が移動していき、次の"黒くない部分"が一定幅を超えると次の"黒い部分"が出てくることで"点々"になりますので、その次の部分に位置を戻しています。 start メソッドはスレッドの開始です。 repaint は Swing に対して"このコンポーネントを都合の良い時に再描画させてください (=paintComponent を呼び出してください)" と要求するメソッドで、Swing のコンポーネントは皆持っています。 つまり、 start によって開始されるスレッドは、 100ms待ち、move で移動位置を計算し、repaint で再描画を要求し、また100ms待ち・・・を繰り返し、これでアニメーションしています。 私のコードに対するご質問であれば、出来る限りお答えするつもりです。私のコードを一番分かっているのは私のはずですので。 まぁ、余り時間を掛けたコードではありませんので、先のインターフェース等は何かの拍子にマズイことが分かるかもしれませんし、スレッドも動き続ける前提にしてますので、stop して start したらどうなるかまで考えていない等、結局習作というかサンプルというか、そんなレベルでしかありませんが ^^;;;
jimbe

2022/01/10 11:23

move メソッドの後半の材料の座標の計算で、乗っていた材料がコンベアから出た場合の判定を Rectangle の intersects メソッドで行っています。 intersects メソッドは良く当たり判定等で使われ、「交差するかを返す」と表現されたりしますが、実際に返すのは「二つの Rectangle に共通部分が有るか」です。 この場合はコンベアの描画領域内に材料の描画範囲が(少しでも)入っているかをチェックし、入っていなければ端に到達として自身から削除(ite.remove)し、次のゲートに繋がっていれば(nextGate!=null) そのゲートに入れています(nextGate.set(m))。
magutyan0814

2022/01/19 05:45 編集

お返事大変遅れました。こちらのコードを参考にさせていただきながら、JavaFXに移行させなんとか作品を完成させることが出来ました。質問に答えて頂き、丸ぱくりではなくいろいろな工夫もできたので本当に良かったです。ありがとうございました。
guest

0

ベストアンサー

内容はまったく理解していませんが、TN8001さんの指摘、

少なくとも add(circle) はコンストラクタに移せるはずです。

もふくめて、わたしならこう書くかな、という案を。

ちなみに、何度もコピペするのがめんどうなので、インナークラスにしていますが、そこは如何様にでも。

java

1package application; 2 3import javafx.animation.Interpolator; 4import javafx.animation.PathTransition; 5import javafx.application.Application; 6import javafx.fxml.FXMLLoader; 7import javafx.scene.Scene; 8import javafx.scene.layout.Pane; 9import javafx.scene.paint.Color; 10import javafx.scene.shape.*; 11import javafx.stage.Stage; 12import javafx.util.Duration; 13 14import java.net.URL; 15 16// 17public class Main extends Application { 18 19 // @FXML 20 // private Circle circle1; 21 22 public static void main(String[] args) { 23 launch(args); 24 } 25 26 @Override 27 public void start(Stage primaryStage) throws Exception { 28 //Pane setup 29 final URL location = getClass().getResource("Main.fxml"); 30 final FXMLLoader fxmlLoader = new FXMLLoader(location); 31 final Pane root = (Pane) fxmlLoader.load(); 32 final int width = 600; 33 final int height = 400; 34 35 //path setup 36 final int startRedX = 100; 37 final int startRedY = 100; 38 final int moveWidth = 350; 39 final int moveHeight = 200; 40 41 final Path pathRB = new Path(); 42 pathRB.getElements().add(new MoveTo(startRedX, startRedY)); 43 pathRB.getElements().add(new LineTo(startRedX + moveWidth, startRedY)); 44 45 final Path pathBG = new Path(); 46 pathBG.getElements().add(new MoveTo(startRedX + moveWidth, startRedY)); 47 pathBG.getElements().add(new LineTo(startRedX + moveWidth, startRedY + moveHeight)); 48 49 final Path pathGY = new Path(); 50 pathGY.getElements().add(new MoveTo(startRedX + moveWidth, startRedY + moveHeight)); 51 pathGY.getElements().add(new LineTo(startRedX, startRedY + moveHeight)); 52 53 final Path pathYR = new Path(); 54 pathYR.getElements().add(new MoveTo(startRedX, startRedY + moveHeight)); 55 pathYR.getElements().add(new LineTo(startRedX, startRedY)); 56 57 //machine setup 58 final Machine machineRed = new Machine(root, Color.RED, 80, 70); 59 final Machine machineBlue = new Machine(root, Color.BLUE, 440, 70); 60 final Machine machineGreen = new Machine(root, Color.GREEN, 440, 280); 61 final Machine machineYellow = new Machine(root, Color.YELLOW, 80, 280); 62 63 // machineRed.set(root, Color.RED, 80, 70); 64 // machineBlue.set(root, Color.BLUE, 440, 70); 65 // machineGreen.set(root, Color.GREEN, 440, 280); 66 // machineYellow.set(root, Color.YELLOW, 80, 280); 67 68 machineRed.view(); 69 machineBlue.view(); 70 machineGreen.view(); 71 machineYellow.view(); 72 73 //material setup 74 //threads 75 final Material matR = new Material(root, pathRB); 76 final Material matB = new Material(root, pathBG); 77 final Material matG = new Material(root, pathGY); 78 final Material matY = new Material(root, pathYR); 79 80 // matR.set(root, pathRB); 81 matR.colorSet(Color.RED); 82 // matB.set(root, pathBG); 83 matB.colorSet(Color.BLUE); 84 // matG.set(root, pathGY); 85 matG.colorSet(Color.GREEN); 86 // matY.set(root, pathYR); 87 matY.colorSet(Color.YELLOW); 88 89 // MaterialThread matRthread = new MaterialThread(matR); 90 // MaterialThread matBthread = new MaterialThread(matB); 91 92 93 matR.start();//エラー発生? 94 matB.start(); 95 matG.start(); 96 matY.start(); 97 matR.move(); 98 matB.move(); 99 100 // matRthread.start(); 101 // matBthread.start(); 102 103 primaryStage.setScene(new Scene(root, width, height)); 104 primaryStage.show(); 105 } 106 107 // 108 static class Machine { 109 private static final int width = 80; 110 private static final int height = 60; 111 private final int x, y; 112 private final Rectangle machine; 113 private final Pane root; 114 private final Color color; 115 116 // private Thread t; 117 118 public Machine(Pane root, Color color, int x, int y) { 119 this.x = x; 120 this.y = y; 121 this.machine = new Rectangle(x, y, width, height); 122 this.root = root; 123 this.color = color; 124 } 125 126 public void view() { 127 machine.setFill(color); 128 root.getChildren().add(machine); 129 } 130 } 131 132 // 133 static class Material extends Thread { 134 private final Circle circle = new Circle(10); 135 private final Pane root; 136 private final Path path; 137 // private PathTransition animation; 138 private Color color; 139 140 public Material(Pane root, Path path /*,double x, double y*/) { 141 this.root = root; 142 this.path = path; 143 this.root.getChildren().add(circle); 144 } 145 146 public void colorSet(Color color) { 147 this.color = color; 148 circle.setFill(color); 149 } 150 151 public void move() { 152 final PathTransition animation = new PathTransition(); 153 animation.setNode(circle); 154 animation.setDuration(Duration.seconds(4)); 155 animation.setPath(path); 156 animation.setInterpolator(Interpolator.LINEAR); 157 animation.setAutoReverse(false); 158 animation.setCycleCount(0); 159 // root.getChildren().add(circle); 160 animation.play(); 161 } 162 163 @Override 164 public void run() { 165 final PathTransition animation = new PathTransition(); 166 animation.setNode(this.circle); 167 animation.setDuration(Duration.seconds(4)); 168 animation.setPath(this.path); 169 animation.setInterpolator(Interpolator.LINEAR); 170 animation.setAutoReverse(false); 171 animation.setCycleCount(0); 172 // this.root.getChildren().add(circle); 173 animation.play(); 174 } 175 } 176}

投稿2022/01/09 02:30

shiketa

総合スコア4061

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

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

magutyan0814

2022/01/09 03:30

再びの回答ありがとうございます。 頂いたコードとTN8001さんの指摘( Platform.runLater(() -> { animation.play(); }); })も併せて工夫し実行したところ確実に動くようになりました!本当にありがとうございました。
guest

0

すべてのソースが提示されていないので、再現のしようがない。なので、

java.lang.IllegalArgumentException: Children: duplicate children added: ...

を再現する例だけ提示しておく。"duplicate children added error"ボタンを押下すれば再現できる。

スレッドどうのこうの関係ない。たぶん。原因は、単に、すでにどこかに追加済みのNodeを別の親に追加しようとしているから。どうしても使いまわしたいのであれば、元の親から削除してから別の親に追加すればいい。

java

1import javafx.application.Application; 2import javafx.scene.Scene; 3import javafx.scene.control.Button; 4import javafx.scene.control.TextArea; 5import javafx.scene.layout.HBox; 6import javafx.scene.layout.VBox; 7import javafx.scene.paint.Color; 8import javafx.scene.shape.Rectangle; 9import javafx.stage.Stage; 10 11public class DupError { 12 public static void main(final String[] args) throws Exception { 13 Application.launch(App.class, args); 14 } 15 16 public static class App extends Application { 17 private Rectangle newReac() { 18 final Rectangle rect = new Rectangle(10, 10); 19 rect.setStyle("background: red"); 20 rect.setFill(new Color(Math.random(), Math.random(), Math.random(), 1.0)); 21 return rect; 22 } 23 24 @Override 25 public void start(final Stage stage) throws Exception { 26 final Rectangle hogeRect = newReac(); 27 28 final HBox hbox1 = new HBox(hogeRect); 29 final HBox hbox2 = new HBox(); 30 final TextArea text = new TextArea(); 31 32 final Button button1 = new Button("add new rect"); 33 button1.setOnAction(event -> hbox1.getChildren().add(newReac())); 34 35 final Button button2 = new Button("duplicate children added error"); 36 button2.setOnAction(event -> { 37 try { 38 hbox1.getChildren().add(hogeRect); 39 } catch (Exception e) { 40 text.setText(text.getText() + e + "\n"); 41 } 42 }); 43 44 final VBox vbox = new VBox(hbox1, button1, button2, hbox2, text); 45 46 stage.setScene(new Scene(vbox)); 47 stage.setWidth(800); 48 stage.setHeight(400); 49 stage.show(); 50 } 51 } 52}

投稿2022/01/08 14:17

編集2022/01/08 14:19
shiketa

総合スコア4061

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

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

magutyan0814

2022/01/08 14:24

コメントありがとうございます! ご指摘で、FXMLのソースを掲載し忘れていたことに気が付きました。急いで追記いたしました。 頂いた回答を基に自分でも試行してみます。もしソースコードを見て気づいたことがございましたらお返事くださると大変嬉しいです。よろしくお願い致します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問