From 6ddf373bc968dfa26b4a98d697bd0820e2710188 Mon Sep 17 00:00:00 2001 From: Kai Simon Date: Thu, 23 Jun 2022 20:48:08 +0200 Subject: [PATCH] Aufgabe 09 --- GameProject/src/base/BreakoutGame.java | 2 +- .../src/controller/BreakoutController.java | 28 ++++ .../src/controller/MineController.java | 21 ++- GameProject/src/log4j2.xml | 2 +- .../src/playground/BreakoutLevel0.java | 119 ++++++++++++++ .../src/playground/BreakoutLevel2.java | 152 ++++++++++++++++++ .../src/playground/BreakoutLevel3.java | 70 ++++++++ .../playground/BreakoutLevelBaseAdvanced.java | 98 +++++++++++ 8 files changed, 486 insertions(+), 6 deletions(-) create mode 100644 GameProject/src/controller/BreakoutController.java create mode 100644 GameProject/src/playground/BreakoutLevel0.java create mode 100644 GameProject/src/playground/BreakoutLevel2.java create mode 100644 GameProject/src/playground/BreakoutLevel3.java create mode 100644 GameProject/src/playground/BreakoutLevelBaseAdvanced.java diff --git a/GameProject/src/base/BreakoutGame.java b/GameProject/src/base/BreakoutGame.java index d9cc8b5..34f90d3 100644 --- a/GameProject/src/base/BreakoutGame.java +++ b/GameProject/src/base/BreakoutGame.java @@ -19,7 +19,7 @@ public class BreakoutGame extends GameLoop { @Override public void defineLevels() { this.resetLevels(); // removes Level1 added by superclass constructor - this.addLevel(new BreakoutLevel1()); // FIXME add this as soon as your level exists + this.addLevel(new BreakoutLevel3()); // FIXME add this as soon as your level exists } /** diff --git a/GameProject/src/controller/BreakoutController.java b/GameProject/src/controller/BreakoutController.java new file mode 100644 index 0000000..87b5fa0 --- /dev/null +++ b/GameProject/src/controller/BreakoutController.java @@ -0,0 +1,28 @@ +package controller; + +import java.awt.event.KeyEvent; + +import gameobjects.GameObject; + +public class BreakoutController extends EgoController{ + + public BreakoutController(double width, double height) { + super(width, height); + } + + + + @Override + public void onUp(KeyEvent kc, GameObject ego) { + } + + + @Override + public void onDown(KeyEvent kc, GameObject ego) { + } + + @Override + public void onSpace(KeyEvent e, GameObject ego) { + + } +} diff --git a/GameProject/src/controller/MineController.java b/GameProject/src/controller/MineController.java index 5aa7e83..8558db4 100644 --- a/GameProject/src/controller/MineController.java +++ b/GameProject/src/controller/MineController.java @@ -4,7 +4,14 @@ import gameobjects.GameObject; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; - +/** + * Stops GameObject movement at bottom of level and let it 'fly' in x-direction towards ego object. + * If the object flies outside of the level (by x-coordinate) it is removed. + * + * The behavior looks like a sinking sea mine, which stays at a certain depth and moves left/right to catch the player. + * + * + */ public class MineController extends ObjectController { int rad = 3; @@ -17,13 +24,19 @@ public class MineController extends ObjectController { this.lineSpeed = lineSpeed; } + /** + * Fetches ego object by name 'ego' from current level and determines it's x-position. + * The controlled GameObject will move towards this position (in x-direction only). + * y-direction speed is reduced to zero, if the objects reached lower bound of level (high y-values). + * Only deletes the object if it flies out of visible game area. + */ @Override public void updateObject() { if (gameObject.getY() >= this.getPlayground().getSizeY() - 10) { this.gameObject.setVY(0); if (xSpeed == 0.) { - GameObject ego = getPlayground().getObject("ego"); + GameObject ego = this.getPlayground().getObject("ego"); double egoXPos = ego.getX(); if (egoXPos > this.gameObject.getX()) { xSpeed = 50; @@ -38,9 +51,9 @@ public class MineController extends ObjectController { } if (this.gameObject.getX() < 0 || (this.gameObject.getX() > this.getPlayground().getSizeX())) { logger.debug("deleting" + this.gameObject.getId()); - getPlayground().deleteObject(this.gameObject.getId()); + this.getPlayground().deleteObject(this.gameObject.getId()); } - applySpeedVector(); + this.applySpeedVector(); } } diff --git a/GameProject/src/log4j2.xml b/GameProject/src/log4j2.xml index e48b670..f21f70a 100644 --- a/GameProject/src/log4j2.xml +++ b/GameProject/src/log4j2.xml @@ -20,7 +20,7 @@ - + diff --git a/GameProject/src/playground/BreakoutLevel0.java b/GameProject/src/playground/BreakoutLevel0.java new file mode 100644 index 0000000..842c1a1 --- /dev/null +++ b/GameProject/src/playground/BreakoutLevel0.java @@ -0,0 +1,119 @@ +package playground; + +import java.awt.Color; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import collider.RectCollider; +import controller.EgoController; +import controller.ReboundController2; +import gameobjects.FallingStar; +import gameobjects.GameObject; +import gameobjects.RectObject; + +/** + * simple solution example for Breakout game. + * + * not implemented: penalty if balls hits ground, no score for removed bricks, no bonus items, no + * lives. + * + * + */ + + +public class BreakoutLevel0 extends BreakoutLevelBase { + + private static Logger logger = LogManager.getLogger(BreakoutLevelBase.class); + + + protected GameObject createEgoObject() { + RectObject ro = new RectObject("ego", this, 350, 550, 0, 0, 80, 10, Color.BLUE); + ro.generateColliders(); + EgoController ec = new EgoController(80,10); + ro.addController(ec); + logger.info("ego created."); + + return ro; + } + + + protected GameObject createBall() { + GameObject co = new FallingStar("ball1", this, 350, 350, 100, 100, Color.RED, 5); + co.addController(new ReboundController2()); + logger.info("ball created."); + return co; + } + + + /** + * creates one brick. For + * collision {@link RectCollider} is used. + */ + @Override + protected GameObject createBrick(int row, int column) { + double xSize = 60; + double ySize = 30; + double xStart = 40; + double yStart = 40; + double space = 5; + Color c = Color.BLUE; + + RectObject ro = new RectObject("brick" + row + "/" + column, this, xStart + column * (xSize + space), + yStart + row * (ySize + space), 0, 0, xSize - 4, ySize - 4, c); + RectCollider rc = new RectCollider("egal", ro, xSize - 4, ySize - 4); + ro.addCollider(rc); + + return ro; + } + + /** + * lets ball bounce in Y direction, deletes brick. + */ + @Override + protected void actionIfBallHitsBrick(GameObject ball, GameObject brick) { + + ball.setVY(ball.getVY() * -1); // bounce effect for ball + this.deleteObject(brick.getId()); // remove brick from field + logger.debug("deleted brick " + brick.getId()); + + } + + + /** + * Let the ball bounce off in Y direction. + * + */ + @Override + protected void actionIfBallHitsEgo(GameObject ball, GameObject ego) { + double ballY = ball.getY(); + double egoY = ego.getY(); + ball.setY(ballY < egoY ? ballY - 10 : ballY + 10); // radius 5 hard coded. + ball.setVY(ball.getVY() * -1); + logger.debug("ball bounces of ego object."); + } + + + /** + * Prepares a Breakout level with a 3 x 3 matrix of blocks on top. This method relies on the + * methods {@link #createEgo}, {@link #createBall} and {@link #createBrick}, among others. + * + * @param level String passes by the game engine (not used currently and can be ignored). + */ + @Override + public void prepareLevel(String level) { + for (int y = 3; y < 6; y++) { + for (int x = 0; x < 3; x++) { + logger.trace("trying to create brick X, Y (" + x + "," + y + ")"); + GameObject brick = this.createBrick(x, y); + this.addObject(brick); + } + } + this.ego = this.createEgoObject(); + this.ball = this.createBall(); + this.addObject(this.ego); + this.addObject(this.ball); + logger.info("level preperation succeeded."); + + } + + +} diff --git a/GameProject/src/playground/BreakoutLevel2.java b/GameProject/src/playground/BreakoutLevel2.java new file mode 100644 index 0000000..6e9ef2a --- /dev/null +++ b/GameProject/src/playground/BreakoutLevel2.java @@ -0,0 +1,152 @@ +package playground; + +import java.awt.Color; +import java.util.Random; + +import controller.BreakoutController; +import controller.LimitedTimeController; +import controller.ReboundController; +import gameobjects.GameObject; +import gameobjects.RectObject; + +public class BreakoutLevel2 extends BreakoutLevelBaseAdvanced { + + @Override + protected int calcNrBricksX() { + return 6; + } + + @Override + protected int calcNrBricksY() { + return 5; + } + + @Override + protected double getBrickSizeX() { + return 60.0; + } + + @Override + protected double getBrickSizeY() { + return 30.0; + } + + @Override + protected double getBrickStartX() { + return 90.0; + } + + @Override + protected double getBrickStartY() { + return 60.0; + } + + + /** Create Explosion effect with random Colors that lasts 2 Seconds. + * + * @param ball GameObject + * @param brick GameObject + */ + @Override + protected void actionIfBallHitsBrick(GameObject ball, GameObject brick) { + //logger.info(brick.getId()+" is hitted and deleted!"); + ball.setVY(ball.getVY() * -1.0); + int max = 240; + int min = -240; + Random rand = new Random(); + + for(int i = 0; i < 20; i++) { + Color randColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat()); + gameobjects.FallingStar explosion = new gameobjects.FallingStar("explosion"+gameTime+i, this, brick.getX(), brick.getY(), rand.nextInt(max - min + 1) + min, rand.nextInt(max - min + 1) + min, randColor, 2); + explosion.addController(new LimitedTimeController(gameTime, 2)); + this.addObjectNow(explosion); + + this.deleteObject(brick.getId()); + } + } + + + + + /** Lets Ball bounce of from Ego. + * If the right or left end part is hitted, the ball bounces of in the end part direction doesn't matter from which site + * + * @param ball Gameobject + * @param ego GameObject + */ + @Override + protected void actionIfBallHitsEgo(GameObject ball, GameObject ego) { + RectObject egoCopyCastedInRectObject = (RectObject) ego; + double egoMiddleX = ego.getX(); + double ballX = ball.getX(); + double egoWidth = egoCopyCastedInRectObject.getWidth();// Maybe an easier and cleaner way instead of casting an new + double egoHalfWidth = egoWidth / 2.0; + + double egoEndPerc = 45.0; + double egoDistEndPartFromMiddle = (egoEndPerc * egoHalfWidth) / 100; + double egoEndPartStartLeft = egoMiddleX - egoDistEndPartFromMiddle; + double egoEndPartStartRight = egoMiddleX + egoDistEndPartFromMiddle; + + if ((ballX < egoEndPartStartLeft && (ball.getVX() > 0.0)) || (ballX > egoEndPartStartRight && (ball.getVX() < 0.0))) { + ball.setVX(ball.getVX() * -1.0); + } + + ball.setVY(ball.getVY() * -1.0); + } + + /** Creates Ego object with an extra Controller for Breakout. + * + * @return ego + */ + @Override + protected GameObject createEgoObject() { + RectObject ego = new RectObject("ego", this, 350, 550, 0, 0, 60, 10, Color.blue); + ego.addController(new BreakoutController(ego.getWidth(), ego.getHeight())); + ego.addCollider(new collider.RectCollider(ego.getId(), ego, ego.getWidth(), ego.getHeight())); + return ego; + } + + /** Creates Ball. + * + * @return ball + */ + @Override + protected GameObject createBall() { + gameobjects.FallingStar ball = new gameobjects.FallingStar("ball", this, 350, 350, 130, 130, Color.red, 5); + ball.addController(new ReboundController()); + return ball; + } + + /** Creates Brick with a random color. + * + * @param row int + * @param column int + * + * @return brick + */ + @Override + protected GameObject createBrick(int row, int column) { + + double rnd = Math.random(); + Color clr; + if (rnd <= .25) { + clr = Color.red; + } else { + if (rnd <= .5) { + clr = Color.blue; + } else { + if (rnd <= .75) { + clr = Color.yellow; + } else { + clr = Color.green; + } + } + } + + RectObject brick = new RectObject("brick" + row + column, this, getBrickStartX() + (row * (getBrickSizeX() * 2)), getBrickStartY() + (column * (getBrickSizeY() * 2)), 0, 0, getBrickSizeX(), getBrickSizeY(), clr); + brick.addCollider(new collider.RectCollider(brick.getId(), brick, brick.getWidth(), brick.getHeight())); + + return brick; + } + +} diff --git a/GameProject/src/playground/BreakoutLevel3.java b/GameProject/src/playground/BreakoutLevel3.java new file mode 100644 index 0000000..4b91499 --- /dev/null +++ b/GameProject/src/playground/BreakoutLevel3.java @@ -0,0 +1,70 @@ +package playground; + +import java.awt.Color; + +import gameobjects.GameObject; + +public class BreakoutLevel3 extends BreakoutLevel2 { + + gameobjects.TextObject textPoints, textLives; + + /** Creates flags and TextObjects to display scores and Lives. + * + * @param level String + */ + @Override + public void prepareLevel(String level) { + setGlobalFlag("points", 0); + getOrCreateGlobalFlag("lives", 3); + textPoints = new gameobjects.TextObject("textPoints", this, 50 , 10, 0, 0, "Score: 0", 10, Color.black); + textLives = new gameobjects.TextObject("textLives", this, 600 , 10, 0, 0, "Lives: "+(int)getOrCreateGlobalFlag("lives", 3), 10, Color.black); + this.addObject(textPoints); + this.addObject(textLives); + + super.prepareLevel(level); + } + + /** Sets the Score if a Brick is hitted. + * + * @param ball GameObject + * @param brick + */ + @Override + protected void actionIfBallHitsBrick(GameObject ball, GameObject brick) { + super.actionIfBallHitsBrick(ball, brick); + + setGlobalFlag("points", (int)(getGlobalFlag("points")) + 10); + textPoints.setText("Score: "+(int)getGlobalFlag("points")); + + } + + /** If the Ball goes under the Ego, Lives are decreased and Ball is set above Ego. + */ + @Override + public void applyGameLogic() { + super.applyGameLogic(); + + if(ball.getY() > ego.getY()) { + setGlobalFlag("lives", (int)(getGlobalFlag("lives")) - 1); + textLives.setText("Lives: "+(int)getGlobalFlag("lives")); + if(ball.getVY() > 0) { + ball.setVY(ball.getVY() *- 1); + } + ball.setY(ego.getY()-20.0); + } + } + + /** game ends if egoLives < 0 . + * + * @return gameOver boolean + */ + @Override + public boolean gameOver() { + if((int)getGlobalFlag("lives") < 0) { + return true; + } + return false; + } +} + + diff --git a/GameProject/src/playground/BreakoutLevelBaseAdvanced.java b/GameProject/src/playground/BreakoutLevelBaseAdvanced.java new file mode 100644 index 0000000..1022481 --- /dev/null +++ b/GameProject/src/playground/BreakoutLevelBaseAdvanced.java @@ -0,0 +1,98 @@ +package playground; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; + +/** + * Advanced version of abstract {@link BreakoutLevelBase} providing a complete implementation of + * {@link #prepareLevel(String)}. Additionally abstract methods for number of bricks in X and Y + * direction are provided as well as abstract methods for brick sizes and start coordinates. + * + * @see #calcNrBricksX() + * @see #calcNrBricksY() + * @see #getBrickSizeX() + * @see #getBrickSizeY() + * @see #getBrickStartX() + * @see #getBrickStartY() + * + */ +public abstract class BreakoutLevelBaseAdvanced extends BreakoutLevelBase { + + private static Logger logger = LogManager.getLogger(BreakoutLevelBaseAdvanced.class); + + + /** + * provides the number of bricks to be set in horizontal direction. + * + * @return positive value of how many bricks are to be next to each other in X direction + */ + protected abstract int calcNrBricksX(); + + /** + * provides the number of bricks to be set in vertical direction. + * + * @return positive value of how many bricks are to be next to each other in Y direction + */ + protected abstract int calcNrBricksY(); + + /** + * provides the length of one brick. + * + * @return positive value of how long a brick should be in X direction. + */ + protected abstract double getBrickSizeX(); + + /** + * provides the height of one brick. + * + * @return positive value of how high a brick should be in Y direction. + */ + protected abstract double getBrickSizeY(); + + /** + * provides the start coordinate of upper left corner (X value). + * + * @return positive value of the X coordinate to use as the starting point of the upper left + * corner of the brick set. + */ + protected abstract double getBrickStartX(); + + /** + * provides the start coordinate of upper left corner (Y value). + * + * @return positive value of the Y coordinate to use as the starting point of the upper left + * corner of the brick set. + */ + protected abstract double getBrickStartY(); + + + + /** + * Prepares a complete Breakout type level and uses the values provided by implementations of + * {@link #calcNrBricksX()} and {@link #calcNrBricksY()} to generate the stone matrix. + * Furthermore, it relies on the methods {@link #createEgoObject()}, {@link #createBall} and {@link #createBrick}, + * which are meant to be overwritten in subclasses.
+ * Attention: For collision detection bricks created by {@link #createBrick(int, int)} need to have the String 'brick' in ID. + * + * @see LevelBreakoutBase#prepareLevel(String) for further information. + * + * @param level String passes by the game engine (not used currently and can be ignored). + * + */ + @Override + public void prepareLevel(String level) { + + for (int y = 0; y < this.calcNrBricksY(); y++) { + for (int x = 0; x < this.calcNrBricksX(); x++) { + logger.trace("trying to create brick X, Y (" + x + "," + y + ")"); + this.addObject(this.createBrick(x, y)); + } + } + this.ego = this.createEgoObject(); + this.ball = this.createBall(); + this.addObject(this.ego); + this.addObject(this.ball); + logger.info("level preperation succeeded."); + } + +}