gpt4 book ai didi

java - 如何从后台线程安全地更新 JavaFX 应用程序的 GUI?

转载 作者:太空宇宙 更新时间:2023-11-04 10:55:05 25 4
gpt4 key购买 nike

我正在尝试使用 JavaFX 2.2 编写 Asteroids 游戏,但当我尝试移动游戏对象(即岩石、宇宙飞船和光束)或检测它们之间的碰撞时遇到问题。

最初,我尝试使用 ScheduledThreadPoolExecutor 类的 scheduleAtFixedRate(Runnable, long, long, TimeUnit) 方法从后台线程执行所有移动和碰撞检测,但这导致了可怕的运行时异常,这些异常甚至不在我的代码中,因为我试图从后台线程修改 GUI。

我的下一个方法是使用 AnimationTimer 类从 UI 线程本身更新游戏对象。虽然这种方法解决了异常问题,但在 UI 线程上运行时,它导致了严重的滞后。

那么,我想知道是否有一种可行的方法来更新游戏对象而不导致异常或滞后?

这是我的应用程序的主类:

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.media.AudioClip;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;

import java.util.ArrayList;

public class Main extends Application {
private ArrayList<Rock> rocks = new ArrayList<>();
private ArrayList<Beam> beams = new ArrayList<>();
private SpaceShip spaceShip = null;
private Group group;
private final int SCENE_WIDTH = 900, SCENE_HEIGHT = 600;
private final int ROCK_COUNT = 20;
private boolean upKeyPressed, upKeyReleased, zKeyPressed, leftKeyPressed, rightKeyPressed;
private int bulletsFired = 0, skipCount = 10;
private AudioClip explosion = new AudioClip(Main.class.getResource("explosion.wav").toString());
private AudioClip destroy = new AudioClip(Main.class.getResource("destroy.mp3").toString());

public static void main(String args[]) {
launch();
}

@Override
public void start(Stage primaryStage) throws Exception {
ImageView spaceBackground = new ImageView("space.jpg");
spaceBackground.setFitHeight(SCENE_HEIGHT);
spaceBackground.setFitWidth(SCENE_WIDTH);

group = new Group(spaceBackground);
Scene scene = new Scene(group, SCENE_WIDTH, SCENE_HEIGHT);

initializeGameObjects();

// add event listeners for the spaceShip controls
scene.setOnKeyPressed((keyEvent) -> {
switch(keyEvent.getCode()) {
case UP:
upKeyPressed = true;
break;
case Z:
zKeyPressed = true;
break;
case LEFT:
leftKeyPressed = true;
break;
case RIGHT:
rightKeyPressed = true;
}
});

scene.setOnKeyReleased((keyEvent) -> {
switch(keyEvent.getCode()) {
case UP:
upKeyPressed = false;
upKeyReleased = true;
break;
case Z:
zKeyPressed = false;
break;
case LEFT:
leftKeyPressed = false;
break;
case RIGHT:
rightKeyPressed = false;
}
});

AnimationTimer updater = new AnimationTimer() {
@Override
public void handle(long now) {
updateGameObjects();
}
};

primaryStage.setScene(scene);
primaryStage.setTitle("Asteroids");
primaryStage.setResizable(false);
primaryStage.getIcons().add(new Image(Main.class.getResource("icon.png").toString()));
primaryStage.show();

updater.start();
}

private void initializeGameObjects() {
// initialize the Rock ArrayList
for(int i=0; i<ROCK_COUNT; i++) {
Rock rock = new Rock();
rocks.add(rock);
group.getChildren().add(rock);
}

// add the space ship to the center
spaceShip = new SpaceShip();
group.getChildren().add(spaceShip);
}

private void updateGameObjects() {
// move the rocks
for(Rock rock: rocks) {
rock.move(rocks);
}

// check for collision among rocks
for(int i=0; i<rocks.size(); i++) {
for(int j=i+1; j<rocks.size(); j++) {
Rock rock1 = rocks.get(i), rock2 = rocks.get(j);

// if two rocks collide, interchange their speeds
if(rock1.getBoundsInParent().intersects(rock2.getBoundsInParent())) {
int tmpSpeedX = rock1.getSpeedX();
int tmpSpeedY = rock1.getSpeedY();

rock1.setSpeedX(rock2.getSpeedX());
rock1.setSpeedY(rock2.getSpeedY());

rock2.setSpeedX(tmpSpeedX);
rock2.setSpeedY(tmpSpeedY);
}
}
}

// control the spaceShip
if(upKeyPressed) {
spaceShip.accelerate();
//System.out.println(spaceShip.getSpeed());
}
else if(upKeyReleased) {
if(spaceShip.getSpeed() > 0)
spaceShip.decelerate();
else {
spaceShip.nullifySpeed();
upKeyReleased = false;
}
//System.out.println(spaceShip.getSpeed());
}

if(leftKeyPressed)
spaceShip.rotateLeft();
if(rightKeyPressed)
spaceShip.rotateRight();
if(zKeyPressed) {
if(bulletsFired < 4) {
beams = spaceShip.fire(group);
bulletsFired++;
skipCount = 15;
} else {
skipCount--;

if(skipCount == 0)
bulletsFired = 0;
}
}

// move the beams
for(int i=0; i<beams.size(); i++) {
Beam beam = beams.get(i);

if(!beam.isAlive()) {
beams.remove(beam);
continue;
}

beam.move();
}

// check if the ship hits a rock
for(int i=0; i<rocks.size(); i++) {
Rock rock = rocks.get(i);

if(Shape.intersect(spaceShip, rock).getLayoutBounds().getWidth() > 0) {
rock.setVisible(false);
rocks.remove(rock);
explosion.play(0.04, 0, 1.5, 0, 1);
}
}

// check if a beam hits a rock
for(int i=0; i<beams.size(); i++) {
for(int j=0; j<rocks.size(); j++) {
Beam beam = beams.get(i);
Rock rock = rocks.get(j);

if(Shape.intersect(beam, rock).getLayoutBounds().getWidth() > 1) {
rock.setVisible(false);
rocks.remove(rock);
beam.setVisible(false);
beams.remove(beam);

destroy.play(0.04, 0, 1.5, 0, 1);
}
}
}
}
}

为了简洁起见,我省略了 SpaceShip、Beam 和 Rock 类。

最佳答案

我发现了问题。我按照 James_D 的建议分析了我的代码,发现所有延迟都是由 Shape.intersect(Shape, Shape) 方法引起的,我使用该方法进行更精确的碰撞检查。我用常规的边界检查方法替换了它,现在运行顺利。

关于java - 如何从后台线程安全地更新 JavaFX 应用程序的 GUI?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47397267/

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