我正在尝试创建一个简单的游戏,但我已经在背景方面失败了。 在出现此错误之前,我的代码仅运行几秒钟:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at javafx.scene.Parent.updateCachedBounds(Parent.java:1592)
at javafx.scene.Parent.recomputeBounds(Parent.java:1535)
at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
at javafx.scene.Node.updateGeomBounds(Node.java:3579)
at javafx.scene.Node.getGeomBounds(Node.java:3532)
at javafx.scene.Node.getLocalBounds(Node.java:3480)
at javafx.scene.Node.updateTxBounds(Node.java:3643)
at javafx.scene.Node.getTransformedBounds(Node.java:3426)
at javafx.scene.Node.updateBounds(Node.java:559)
at javafx.scene.Parent.updateBounds(Parent.java:1719)
at javafx.scene.Parent.updateBounds(Parent.java:1717)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2404)
at com.sun.javafx.tk.Toolkit.lambda$runPulse$29(Toolkit.java:398)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:397)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:424)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:510)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:490)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$403(QuantumToolkit.java:319)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication.lambda$null$48(GtkApplication.java:139)
at java.lang.Thread.run(Thread.java:748)
我尝试自己找出答案,但我不知道。
这是我的代码:
public class RocketMain extends Application{
int width = 640;
int height = 1000;
Pane root;
Stars stars;
public static void main(String[] args){
Application.launch();
}
@Override
public void start(Stage primaryStage) throws Exception {
root = new Pane();
Scene sc = new Scene(root, width, height);
primaryStage.setScene(sc);
primaryStage.setResizable(false);
primaryStage.centerOnScreen();
stars = new Stars(width, height);
new Thread(() -> stars.createStars()).start();
new Thread(() -> update()).start();
root.getChildren().add(stars);
primaryStage.show();
primaryStage.setOnCloseRequest(e -> {
Platform.exit();
System.exit(0);
});
}
public void update(){
while(true){
Platform.runLater(() -> {
stars.getChildren().removeAll(stars.getChildren());
stars.getChildren().addAll(stars.stars);
root.getChildren().removeAll(root.getChildren());
root.getChildren().add(stars);
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
星星类不断产生新的 Sprite 并在屏幕外移动/删除它们:
public class Stars extends Pane {
public ArrayList<Rectangle> stars;
ArrayList<Rectangle> rem;
double width, height;
public Stars(int width, int height){
stars = new ArrayList<>();
rem = new ArrayList<>();
this.width = width;
this.height = height;
}
public void createStars(){
int slowing = 3;
while(true){
if (Math.random() < (1.0/slowing)){
int size = (int) Math.floor(Math.random()*8);
Rectangle temp = new Rectangle(Math.random() * width, -10, size, size);
temp.setFill(Color.BLACK);
this.stars.add(temp);
Platform.runLater(() -> {
this.getChildren().remove(temp);
this.getChildren().add(temp);
});
}
for (Rectangle r : stars) {
r.setY(r.getY() + r.getHeight() / slowing);
if (r.getY() > height / 2) {
rem.add(r);
}
}
stars.removeAll(rem);
rem.clear();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
蒂亚,我希望这是可以理解的
最佳答案
您似乎已将几乎所有从属于场景的后台线程节点更新的代码移至 Platform.runLater
调用中。堆栈跟踪表明情况确实如此。
不过您错过了一个进行更新的地方:
r.setY(r.getY() + r.getHeight() / slowing);
在 createStars
内增强的 for
循环中。
但是,还有另一个问题可能会导致类似的问题,但堆栈跟踪表明在本次运行中情况并非如此:
您从后台线程更新列表,但从 javafx 应用程序 thead 读取它们。不过,没有为此完成同步,这可能会导致其中一个线程访问处于不一致状态的列表。
但是您的逻辑比实际需要的更复杂。如果我们忽略 Thread.sleep,则在每次循环迭代中运行的代码执行起来不会花费很长时间。 JavaFX 为您提供了一个类,允许您在 JavaFX 应用程序线程上安排定期运行的逻辑,我建议使用该类 (Timeline
)。
public class Stars extends Pane {
double width, height;
private final Timeline timeline;
public void startAnimation() {
timeline.play();
}
public Stars(int width, int height){
timeline = new Timeline(new KeyFrame(Duration.millis(20), new EventHandler<ActionEvent>() {
private int iteration = 0;
private final Random random = new Random();
private final List<Rectangle> stars = new LinkedList<>();
private final Set<Rectangle> toRemove = new HashSet<>();
@Override
public void handle(ActionEvent event) {
// iteration is 0 on every 5th iteration to simulate both background threads at once
iteration = (iteration + 1) % 5;
final int slowing = 3;
if (random.nextInt(slowing) == 0) { // also true in 1 of 3 cases
int size = random.nextInt(8);
Rectangle temp = new Rectangle(random.nextDouble() * Stars.this.width, -10, size, size);
temp.setFill(Color.BLACK);
this.stars.add(temp);
getChildren().add(temp);
}
Iterator<Rectangle> iter = stars.iterator();
while (iter.hasNext()) {
Rectangle r = iter.next();
if (r.getY() > Stars.this.height / 2) {
// move rect list to set
toRemove.add(r);
iter.remove();
}
}
if (iteration == 0) {
// logic from background thread with lower frequency
getChildren().removeAll(toRemove);
toRemove.clear();
}
}
}));
timeline.setCycleCount(Animation.INDEFINITE);
this.width = width;
this.height = height;
}
}
这样,您所需要做的就是调用 startAnimation
,不需要线程,也不会出现并发问题。
进一步说明:
list.removeAll(list);
应改为
list.clear();
既然你跟进了
list.addAll(list2);
您也可以将这两行替换为
list.setAll(list2);
(ObservableList
提供此功能)
关于java - 如何找出导致此 Java FX 应用程序线程异常的原因?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59796573/