Initial commit, for 2024/2025 students
This commit is contained in:
parent
59e5da3dd5
commit
7ceadb7ebd
65
pom.xml
Normal file
65
pom.xml
Normal file
@ -0,0 +1,65 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.techfak</groupId>
|
||||
<artifactId>Part14</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.0</version>
|
||||
<name>Part14</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<javafx.version>21.0.2</javafx.version>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<!-- JavaFX dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-controls</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-fxml</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-base</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-graphics</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-media</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>${basedir}/src</sourceDirectory>
|
||||
<plugins>
|
||||
<!-- Compiler plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
369
src/asteroids/AsteroidsApplication.java
Normal file
369
src/asteroids/AsteroidsApplication.java
Normal file
@ -0,0 +1,369 @@
|
||||
package asteroids;
|
||||
|
||||
import javafx.animation.AnimationTimer;
|
||||
import javafx.application.Application;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.shape.Polygon;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.scene.shape.Shape;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class AsteroidsApplication extends Application {
|
||||
public static final int WIDTH = 800;
|
||||
public static final int HEIGHT = 600;
|
||||
private GameController controller;
|
||||
|
||||
@Override
|
||||
public void start(Stage stage) {
|
||||
GameView view = new GameView();
|
||||
GameModel model = new GameModel(); // Initialize GameModel with no controller
|
||||
controller = new GameController(view, model); // Pass the model to the controller
|
||||
|
||||
model.setController(controller); // Set the controller in the model after initialization
|
||||
controller.startGameLoop(); // Start the game loop after everything is initialized
|
||||
|
||||
stage.setTitle("Asteroids!");
|
||||
stage.setScene(view.getScene());
|
||||
stage.show();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
launch(AsteroidsApplication.class);
|
||||
}
|
||||
}
|
||||
|
||||
class GameModel {
|
||||
private final List<Asteroid> asteroids = new ArrayList<>();
|
||||
private final List<Projectile> projectiles = new ArrayList<>();
|
||||
private final Ship ship;
|
||||
private final AtomicInteger points = new AtomicInteger();
|
||||
private boolean isGameOver = false; // Game Over state
|
||||
private GameController controller; // Reference to the controller
|
||||
|
||||
public GameModel() {
|
||||
ship = new Ship(AsteroidsApplication.WIDTH / 2, AsteroidsApplication.HEIGHT / 2);
|
||||
for (int i = 0; i < 15; i++) {
|
||||
asteroids.add(new Asteroid(new Random().nextInt(AsteroidsApplication.WIDTH / 3), new Random().nextInt(AsteroidsApplication.HEIGHT)));
|
||||
}
|
||||
}
|
||||
|
||||
// Setter for controller
|
||||
public void setController(GameController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
public Ship getShip() { return ship; }
|
||||
public List<Asteroid> getAsteroids() { return asteroids; }
|
||||
public List<Projectile> getProjectiles() { return projectiles; }
|
||||
public AtomicInteger getPoints() { return points; }
|
||||
public boolean isGameOver() { return isGameOver; }
|
||||
|
||||
public void update() {
|
||||
if (isGameOver) return; // Stop updates when game over
|
||||
|
||||
ship.move();
|
||||
asteroids.forEach(Asteroid::move);
|
||||
projectiles.forEach(Projectile::move);
|
||||
|
||||
checkCollisions();
|
||||
}
|
||||
|
||||
private void checkCollisions() {
|
||||
List<Projectile> toRemoveProjectiles = new ArrayList<>();
|
||||
List<Asteroid> toRemoveAsteroids = new ArrayList<>();
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
for (Projectile projectile : projectiles) {
|
||||
if (currentTime - projectile.getSpawnTime() > 200 && projectile.collide(ship)) {
|
||||
gameOver();
|
||||
return;
|
||||
}
|
||||
|
||||
for (Asteroid asteroid : asteroids) {
|
||||
if (projectile.collide(asteroid)) {
|
||||
toRemoveProjectiles.add(projectile);
|
||||
toRemoveAsteroids.add(asteroid);
|
||||
points.addAndGet(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Asteroid asteroid : asteroids) {
|
||||
if (asteroid.collide(ship)) {
|
||||
gameOver();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the view in the controller
|
||||
toRemoveAsteroids.forEach(asteroid -> asteroids.remove(asteroid));
|
||||
toRemoveProjectiles.forEach(projectile -> projectiles.remove(projectile));
|
||||
|
||||
if (!toRemoveAsteroids.isEmpty() || !toRemoveProjectiles.isEmpty()) {
|
||||
controller.updateViewAfterCollisions(toRemoveAsteroids, toRemoveProjectiles);
|
||||
}
|
||||
}
|
||||
|
||||
private void gameOver() {
|
||||
isGameOver = true; // End the game
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class GameView {
|
||||
private final Pane pane;
|
||||
private final Text scoreText;
|
||||
private final Scene scene;
|
||||
private final Text gameOverText;
|
||||
|
||||
public GameView() {
|
||||
pane = new Pane();
|
||||
pane.setPrefSize(AsteroidsApplication.WIDTH, AsteroidsApplication.HEIGHT);
|
||||
|
||||
scoreText = new Text(10, 20, "Points: 0");
|
||||
pane.getChildren().add(scoreText);
|
||||
|
||||
gameOverText = new Text(AsteroidsApplication.WIDTH / 2 - 50, AsteroidsApplication.HEIGHT / 2, "Game Over!");
|
||||
gameOverText.setVisible(false);
|
||||
pane.getChildren().add(gameOverText);
|
||||
|
||||
scene = new Scene(pane);
|
||||
}
|
||||
|
||||
public Scene getScene() { return scene; }
|
||||
public Pane getPane() { return pane; }
|
||||
public Text getScoreText() { return scoreText; }
|
||||
|
||||
public void addGameObject(Character obj) {
|
||||
pane.getChildren().add(obj.getCharacter());
|
||||
}
|
||||
|
||||
public void removeGameObject(Character obj) {
|
||||
pane.getChildren().remove(obj.getCharacter());
|
||||
}
|
||||
|
||||
public void setGameOverVisible(boolean visible) {
|
||||
gameOverText.setVisible(visible);
|
||||
}
|
||||
}
|
||||
|
||||
class GameController {
|
||||
private final GameView view;
|
||||
private final GameModel model;
|
||||
private final Map<KeyCode, Boolean> pressedKeys = new HashMap<>();
|
||||
private AnimationTimer gameLoop;
|
||||
private long lastShotTime = 0;
|
||||
|
||||
public GameController(GameView view, GameModel model) {
|
||||
this.view = view;
|
||||
this.model = model;
|
||||
|
||||
view.addGameObject(model.getShip());
|
||||
model.getAsteroids().forEach(view::addGameObject);
|
||||
|
||||
view.getScene().setOnKeyPressed(event -> pressedKeys.put(event.getCode(), true));
|
||||
view.getScene().setOnKeyReleased(event -> pressedKeys.put(event.getCode(), false));
|
||||
}
|
||||
|
||||
public void startGameLoop() {
|
||||
gameLoop = new AnimationTimer() {
|
||||
@Override
|
||||
public void handle(long now) {
|
||||
if (model.isGameOver()) {
|
||||
gameLoop.stop();
|
||||
view.setGameOverVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
handleInputs();
|
||||
spawnAsteroids();
|
||||
model.update();
|
||||
updateView();
|
||||
}
|
||||
};
|
||||
gameLoop.start();
|
||||
}
|
||||
|
||||
private void handleInputs() {
|
||||
if (model.isGameOver()) return;
|
||||
|
||||
if (pressedKeys.getOrDefault(KeyCode.LEFT, false)) model.getShip().turnLeft();
|
||||
if (pressedKeys.getOrDefault(KeyCode.RIGHT, false)) model.getShip().turnRight();
|
||||
if (pressedKeys.getOrDefault(KeyCode.UP, false)) model.getShip().accelerate();
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (pressedKeys.getOrDefault(KeyCode.SPACE, false) && model.getProjectiles().size() < 3
|
||||
&& currentTime - lastShotTime > 200) {
|
||||
lastShotTime = currentTime;
|
||||
double shipAngle = Math.toRadians(model.getShip().getCharacter().getRotate());
|
||||
double spawnX = model.getShip().getX() + Math.cos(shipAngle) * 15;
|
||||
double spawnY = model.getShip().getY() + Math.sin(shipAngle) * 15;
|
||||
|
||||
Projectile projectile = new Projectile(spawnX, spawnY, System.currentTimeMillis());
|
||||
projectile.getCharacter().setRotate(model.getShip().getCharacter().getRotate());
|
||||
model.getProjectiles().add(projectile);
|
||||
projectile.accelerate();
|
||||
|
||||
view.addGameObject(projectile);
|
||||
}
|
||||
}
|
||||
|
||||
private void spawnAsteroids() {
|
||||
if (Math.random() < 0.005) {
|
||||
Asteroid asteroid = new Asteroid(Math.random() * AsteroidsApplication.WIDTH, 0);
|
||||
if (!asteroid.collide(model.getShip())) {
|
||||
model.getAsteroids().add(asteroid);
|
||||
view.addGameObject(asteroid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateView() {
|
||||
view.getScoreText().setText("Points: " + model.getPoints().get());
|
||||
|
||||
List<Projectile> toRemove = new ArrayList<>();
|
||||
for (Projectile projectile : model.getProjectiles()) {
|
||||
if (projectile.getX() < 0 || projectile.getX() > AsteroidsApplication.WIDTH ||
|
||||
projectile.getY() < 0 || projectile.getY() > AsteroidsApplication.HEIGHT) {
|
||||
toRemove.add(projectile);
|
||||
}
|
||||
}
|
||||
|
||||
toRemove.forEach(projectile -> {
|
||||
model.getProjectiles().remove(projectile);
|
||||
view.removeGameObject(projectile);
|
||||
});
|
||||
}
|
||||
|
||||
public void updateViewAfterCollisions(List<Asteroid> removedAsteroids, List<Projectile> removedProjectiles) {
|
||||
// Handle view updates here
|
||||
removedAsteroids.forEach(asteroid -> view.removeGameObject(asteroid));
|
||||
removedProjectiles.forEach(projectile -> view.removeGameObject(projectile));
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Character {
|
||||
protected final Polygon character;
|
||||
protected double x, y, velocityX, velocityY;
|
||||
|
||||
public Character(Polygon shape, double x, double y) {
|
||||
this.character = shape;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.velocityX = 0;
|
||||
this.velocityY = 0;
|
||||
this.character.setTranslateX(x);
|
||||
this.character.setTranslateY(y);
|
||||
}
|
||||
|
||||
public void move() {
|
||||
x += velocityX;
|
||||
y += velocityY;
|
||||
|
||||
// Wrap around screen horizontally
|
||||
if (x < 0) x += AsteroidsApplication.WIDTH;
|
||||
if (x > AsteroidsApplication.WIDTH) x -= AsteroidsApplication.WIDTH;
|
||||
|
||||
// Wrap around screen vertically
|
||||
if (y < 0) y += AsteroidsApplication.HEIGHT;
|
||||
if (y > AsteroidsApplication.HEIGHT) y -= AsteroidsApplication.HEIGHT;
|
||||
|
||||
character.setTranslateX(x);
|
||||
character.setTranslateY(y);
|
||||
}
|
||||
|
||||
public boolean collide(Character other) {
|
||||
Shape collisionArea = Shape.intersect(this.character, other.getCharacter());
|
||||
return !collisionArea.getBoundsInLocal().isEmpty();
|
||||
}
|
||||
|
||||
public Polygon getCharacter() {
|
||||
return character;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
character.setTranslateX(x);
|
||||
}
|
||||
|
||||
public void setY(double y) {
|
||||
this.y = y;
|
||||
character.setTranslateY(y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Ship extends Character {
|
||||
public Ship(double x, double y) {
|
||||
super(new Polygon(-10, -10, 20, 0, -10, 10), x, y);
|
||||
}
|
||||
|
||||
public void turnLeft() {
|
||||
character.setRotate(character.getRotate() - 5);
|
||||
}
|
||||
|
||||
public void turnRight() {
|
||||
character.setRotate(character.getRotate() + 5);
|
||||
}
|
||||
|
||||
public void accelerate() {
|
||||
double angle = Math.toRadians(character.getRotate());
|
||||
velocityX += Math.cos(angle) * 0.1;
|
||||
velocityY += Math.sin(angle) * 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
class Asteroid extends Character {
|
||||
public Asteroid(double x, double y) {
|
||||
super(new PolygonFactory().createPolygon(), x, y);
|
||||
Random rnd = new Random();
|
||||
velocityX = rnd.nextDouble() - 0.5;
|
||||
velocityY = rnd.nextDouble() - 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Projectile extends Character {
|
||||
private final long spawnTime; // Track when the projectile was created
|
||||
|
||||
public Projectile(double x, double y, long spawnTime) {
|
||||
super(new Polygon(2, -2, 2, 2, -2, 2, -2, -2), x, y);
|
||||
this.spawnTime = spawnTime; // Store the spawn time
|
||||
}
|
||||
|
||||
public void accelerate() {
|
||||
double angle = Math.toRadians(character.getRotate());
|
||||
velocityX = Math.cos(angle) * 3;
|
||||
velocityY = Math.sin(angle) * 3;
|
||||
}
|
||||
|
||||
public long getSpawnTime() {
|
||||
return spawnTime;
|
||||
}
|
||||
}
|
||||
|
||||
class PolygonFactory {
|
||||
public Polygon createPolygon() {
|
||||
Random rnd = new Random();
|
||||
double size = 10 + rnd.nextInt(10);
|
||||
Polygon polygon = new Polygon();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
double angle = 2 * Math.PI * i / 5;
|
||||
polygon.getPoints().addAll(size * Math.cos(angle), size * Math.sin(angle));
|
||||
}
|
||||
return polygon;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user