2017-11-20 1 views
1

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

내가 우주선을 생략하고있어, 간결성을 위해 빔과 락 클래스.

+0

'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

+0

내 질문에 언급했듯이, 나는 현재 AnimationTimer를 사용하고 있습니다. –

+0

튜토리얼에서는 올바르게 사용하는 방법을 설명합니다. 당신이 이미 그것을 사용하고 있다는 것을 나는 눈치 채지 못했습니다. – Sedrick

답변

0

나는이 문제를 발견했다. 제임스 _D가 제안한대로 코드를 프로파일 링하고보다 지연된 충돌이 더 정확한 충돌 검사를 위해 사용했던 Shape.intersect (Shape, Shape) 메소드로 인해 발생한다는 사실을 발견했습니다. 나는 규칙적인 Bounds 검사 방법으로 바꾸었고 이제는 부드럽게 실행됩니다.

관련 문제