我想在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
+ "]";
}
}
我已经把一个编辑从问题中删除第一段 - 在网站上有一个投票系统,您的问题得分。一般来说,积极分数意味着一个好问题,否定分数是一个不好的问题。如果不好,你可能会收到评论,要求你提供更多的信息或者其他内容,但是如果它很好,你不可能被明确告知! – crazyloonybin
您的支票包括检查每个小行星是否相交,因为您检查每个小行星是否对每个小行星 - 显然这总是如此,因此每个小行星都会在每个帧上“反弹”。 –
还有一些其他的错误你可能会在修复时发现。当小行星碰撞时,你只是否定了速度。如果他们从45度倾斜的墙上反弹,这将是适当的;但这对于小行星的实际碰撞看起来并不正确。 (如果两者的大小相同,则要将它们从垂直于连接小行星中心线的线上反弹;否则,请使用动量守恒方程式)。您可能仍然会因为您而发生“振荡”可能无法将它们移动到足以不再碰撞下一帧。 –