JavaFX从另一个线程更新AnchorPane
所以我在这件事上有点纠结。我有一个非常基本的游戏,你可以用箭头键在网格上移动一艘船 我添加了另一个线程,其中有一个怪物可以自动漫游网格。我可以从print语句中看到线程正在运行,怪物正在移动,但是图像位置没有被更新JavaFX从另一个线程更新AnchorPane,java,javafx,Java,Javafx,所以我在这件事上有点纠结。我有一个非常基本的游戏,你可以用箭头键在网格上移动一艘船 我添加了另一个线程,其中有一个怪物可以自动漫游网格。我可以从print语句中看到线程正在运行,怪物正在移动,但是图像位置没有被更新 import javafx.collections.ObservableList; import javafx.scene.Node; import javafx.scene.image.Image; import javafx.scene.image.ImageView; impo
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.awt.Point;
public class Monster implements Runnable {
private Point currentPoint;
private OceanMap map;
public Monster(int x, int y) {
this.currentPoint = new Point(x, y);
this.map = OceanMap.getInstance();
}
public Point getLocation() {
System.out.println(this.currentPoint.toString());
return this.currentPoint;
}
private void setNewLocation(Point newLocation) {
this.currentPoint = newLocation;
}
private void setY(int newY) {
this.currentPoint.y = newY;
this.setNewLocation(new Point(this.currentPoint.x, this.currentPoint.y));
}
private void setX(int newX) {
this.currentPoint.x = newX;
this.setNewLocation(new Point(this.currentPoint.x, this.currentPoint.y));
System.out.println(this.currentPoint.toString());
}
// public void addToPane() {
// System.out.println("this is called");
// iv.setX(this.currentPoint.x + 1 * 50);
// iv.setY(this.currentPoint.y * 50);
// obsrvList.add(iv);
// }
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setX(this.currentPoint.x + 1);
}
}
}
我发现了一些类似的问题,有很多建议可以使用Platfrom.runLater
。但我不确定它是否符合我的具体情况,如果符合,如何实施
下面是怪物类正在做的事情,每秒将怪物向右移动一个空间。正如我提到的,每次调用setX()
时,我都会记录当前位置,这样我就可以看到位置正在更新
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.awt.Point;
public class Monster implements Runnable {
private Point currentPoint;
private OceanMap map;
public Monster(int x, int y) {
this.currentPoint = new Point(x, y);
this.map = OceanMap.getInstance();
}
public Point getLocation() {
System.out.println(this.currentPoint.toString());
return this.currentPoint;
}
private void setNewLocation(Point newLocation) {
this.currentPoint = newLocation;
}
private void setY(int newY) {
this.currentPoint.y = newY;
this.setNewLocation(new Point(this.currentPoint.x, this.currentPoint.y));
}
private void setX(int newX) {
this.currentPoint.x = newX;
this.setNewLocation(new Point(this.currentPoint.x, this.currentPoint.y));
System.out.println(this.currentPoint.toString());
}
// public void addToPane() {
// System.out.println("this is called");
// iv.setX(this.currentPoint.x + 1 * 50);
// iv.setY(this.currentPoint.y * 50);
// obsrvList.add(iv);
// }
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setX(this.currentPoint.x + 1);
}
}
}
这里是JavaFX线程
/* Monster resources */
private Image monsterImage = new Image(getClass().getResource("monster.png").toExternalForm(), 50, 50, true, true);
private ImageView monsterImageView1 = new ImageView(monsterImage);
private Monster monster1;
private Thread monsterThread;
@Override
public void start(Stage oceanStage) throws Exception {
root = new AnchorPane();
scene = new Scene(root, scale * xDimensions, scale * yDimensions);
oceanStage.setScene(scene);
oceanStage.setTitle("Ocean Explorer");
/* Draw Grid */
for (int x = 0; x < xDimensions; x++) {
for (int y = 0; y < yDimensions; y++) {
Rectangle rect = new Rectangle(x * scale, y * scale, scale, scale);
rect.setStroke(Color.BLACK);
rect.setFill(Color.PALETURQUOISE);
root.getChildren().add(rect);
}
}
oceanStage.show();
monsterThread = new Thread(monster1);
monsterThread.start();
Platform.runLater(() -> {
monsterImageView1.setX(monster1.getLocation().x * scale);
monsterImageView1.setY(monster1.getLocation().y * scale);
root.getChildren().add(monsterImageView1);
});
startSailing();
}
/*怪物资源*/
私有图像monsterImage=新图像(getClass().getResource(“monster.png”).toExternalForm(),50,50,true,true);
private ImageView monsterImageView1=新的ImageView(monsterImage);
私人怪物1;
私有线程;
@凌驾
public void start(阶段oceanStage)引发异常{
根=新的锚烷();
场景=新场景(根、比例*xDimensions、比例*yDimensions);
海洋舞台(场景);
oceanStage.setTitle(“海洋探险家”);
/*绘制网格*/
对于(int x=0;x{
monsterImageView1.setX(monster1.getLocation().x*比例);
monsterImageView1.setY(monster1.getLocation().y*比例);
root.getChildren().add(monsterImageView1);
});
startSailing();
}
如果需要的话,我可以提供更多的代码,这就是我目前认为相关的全部内容
同样,我的问题是,如何从另一个线程更新JavaFX线程的UI?当您在
Monster
内部更新currentPoint
时,此值永远不会传播到monsterImageView1
。您应该将currentPoint
转换为属性,然后绑定到该属性:
class Point {
final int x;
final int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Monster implements Runnable {
private ReadOnlyObjectWrapper<Point> location = new ReadOnlyObjectWrapper<>();
public Monster(int x, int y) {
setLocation(new Point(x, y));
}
public Point getLocation() {
return this.location.get();
}
private void setLocation(Point location) {
this.location.set(location);
}
public ReadOnlyProperty<Point> locationProperty() {
return this.location.getReadOnlyProperty();
}
private void setY(int newY) {
this.setLocation(new Point(this.getLocation().x, newY));
}
private void setX(int newX) {
this.setLocation(new Point(newX, this.getLocation().y));
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// run the update on the FX Application Thread for thread safety as well as to prevent errors in certain cases
Platform.runLater(() -> this.setX(this.getLocation().x + 1));
}
}
}
monsterThread = new Thread(monster1);
monsterThread.start();
monsterImageView1.xProperty().bind(Bindings.createIntegerBinding(() -> monster1.getLocation().x * scale, monster1.locationProperty()));
monsterImageView1.yProperty().bind(Bindings.createIntegerBinding(() -> monster1.getLocation().y * scale, monster1.locationProperty()));
root.getChildren().add(monsterImageView1);
当您在
Monster
内部更新currentPoint
时,此值永远不会传播到monsterImageView1
。您应该将currentPoint
转换为属性,然后绑定到该属性:
class Point {
final int x;
final int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Monster implements Runnable {
private ReadOnlyObjectWrapper<Point> location = new ReadOnlyObjectWrapper<>();
public Monster(int x, int y) {
setLocation(new Point(x, y));
}
public Point getLocation() {
return this.location.get();
}
private void setLocation(Point location) {
this.location.set(location);
}
public ReadOnlyProperty<Point> locationProperty() {
return this.location.getReadOnlyProperty();
}
private void setY(int newY) {
this.setLocation(new Point(this.getLocation().x, newY));
}
private void setX(int newX) {
this.setLocation(new Point(newX, this.getLocation().y));
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// run the update on the FX Application Thread for thread safety as well as to prevent errors in certain cases
Platform.runLater(() -> this.setX(this.getLocation().x + 1));
}
}
}
monsterThread = new Thread(monster1);
monsterThread.start();
monsterImageView1.xProperty().bind(Bindings.createIntegerBinding(() -> monster1.getLocation().x * scale, monster1.locationProperty()));
monsterImageView1.yProperty().bind(Bindings.createIntegerBinding(() -> monster1.getLocation().y * scale, monster1.locationProperty()));
root.getChildren().add(monsterImageView1);
你能发布怪物的源代码吗?不要使用线程来实现这种功能。如果不需要其他线程,请使用。改用AnimationTimer。NAimationTimer中的handle方法以纳秒为单位提供时间戳。经过x秒后,使用
this.setX(this.currentPoint.x+1)创建方法;停止()
@ChrisSmith updated虽然重复的答案是正确的方法,但我想我应该回答你原来的问题。问题是您正在更新Monster
类中的currentPoint
,但此值从未传播到monsterImageView1
。解决该特定问题的方法是将currentPoint
转换为ObjectProperty
,然后对其使用绑定。但是我同意@James_D,一个时间线
将是更好的方法。你能发布怪物
的源代码吗?不要使用线程来实现这种功能。如果不需要其他线程,请使用。改用AnimationTimer。NAimationTimer中的handle方法以纳秒为单位提供时间戳。经过x秒后,使用this.setX(this.currentPoint.x+1)创建方法;停止()
@ChrisSmith updated虽然重复的答案是正确的方法,但我想我应该回答你原来的问题。问题是您正在更新Monster
类中的currentPoint
,但此值从未传播到monsterImageView1
。解决该特定问题的方法是将currentPoint
转换为ObjectProperty
,然后对其使用绑定。但是我同意@James_D,一个时间线将是更好的方法。