2017-08-24 6 views
3

私はJavaFXで小惑星のゲームクローンを作ろうとしています。今のところ、私は船と小惑星をスクリーン上に描画することができました(ここでは四角形がそれらを表しています)。私はまた、船の動きと、小惑星の無作為化された動きを実装しました。JavaFX小惑星ゲームクローン:互いに小惑星をバウンスするのに問題がある

小惑星を互いに衝突させるために必要なコードを実装するのに問題があります。衝突チェックを行う現在の方法(checkAsteroidCollisionsと呼ばれています)は、すべての小惑星が恒常的に吃音を開始するという点で盗聴されています。彼らは動きませんが、むしろその場で急速に前後に振動します。このメソッドを呼び出さなければ、すべての小惑星が正常に動くようになります。

代わりに、私は各小惑星が自由に動くようにしたい、そして、別の小惑星と接触すると、実際の小惑星ゲームのようにお互いに跳ね返る。

MainApp.java

import java.util.ArrayList; 
    import java.util.HashSet; 

    import javafx.animation.AnimationTimer; 
    import javafx.application.Application; 
    import javafx.event.EventHandler; 
    import javafx.scene.Group; 
    import javafx.scene.Scene; 
    import javafx.scene.canvas.Canvas; 
    import javafx.scene.canvas.GraphicsContext; 
    import javafx.scene.input.KeyEvent; 
    import javafx.scene.layout.StackPane; 
    import javafx.scene.paint.Color; 
    import javafx.stage.Stage; 

    public class MainApp extends Application { 
     private static final int WIDTH = 700; 
     private static final int HEIGHT = 900; 

     private static final int NUM_OF_ASTEROIDS = 12; 
     private static final Color ASTEROID_COLOR = Color.GRAY; 

     private static final Color PLAYER_COLOR = Color.BLUE; 

     private Player player; 
     private ArrayList<Entity> asteroids; 

     long lastNanoTime; // For AnimationTimer 

     HashSet<String> inputs; // For inputs 

     private static final int MAX_SPEED = 150; 
     private static final int SPEED = 10; 
     private static final int ASTEROID_SPEED = 150; 

private StackPane background; 

/* 
* Generates a random number between min and max, inclusive. 
*/ 
private float genRandom(int min, int max) { 
    return (float) Math.floor(Math.random() * (max - min + 1) + min); 
} 

/* 
* Initializes the asteroids 
*/ 
private void initAsteroids() { 
    this.asteroids = new ArrayList<Entity>(); 
    for (int i = 0; i < NUM_OF_ASTEROIDS; i++) { 
     Entity asteroid = new Entity(50, 50, ASTEROID_COLOR, EntityType.ASTEROID); 
     float px = (float) genRandom(200, WIDTH - 50); 
     float py = (float) genRandom(200, HEIGHT - 50); 
     asteroid.setPos(px, py); 

     // Keep recalculating position until there are no collisions 
     while (asteroid.intersectsWith(this.asteroids)) { 
      px = (float) genRandom(200, WIDTH - 50); 
      py = (float) genRandom(200, HEIGHT - 50); 
      asteroid.setPos(px, py); 
     } 

     // Randomly generate numbers to change velocity by 
     float dx = this.genRandom(-ASTEROID_SPEED, ASTEROID_SPEED); 
     float dy = this.genRandom(-ASTEROID_SPEED, ASTEROID_SPEED); 
     asteroid.changeVelocity(dx, dy); 

     this.asteroids.add(asteroid); 
    } 
} 

/* 
* Initializes the player 
*/ 
private void initPlayer() { 
    this.player = new Player(30, 30, PLAYER_COLOR, EntityType.PLAYER); 
    this.player.setPos(WIDTH/2, 50); 
} 

/* 
* Checks collisions with screen boundaries 
*/ 
private void checkOffScreenCollisions(Entity e) { 
    if (e.getX() < -50) 
     e.setX(WIDTH); 
    if (e.getX() > WIDTH) 
     e.setX(0); 
    if (e.getY() < -50) 
     e.setY(HEIGHT); 
    if (e.getY() > HEIGHT) 
     e.setY(0); 
} 

/* 
* Controls speed 
*/ 
private void controlSpeed(Entity e) { 
    if (e.getDx() < -MAX_SPEED) 
     e.setDx(-MAX_SPEED); 
    if (e.getDx() > MAX_SPEED) 
     e.setDx(MAX_SPEED); 
    if (e.getDy() < -MAX_SPEED) 
     e.setDy(-MAX_SPEED); 
    if (e.getDy() > MAX_SPEED) 
     e.setDy(MAX_SPEED); 
} 

/* 
* Controls each asteroid's speed and collision off screen 
*/ 
private void controlAsteroids(ArrayList<Entity> asteroids) { 
    for (Entity asteroid : asteroids) { 
     this.checkOffScreenCollisions(asteroid); 
     this.controlSpeed(asteroid); 
    } 
} 

/* 
* Checks an asteroid's collision with another asteroid 
*/ 
private void checkAsteroidCollisions() { 
    for (int i = 0; i < NUM_OF_ASTEROIDS; i++) { 
     Entity asteroid = this.asteroids.get(i); 
     if (asteroid.intersectsWith(this.asteroids)){ 
      float dx = (float) asteroid.getDx(); 
      float dy = (float) asteroid.getDy(); 
      asteroid.setDx(0); 
      asteroid.setDy(0); 
      asteroid.changeVelocity(-dx, -dy); 
     } 
    } 
} 

@Override 
public void start(Stage primaryStage) throws Exception { 
    primaryStage.setTitle("Hello World!"); 

    this.initAsteroids(); 
    this.initPlayer(); 

    background = new StackPane(); 
    background.setStyle("-fx-background-color: pink"); 

    this.inputs = new HashSet<String>(); 

    Group root = new Group(); 
    Scene scene = new Scene(root); 
    primaryStage.setScene(scene); 

    scene.setOnKeyPressed(new EventHandler<KeyEvent>() { 

     @Override 
     public void handle(KeyEvent e) { 
      String code = e.getCode().toString(); 
      inputs.add(code); 
     } 

    }); 

    scene.setOnKeyReleased(new EventHandler<KeyEvent>() { 

     @Override 
     public void handle(KeyEvent e) { 
      String code = e.getCode().toString(); 
      inputs.remove(code); 
     } 

    }); 

    Canvas canvas = new Canvas(WIDTH, HEIGHT); 
    GraphicsContext gc = canvas.getGraphicsContext2D(); 

    background.getChildren().add(canvas); 
    root.getChildren().add(background); 

    lastNanoTime = System.nanoTime(); 

    new AnimationTimer() { 
     @Override 
     public void handle(long currentNanoTime) { 
      float elapsedTime = (float) ((currentNanoTime - lastNanoTime)/1000000000.0); 
      lastNanoTime = currentNanoTime; 

      /* PLAYER */ 
      // Game Logic 
      if (inputs.contains("A")) 
       player.changeVelocity(-SPEED, 0); 
      if (inputs.contains("D")) 
       player.changeVelocity(SPEED, 0); 
      if (inputs.contains("W")) 
       player.changeVelocity(0, -SPEED); 
      if (inputs.contains("S")) 
       player.changeVelocity(0, SPEED); 
      // Collision with edge of map 
      checkOffScreenCollisions(player); 
      // Control speed 
      controlSpeed(player); 
      player.update(elapsedTime); 

      /* ASTEROIDS */ 
      gc.setFill(ASTEROID_COLOR); 

      for(int i = 0; i < NUM_OF_ASTEROIDS; i++) { 
       checkAsteroidCollisions(i); // BUGGY CODE 
      } 
      controlAsteroids(asteroids); 

      gc.clearRect(0, 0, WIDTH, HEIGHT); 

      for (Entity asteroid : asteroids) { 
       asteroid.update(elapsedTime); 
       asteroid.render(gc); 
      } 

      gc.setFill(PLAYER_COLOR); 
      player.render(gc); 
     } 

    }.start(); 

    primaryStage.show(); 
} 

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

} 

Entity.java:

import java.util.ArrayList; 

    import javafx.geometry.Rectangle2D; 
    import javafx.scene.canvas.GraphicsContext; 
    import javafx.scene.paint.Color; 

    public class Entity { 
     private Color color; 
     private double x, y, width, height, dx, dy; 
     private EntityType entityType; // ID of this Entity 

public Entity(float width, float height, Color color, EntityType type) { 
    this.x = this.dx = 0; 
    this.y = this.dy = 0; 
    this.width = width; 
    this.height = height; 
    this.color = color; 
    this.entityType = type; 
} 

/* 
* Getters and setters 
*/ 
public Color getColor() { 
    return color; 
} 

public void setColor(Color color) { 
    this.color = color; 
} 

public double getX() { 
    return x; 
} 

public void setX(float x) { 
    this.x = x; 
} 

public double getY() { 
    return y; 
} 

public void setY(float y) { 
    this.y = y; 
} 

public double getDx() { 
    return dx; 
} 

public void setDx(float dx) { 
    this.dx = dx; 
} 

public double getDy() { 
    return dy; 
} 

public void setDy(float dy) { 
    this.dy = dy; 
} 

public EntityType getEntityType() { 
    return entityType; 
} 

/* 
* Adds to dx and dy (velocity) 
*/ 
public void changeVelocity(float dx, float dy) { 
    this.dx += dx; 
    this.dy += dy; 
} 

/* 
* Sets position 
*/ 
public void setPos(float x, float y) { 
    this.setX(x); 
    this.setY(y); 
} 

/* 
* Gets new position of the Entity based on velocity and time 
*/ 
public void update(float time) { 
    this.x += this.dx * time; 
    this.y += this.dy * time; 
} 

/* 
* Used for collisions 
*/ 
public Rectangle2D getBoundary() { 
    return new Rectangle2D(this.x, this.y, this.width, this.height); 
} 

/* 
* Checks for intersections 
*/ 
public boolean intersectsWith(Entity e) { 
    return e.getBoundary().intersects(this.getBoundary()); 
} 

/* 
* If any of the entities in the passed in ArrayList 
* intersects with this, then return true; 
*/ 
public boolean intersectsWith(ArrayList<Entity> entities) { 
    for(Entity e : entities) { 
     if(e.getBoundary().intersects(this.getBoundary())) 
      return true; 
    } 
    return false; 
} 


/* 
* Draws the shape 
*/ 
public void render(GraphicsContext gc) { 
    gc.fillRoundRect(x, y, width, height, 10, 10); 
} 

@Override 
public String toString() { 
    return "Entity [x=" + x + ", y=" + y + ", width=" + width + ", height=" + height + ", entityType=" + entityType 
      + "]"; 
} 

} 
+0

質問の最初の段落を削除して編集しました。質問が採点されるウェブサイトに投票システムがあります。一般に、肯定的なスコアは良い質問を意味し、否定的なスコアは悪い質問です。それが悪い場合は、より多くの情報や何かを与えるようにコメントを追加することができますが、それが良いのであれば、明示的に言われることはありません! – crazyloonybin

+1

あなたの小惑星がすべての小惑星をチェックするので、小惑星が交差しているかどうかのチェックが含まれています - これは常に真実であるため、すべての小惑星はすべてのフレームで "バウンス"します。 –

+0

そこにはいくつかのバグがあります。そのバグは、あなたがそれを修正したときに明らかになるでしょう。小惑星が衝突すると、速度を打ち消すだけです。これは、45度の角度の壁を跳ね返っている場合には適切です。しかし、これは小惑星の実際の衝突を正しく見ることはできません。 (同じ大きさの場合は、小惑星の中心を結んでいる線に垂直な線からそれらを跳ね返りたい、そうでなければ運動量の保存方程式を使う)。次のフレームにもはや衝突しないように、それらを十分に移動させないかもしれない。 –

答えて

0

私は彼らの状態はもう衝突ではないので、あなたはわずか2つの衝突小惑星の位置を変更する必要があると思います。

今すぐ起こるのは、衝突後の動きを変更することですが、アルゴリズムがまだ触れていることに気付くので、以前と同じ結果で動きを再び変更しようとします。

今実装する必要があるのは、2つの小惑星の位置が変更されたため、最初の呼び出し直後に衝突検出が再開しないようにすることです。

私はあなたを助けてくれることを願っています。

+0

OPのコードには他にも問題があります。これらの問題が修正されると、これが発生する可能性があります。私が通常この可能性に対処する方法は、2つのものを、それらが交差する場合には衝突として定義し、*間の距離は小さくなります(小惑星が速度を参照するため、ここではかなり簡単です)。これは、任意の動きのハックを回避します(おそらくここではOKですが、物理シミュレーションではそうではありません)。 –

関連する問題