gpt4 book ai didi

JavaFX 从另一个线程更新 AnchorPane

转载 作者:行者123 更新时间:2023-12-02 02:06:37 26 4
gpt4 key购买 nike

所以我对这个有点卡住了。我有一个非常基本的游戏,你可以用箭头键在网格中移动一艘船。

我添加了另一个线程,其中包含一些应该自动在网格中漫游的怪物。我可以从打印语句中看到线程正在运行并且怪物正在移动,但是图像位置没有更新。

我发现了一些类似的问题,并且有很多使用 Platfrom.runLater 的建议。但我不确定它是否适合我的具体情况,如果适合,如何实现。

这就是 Monster 类正在做的事情,每秒将怪物向右移动一格。正如我提到的,每次调用 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();
}

如果需要,我可以提供更多代码,这就是我目前认为相关的所有代码。

我的问题又来了,如何从另一个线程更新 JavaFX 线程的 UI?

最佳答案

当您更新 Monster 内的 currentPoint 时,该值永远不会传播到 monsterImageView1。您应该将 currentPoint 转换为属性,然后绑定(bind)到它:

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);
<小时/>

但是,正如 @James_D 提到的,Timeline以正确的方式解决这个问题将是一个更好的方法:

class Monster {
private ReadOnlyObjectWrapper<Point> location = new ReadOnlyObjectWrapper<>();
private Timeline timeline;

public Monster(int x, int y) {
setLocation(new Point(x, y));

timeline = new Timeline(new KeyFrame(Duration.seconds(1), event -> {
setX(getLocation().x + 1);
}));
timeline.setCycleCount(Timeline.INDEFINITE);
}

public void start() {
timeline.play();
}

// NOTE: remember to call stop() or this will result in a memory leak
public void stop() {
timeline.stop();
}

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));
}
}

关于JavaFX 从另一个线程更新 AnchorPane,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50671047/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com