JavaFX 2.2을 사용하여 소행성 게임을 작성하려고하지만 게임 개체 (예 : 바위, 우주선 및 광선)를 이동하거나 충돌을 감지하려고 할 때 문제가 발생합니다. JavaFX 응용 프로그램의 GUI를 백그라운드 스레드에서 안전하게 업데이트하는 방법은 무엇입니까?
처음에 나는 (Runnable를 오래, 오래, TimeUnit와)를 scheduleAtFixedRate 로 스케줄 클래스의 방법을 사용하여 백그라운드 스레드에서 모든 이동 및 충돌 감지하려고 노력하지만, 이것은 년후 끔찍한 런타임 예외를 일으키는 심지어 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);
}
}
}
}
}
내가 우주선을 생략하고있어, 간결성을 위해 빔과 락 클래스.
'JavaFX's' ['AnimatinTimer'] (https://docs.oracle.com/javase/8/javafx/api/javafx/animation/AnimationTimer.html)를보십시오. 이 튜토리얼을 통해 이동하십시오 : https://gamedevelopment.tutsplus.com/tutorials/introduction-to-javafx-for-game-development--cms-23835 – Sedrick
내 질문에 언급했듯이, 나는 현재 AnimationTimer를 사용하고 있습니다. –
튜토리얼에서는 올바르게 사용하는 방법을 설명합니다. 당신이 이미 그것을 사용하고 있다는 것을 나는 눈치 채지 못했습니다. – Sedrick