2017-11-20 2 views
1

Ich versuche, das Asteroids-Spiel mit JavaFX 2.2 zu schreiben, aber ich habe Probleme, wenn ich versuche, die Spielobjekte zu bewegen (d. H. Die Felsen, Raumschiff und Balken) oder Kollisionen zwischen ihnen zu erkennen.Wie kann man die GUI einer JavaFX-Anwendung von einem Hintergrund-Thread sicher aktualisieren?

Anfangs habe ich versucht, alle beweglichen und Kollisionserkennung von einem Hintergrund-Thread mit dem scheduleAtFixedRate (Runnable, lang, lang, Timeunit) Methode der ScheduledThreadPoolExecutor Klasse zu tun, aber diese Ausnahmen allerdings nicht der Laufzeit verursacht wurde, die wasn nicht einmal in meinem Code, weil ich versucht habe, die GUI als Hintergrund-Thread zu modifizieren.

Meine nächste Vorgehensweise bestand darin, die Spielobjekte von einem UI-Thread selbst zu aktualisieren, indem die AnimationTimer Klasse verwendet wurde. Während mit diesem Ansatz das Problem der Ausnahmen behoben wurde, das auf dem UI-Thread ausgeführt wird, verursacht es erhebliche Verzögerungen.

Also, ich möchte wissen, ob es eine machbare Möglichkeit gibt, die Spielobjekte zu aktualisieren, ohne Ausnahmen oder Verzögerungen zu verursachen?

Hier ist die Hauptklasse meiner Anwendung:

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

ich das Raumschiff, Beam und Rock-Klassen für die der Kürze halber weggelassen.

+0

Schauen Sie sich 'JavaFX's [' AnimatinTimer'] an (https://docs.oracle.com/javase/8/javafx/api/javafx/animation/AnimationTimer.html). Gehen Sie durch dieses Tutorial: https://gamedevelopment.tutsplus.com/tutorials/introduction-to-javafx-for-game-development--cms-23835 – Sedrick

+0

Wie ich in meiner Frage erwähnt habe, verwende ich derzeit AnimationTimer. –

+0

Das Tutorial erklärt, wie man es richtig benutzt. Ich habe nicht bemerkt, dass du es bereits benutzt hast. – Sedrick

Antwort

0

Ich habe das Problem herausgefunden. Ich profilierte meinen Code wie von James_D vorgeschlagen und fand heraus, dass die gesamte Verzögerung durch die Shape.intersect (Shape, Shape) Methode verursacht wurde, die ich für eine genauere Kollisionsprüfung verwendete. Ich ersetzte das mit der regulären Bounds-Prüfmethode und es läuft jetzt reibungslos.

Verwandte Themen