Initial commit, for 2024/2025 students

This commit is contained in:
Daniel Merkle 2025-01-28 09:06:43 +01:00
parent 59e5da3dd5
commit 7ceadb7ebd
2 changed files with 434 additions and 0 deletions

65
pom.xml Normal file
View 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>

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