Added alternative bubbles and start/end screen

This commit is contained in:
Andreas Greiner 2021-06-25 17:57:30 +02:00
parent 11b7799168
commit 3d389fd180
28 changed files with 539 additions and 148 deletions

View File

@ -23,6 +23,7 @@ class GameObjectManager {
private final LivesLabel livesLabel; private final LivesLabel livesLabel;
private final Background background; private final Background background;
private final GameFrame frame; private final GameFrame frame;
private final GameOverlay gameOverlay;
private final PlayerObject playerObject; private final PlayerObject playerObject;
@ -46,6 +47,16 @@ class GameObjectManager {
background = new Background(gameView); background = new Background(gameView);
frame = new GameFrame(gameView); frame = new GameFrame(gameView);
playerObject = new PlayerObject(gameView); playerObject = new PlayerObject(gameView);
gameOverlay = new GameOverlay(gameView);
}
/**
* Clear all game object lists.
*/
public void clearAll(){
gameObjects.clear();
bubbles.clear();
harpoons.clear();
} }
/** /**
@ -62,6 +73,7 @@ class GameObjectManager {
gameObjects.add(livesLabel); gameObjects.add(livesLabel);
gameObjects.add(playerObject); gameObjects.add(playerObject);
gameObjects.addAll(bubbles); gameObjects.addAll(bubbles);
gameObjects.add(gameOverlay);
gameObjects.forEach(gameObject ->{ gameObjects.forEach(gameObject ->{
gameObject.update(); gameObject.update();
@ -71,6 +83,7 @@ class GameObjectManager {
/** /**
* Get the player object. * Get the player object.
*
* @return the player * @return the player
*/ */
public PlayerObject getPlayerObject(){ public PlayerObject getPlayerObject(){
@ -79,6 +92,7 @@ class GameObjectManager {
/** /**
* Get the bubbles. * Get the bubbles.
*
* @return the bubbles * @return the bubbles
*/ */
public LinkedList<Bubble> getBubbles() { public LinkedList<Bubble> getBubbles() {
@ -87,14 +101,16 @@ class GameObjectManager {
/** /**
* Get the level label. * Get the level label.
*
* @return the level label * @return the level label
*/ */
public LevelLabel getLevelLabel(){ public LevelLabel getLevelLabel(){
return this.levelLabel; return levelLabel;
} }
/** /**
* Get the lives label. * Get the lives label.
*
* @return the lives label * @return the lives label
*/ */
public LivesLabel getLivesLabel() { public LivesLabel getLivesLabel() {
@ -103,6 +119,7 @@ class GameObjectManager {
/** /**
* Get the score label. * Get the score label.
*
* @return the score label * @return the score label
*/ */
public ScoreLabel getScoreLabel() { public ScoreLabel getScoreLabel() {
@ -111,6 +128,7 @@ class GameObjectManager {
/** /**
* Get the level progress bar. * Get the level progress bar.
*
* @return the level progress bar * @return the level progress bar
*/ */
public LevelProgressBar getLevelProgressBar(){ public LevelProgressBar getLevelProgressBar(){
@ -119,10 +137,19 @@ class GameObjectManager {
/** /**
* Get the harpoons. * Get the harpoons.
*
* @return the harpoons * @return the harpoons
*/ */
public LinkedList<Harpoon> getHarpoons() { public LinkedList<Harpoon> getHarpoons() {
return harpoons; return harpoons;
} }
/**
* Get the game overlay.
*
* @return the game overlay
*/
public GameOverlay getGameOverlay(){
return gameOverlay;
}
} }

View File

@ -11,9 +11,9 @@ import de.thdeg.greiner.superpangworld.graphics.moveable.HexagonalBubble;
import de.thdeg.greiner.superpangworld.graphics.moveable.RoundBubble; import de.thdeg.greiner.superpangworld.graphics.moveable.RoundBubble;
import de.thdeg.greiner.superpangworld.graphics.moveable.SpecialBubble; import de.thdeg.greiner.superpangworld.graphics.moveable.SpecialBubble;
import java.util.ArrayList; import java.awt.*;
import java.util.Collections; import java.util.*;
import java.util.Random; import java.util.List;
/** /**
* The manager which handles the gameplay. * The manager which handles the gameplay.
@ -26,11 +26,20 @@ public class GamePlayManager {
private final GameObjectManager gameObjectManager; private final GameObjectManager gameObjectManager;
/** Generator for random values */ /** Generator for random values */
private final Random random; private final Random random;
/** Das erste Level */ /** The current level */
private Level level; private Level level;
/** Der Spieler */ /** The player */
private final Player player; private Player player;
/** The level manager */
private LevelManager levelManager;
/** Flag, if bubbles should be spawned */
private boolean spawnBubbles;
/** Flag, if all bubbles should be destroyed */
private boolean destroyAllBubbles;
/** Flag, is a special bubble is currently present */
public boolean specialBubblePresent;
/** Flag, if the bubble movement is froze */
public boolean movementFroze;
/** /**
* Create the manager handling the gameplay. * Create the manager handling the gameplay.
@ -39,47 +48,130 @@ public class GamePlayManager {
* @param gameObjectManager the manager handling the game objects * @param gameObjectManager the manager handling the game objects
*/ */
public GamePlayManager(GameView gameView, GameObjectManager gameObjectManager) { public GamePlayManager(GameView gameView, GameObjectManager gameObjectManager) {
random = new Random();
this.gameView = gameView; this.gameView = gameView;
this.gameObjectManager = gameObjectManager; this.gameObjectManager = gameObjectManager;
level = new Level(1,10000,10000); boolean easyDifficulty = gameView.showSimpleStartScreen("Super Pang World","Start the game");
player = new Player(this.level);
random = new Random(); initializeGame(easyDifficulty);
// Set HUD elements initializeLevel();
}
/**
* Initialize the game.
*
* @param easyDifficulty the game difficulty
*/
private void initializeGame(boolean easyDifficulty){
gameObjectManager.clearAll();
levelManager = new LevelManager(easyDifficulty);
try {
level = levelManager.getNextLevel();
} catch (LevelManager.NoMoreLevelsAvailableException e) {
System.out.println("No levels were generated, exiting program");
System.exit(-1);
}
player = new Player(level);
gameObjectManager.getPlayerObject().setGamePlayManager(this); gameObjectManager.getPlayerObject().setGamePlayManager(this);
}
/**
* Initialize the level.
*/
private void initializeLevel(){
// Set Gameplay variables
spawnBubbles = true;
destroyAllBubbles = false;
specialBubblePresent = false;
movementFroze = false;
// Update HUD
updateHUD();
}
/**
* Loose a life and remove all bubbles
*/
public void looseLife(){
player.lives--;
gameObjectManager.getLivesLabel().setLifeCount(player.lives);
gameObjectManager.getBubbles().clear();
specialBubblePresent = false;
spawnBubbles = true;
gameObjectManager.getGameOverlay().flashScreen(Color.red,100);
}
/**
* Switch to the next level or end the game if every level was played.
*/
private void nextLevel(){
try {
level = levelManager.getNextLevel();
initializeLevel();
} catch (LevelManager.NoMoreLevelsAvailableException e) {
gameView.showEndScreen("You won!");
boolean easyDifficulty = gameView.showSimpleStartScreen("Super Pang World","Start the game");
initializeGame(easyDifficulty);
initializeLevel();
}
}
/**
* Update all HUD elements
*/
private void updateHUD(){
gameObjectManager.getLevelLabel().setLevel(level.number); gameObjectManager.getLevelLabel().setLevel(level.number);
gameObjectManager.getLivesLabel().setLifeCount(player.lives); gameObjectManager.getLivesLabel().setLifeCount(player.lives);
gameObjectManager.getScoreLabel().setScore(player.score); gameObjectManager.getScoreLabel().setScore(player.score);
double progress = (player.score- level.neededOverallScore+ level.neededLevelScore * 1.0) / level.neededLevelScore;
gameObjectManager.getLevelProgressBar().setLevelProgress(Math.min((int)(progress*100),100));
} }
/** /**
* Handle the gameplay. * Handle the gameplay.
*/ */
public void updateGamePlay(){ public void updateGamePlay(){
if(gameView.timerExpired("SpawnBubble","GamePlayManager")){ updateHUD();
RoundBubble roundBubble = new RoundBubble(gameView, new ArrayList<>(Collections.singletonList(gameObjectManager.getPlayerObject())));
roundBubble.setGamePlayManager(this); if(player.score>=level.neededOverallScore){
gameObjectManager.getBubbles().add(roundBubble); nextLevel();
gameView.setTimer("SpawnBubble","GamePlayManager",30000000); }
if(gameView.timerExpired("SpawnBubble","GamePlayManager" ) && spawnBubbles && !movementFroze){
addRandomBubble();
gameView.setTimer("SpawnBubble","GamePlayManager",3000);
}
if(movementFroze && gameView.timerExpired("MovementFroze","GamePlayManager")){
movementFroze = false;
}
if(destroyAllBubbles && gameView.timerExpired("DestroyAllBubbles","GamePlayManager")){
List<Bubble> bubblesCopy = new ArrayList<>(gameObjectManager.getBubbles());
for(Bubble bubble: bubblesCopy){
destroy(bubble);
}
gameView.setTimer("DestroyAllBubbles","GamePlayManager",500);
} }
} }
/** /**
* Shoot a harpoon starting at a given position. * Start the timer to destroy all bubbles.
*
* @param startPosition the start position
* @return if a harpoon was shot or not
*/ */
public boolean shootHarpoon(Position startPosition){ public void destroyAllBubbles(){
if(gameView.timerExpired("ShootHarpoon","GamePlayManager") && gameObjectManager.getHarpoons().size()<2){ gameView.setTimer("DestroyAllBubbles","GamePlayManager",500);
ArrayList<CollidableGameObject> collidableGameObjects = new ArrayList<>(gameObjectManager.getBubbles()); spawnBubbles = false;
Harpoon harpoon = new Harpoon(gameView,collidableGameObjects); destroyAllBubbles = true;
harpoon.getPosition().setTo(startPosition.x+16, startPosition.y-15); movementFroze = false;
harpoon.setGamePlayManager(this); }
gameObjectManager.getHarpoons().add(harpoon);
gameView.setTimer("ShootHarpoon","GamePlayManager",300); /**
return true; * Freeze the bubble movement.
} *
return false; * @param freezeTime the freeze duration
*/
public void freezeMovement(int freezeTime){
movementFroze = true;
gameView.setTimer("MovementFroze","GamePlayManager",freezeTime);
} }
/** /**
@ -97,40 +189,21 @@ public class GamePlayManager {
* @param bubble the bubble to remove * @param bubble the bubble to remove
*/ */
public void destroy(Bubble bubble){ public void destroy(Bubble bubble){
if(gameObjectManager.getBubbles().contains(bubble)) { bubble.destroy();
if (bubble.getSize() > 1.25) { gameObjectManager.getBubbles().remove(bubble);
addRandomBubble(bubble.getSize() / 2, bubble.getSpeedInPixel(), bubble.getPosition(), false); player.score += bubble.getBurstScore();
addRandomBubble(bubble.getSize() / 2, bubble.getSpeedInPixel(), bubble.getPosition(), false);
}
gameObjectManager.getBubbles().remove(bubble);
player.score += bubble.getBurstScore();
updateHud();
if(player.score>=level.neededOverallScore){
switchLevel();
}
}
} }
/** /**
* Update all HUD elements * Destroy a bubble colliding with a harpoon.
*
* @param harpoon the harpoon
* @param bubble the bubble
*/ */
private void updateHud(){ public void destroy(Harpoon harpoon, Bubble bubble){
gameObjectManager.getScoreLabel().setScore(player.score); if(gameObjectManager.getHarpoons().contains(harpoon) && gameObjectManager.getBubbles().contains(bubble)){
double progress = (player.score- level.neededOverallScore+ level.neededLevelScore * 1.0) / level.neededLevelScore; destroy(harpoon);
gameObjectManager.getLevelProgressBar().setLevelProgress(Math.min((int)(progress*100),100)); destroy(bubble);
gameObjectManager.getLevelLabel().setLevel(level.number);
}
/**
* Switch the current level.
*/
private void switchLevel(){
if(level.number==99){
System.out.println("You won!");
}else{
level = new Level(level.number+1,10000,(level.number+1)*10000);
updateHud();
} }
} }
@ -139,21 +212,15 @@ public class GamePlayManager {
* - 60% normal bubble * - 60% normal bubble
* - 30% hexagonal bubble * - 30% hexagonal bubble
* - 10% special bubble * - 10% special bubble
*
* TODO: Integrate all different bubbles, not only the normal red bubble
*
* @param size the size of the bubble
* @param speedInPixel the speed of the bubble
* @param position the position of the bubble
* @param spawning the flag if the bubble gets created in spawning mode
*/ */
private void addRandomBubble(double size, double speedInPixel, Position position, boolean spawning){ private void addRandomBubble(){
int randomNumber = random.nextInt(59); int randomNumber = specialBubblePresent ? random.nextInt(90) : random.nextInt(100);
ArrayList<CollidableGameObject> collidableGameObjects = new ArrayList<>(); ArrayList<CollidableGameObject> collidableGameObjects = new ArrayList<>();
collidableGameObjects.add(gameObjectManager.getPlayerObject()); collidableGameObjects.add(gameObjectManager.getPlayerObject());
collidableGameObjects.addAll(gameObjectManager.getHarpoons()); collidableGameObjects.addAll(gameObjectManager.getHarpoons());
if(randomNumber<60){ if(randomNumber<60){
RoundBubble roundBubble = new RoundBubble(gameView, collidableGameObjects,size,speedInPixel,position,spawning); RoundBubble roundBubble = new RoundBubble(gameView, collidableGameObjects);
roundBubble.setGamePlayManager(this); roundBubble.setGamePlayManager(this);
gameObjectManager.getBubbles().add(roundBubble); gameObjectManager.getBubbles().add(roundBubble);
}else if(randomNumber<90){ }else if(randomNumber<90){
@ -164,15 +231,42 @@ public class GamePlayManager {
SpecialBubble specialBubble = new SpecialBubble(gameView, collidableGameObjects); SpecialBubble specialBubble = new SpecialBubble(gameView, collidableGameObjects);
specialBubble.setGamePlayManager(this); specialBubble.setGamePlayManager(this);
gameObjectManager.getBubbles().add(specialBubble); gameObjectManager.getBubbles().add(specialBubble);
specialBubblePresent = true;
} }
} }
/** /**
* Loose a life and remove all bubbles * Add bubbles to the game.
*
* @param bubbles the bubbles
*/ */
public void looseLife(){ public void addBubbles(Bubble... bubbles){
player.lives--; ArrayList<CollidableGameObject> collidableGameObjects = new ArrayList<>();
gameObjectManager.getLivesLabel().setLifeCount(player.lives); collidableGameObjects.add(gameObjectManager.getPlayerObject());
gameObjectManager.getBubbles().clear(); collidableGameObjects.addAll(gameObjectManager.getHarpoons());
for(Bubble bubble: bubbles){
bubble.setGamePlayManager(this);
bubble.addCollidableGameObject(collidableGameObjects);
gameObjectManager.getBubbles().add(bubble);
}
}
/**
* Shoot a harpoon starting at a given position.
*
* @param startPosition the start position
* @return if a harpoon was shot or not
*/
public boolean shootHarpoon(Position startPosition){
if(gameView.timerExpired("ShootHarpoon","GamePlayManager") && gameObjectManager.getHarpoons().size()<2){
Harpoon harpoon = new Harpoon(gameView,new ArrayList<>());
harpoon.getPosition().setTo(startPosition.x+16, startPosition.y-15);
harpoon.setGamePlayManager(this);
gameObjectManager.getHarpoons().add(harpoon);
gameObjectManager.getBubbles().forEach(b -> b.addCollidableGameObject(Collections.singletonList(harpoon)));
gameView.setTimer("ShootHarpoon","GamePlayManager",300);
return true;
}
return false;
} }
} }

View File

@ -0,0 +1,58 @@
package de.thdeg.greiner.superpangworld.game.managers;
import de.thdeg.greiner.superpangworld.graphics.helper.Level;
import java.util.ArrayDeque;
import java.util.Queue;
/**
* The level manager for creating and providing levels.
*/
public class LevelManager {
/** The queue to store the generated levels */
private final Queue<Level> levels;
/**
* Create a level manager.
*
* @param easyDifficulty the difficulty
*/
public LevelManager(boolean easyDifficulty){
levels = new ArrayDeque<>();
generateLevels(easyDifficulty);
}
/**
* Get the next level. If there a no remaining levels an exception is thrown.
*
* @return the next level
* @throws NoMoreLevelsAvailableException if there is no level remaining
*/
public Level getNextLevel() throws NoMoreLevelsAvailableException {
if(levels.isEmpty()){
throw new NoMoreLevelsAvailableException();
}else{
return levels.poll();
}
}
/**
* Generate the levels according to the difficulty.
*
* @param easyDifficulty the difficulty
*/
private void generateLevels(boolean easyDifficulty){
int overallScoreNeeded = 0;
for(int i=0;i<3;i++){
int scoreNeeded = easyDifficulty ? 1000 + (100*i) : 10000 + (1000*i);
overallScoreNeeded += scoreNeeded;
levels.add(new Level(i+1, scoreNeeded,overallScoreNeeded));
}
}
/**
* The exception in case no next level is remaining.
*/
public static class NoMoreLevelsAvailableException extends Exception{}
}

View File

@ -4,6 +4,7 @@ import de.thdeg.greiner.superpangworld.gameview.GameView;
import de.thdeg.greiner.superpangworld.graphics.helper.HelperValues; import de.thdeg.greiner.superpangworld.graphics.helper.HelperValues;
import de.thdeg.greiner.superpangworld.graphics.moveable.Harpoon; import de.thdeg.greiner.superpangworld.graphics.moveable.Harpoon;
import de.thdeg.greiner.superpangworld.graphics.moveable.PlayerObject; import de.thdeg.greiner.superpangworld.graphics.moveable.PlayerObject;
import de.thdeg.greiner.superpangworld.graphics.moveable.SpecialBubble;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.util.*; import java.util.*;
@ -13,21 +14,20 @@ import java.util.*;
*/ */
public abstract class Bubble extends CollidingGameObject implements MovingGameObject{ public abstract class Bubble extends CollidingGameObject implements MovingGameObject{
private static final Point2D VELOCITY_RIGHT = new Point2D.Double(3,1); /** The velocity of a normal right movement */
private static final Point2D VELOCITY_LEFT = new Point2D.Double(-3,1); public static final Point2D VELOCITY_RIGHT = new Point2D.Double(3,1);
/** The velocity of a normal left movement */
/** Random generator */ public static final Point2D VELOCITY_LEFT = new Point2D.Double(-3,1);
private final Random random; /** The random generator */
protected final Random random;
/** Flag, if the bubble is in the spawning phase */ /** Flag, if the bubble is in the spawning phase */
private boolean spawning; private boolean spawning;
/** Spawning speed */ /** Spawning speed */
private int spawnSpeed; private int spawnSpeed;
/** Unique id for the spawn event timer */ /** Unique id for the spawn event timer */
private final long spawnID; protected final long spawnID;
/** Temporary movement action */ /** The current bubble velocity */
private Position nextPosition; protected Point2D velocity;
private Point2D velocity;
/** /**
* Create a bubble with default values. * Create a bubble with default values.
@ -50,7 +50,7 @@ public abstract class Bubble extends CollidingGameObject implements MovingGameOb
spawnSpeed = 500; spawnSpeed = 500;
gameView.setTimer("spawn"+spawnID,"bubble"+spawnID,spawnSpeed); gameView.setTimer("spawn"+spawnID,"bubble"+spawnID,spawnSpeed);
velocity = new Point2D.Double(-3.5,1.4); velocity = random.nextBoolean() ? VELOCITY_LEFT : VELOCITY_RIGHT;
} }
@ -63,25 +63,28 @@ public abstract class Bubble extends CollidingGameObject implements MovingGameOb
* @param speedInPixel the speed in pixel per tick * @param speedInPixel the speed in pixel per tick
* @param position the position * @param position the position
* @param spawning flag, if the bubble is in the spawning phase * @param spawning flag, if the bubble is in the spawning phase
* @param moveLeftToRight flag, if the bubble is moving left to right * @param velocity the initial velocity of the bubble
*/ */
public Bubble(GameView gameView, ArrayList<CollidableGameObject> objectsToCollideWith, double size, double speedInPixel, Position position, boolean spawning, boolean moveLeftToRight){ public Bubble(GameView gameView, ArrayList<CollidableGameObject> objectsToCollideWith, double size, double speedInPixel, Position position, boolean spawning, Point2D velocity){
super(gameView,objectsToCollideWith); super(gameView,objectsToCollideWith);
random = new Random();
rotation = 90; rotation = 90;
this.size = size; this.size = size;
width = (int) (12 * size);
height = (int) (12 * size);
this.speedInPixel = speedInPixel; this.speedInPixel = speedInPixel;
this.position.setTo(position.x,position.y); this.position.setTo(position.x,position.y);
spawnID = UUID.randomUUID().getLeastSignificantBits();
random = new Random();
this.spawning = spawning; this.spawning = spawning;
this.velocity = velocity;
spawnID = UUID.randomUUID().getLeastSignificantBits();
width = (int) (12 * size);
height = (int) (12 * size);
if(spawning){ if(spawning){
spawnSpeed = 500; spawnSpeed = 500;
gameView.setTimer("spawn"+spawnID,"bubble"+spawnID,spawnSpeed); gameView.setTimer("spawn"+spawnID,"bubble"+spawnID,spawnSpeed);
} }
velocity = new Point2D.Double(3.5,1.4);
} }
/** /**
@ -107,16 +110,18 @@ public abstract class Bubble extends CollidingGameObject implements MovingGameOb
// Wall bounce // Wall bounce
if((newLocation.getX() >= GameView.WIDTH - width - HelperValues.FRAME_BORDER_WIDTH) || newLocation.getX() <= HelperValues.FRAME_BORDER_WIDTH){ if((newLocation.getX() >= GameView.WIDTH - width - HelperValues.FRAME_BORDER_WIDTH) || newLocation.getX() <= HelperValues.FRAME_BORDER_WIDTH){
velocity = new Point2D.Double(velocity.getX() * -1.0, velocity.getY()); velocity = new Point2D.Double(velocity.getX() * -1.0, velocity.getY());
if(this instanceof SpecialBubble){
SpecialBubble specialBubble = (SpecialBubble) this;
specialBubble.freezeState = !specialBubble.freezeState;
}
} }
// Floor bounce
// Bottom bounce
if(newLocation.getY() >= HelperValues.FRAME_WINDOW_HEIGHT + HelperValues.FRAME_BORDER_WIDTH - width){ if(newLocation.getY() >= HelperValues.FRAME_WINDOW_HEIGHT + HelperValues.FRAME_BORDER_WIDTH - width){
velocity = new Point2D.Double(velocity.getX(), velocity.getY() * -0.95); velocity = new Point2D.Double(velocity.getX(), velocity.getY() * -0.95);
newLocation = new Point2D.Double(newLocation.getX(), HelperValues.FRAME_WINDOW_HEIGHT + HelperValues.FRAME_BORDER_WIDTH - width); newLocation = new Point2D.Double(newLocation.getX(), HelperValues.FRAME_WINDOW_HEIGHT + HelperValues.FRAME_BORDER_WIDTH - width);
} }
position.setTo(newLocation.getX(), newLocation.getY()); position.setTo(newLocation.getX(), newLocation.getY());
} }
} }
@ -126,7 +131,7 @@ public abstract class Bubble extends CollidingGameObject implements MovingGameOb
* @return the score to add * @return the score to add
*/ */
public int getBurstScore(){ public int getBurstScore(){
return (int)(size*100); return (int)(size*1000);
} }
/** /**
@ -138,31 +143,17 @@ public abstract class Bubble extends CollidingGameObject implements MovingGameOb
return this.size; return this.size;
} }
/**
* Get the speed in pixel per tick.
*
* @return the speed in pixel per tick
*/
public double getSpeedInPixel(){
return speedInPixel;
}
/**
* Get the flag if the bubble is in its spawning phase.
*
* @return <code>true</code> if in spawning phase, else <code>false</code>
*/
public boolean isSpawning(){
return spawning;
}
@Override @Override
public void reactToCollision(CollidableGameObject otherObject) { public void reactToCollision(CollidableGameObject otherObject) {
if(otherObject instanceof PlayerObject){ if (otherObject instanceof PlayerObject && !gamePlayManager.movementFroze) {
gamePlayManager.looseLife(); gamePlayManager.looseLife();
}else if(otherObject instanceof Harpoon && !spawning){ } else if (otherObject instanceof Harpoon && !spawning) {
gamePlayManager.destroy((Harpoon) otherObject); gamePlayManager.destroy((Harpoon) otherObject, this);
gamePlayManager.destroy(this);
} }
} }
/**
* Destroys the bubble.
*/
public abstract void destroy();
} }

View File

@ -3,6 +3,7 @@ package de.thdeg.greiner.superpangworld.graphics.base;
import de.thdeg.greiner.superpangworld.gameview.GameView; import de.thdeg.greiner.superpangworld.gameview.GameView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
/** /**
* An object that can actively collide with other objects, e.g. a shot. * An object that can actively collide with other objects, e.g. a shot.
@ -44,4 +45,8 @@ public abstract class CollidingGameObject extends CollidableGameObject {
protected final boolean collidesWith(CollidableGameObject other) { protected final boolean collidesWith(CollidableGameObject other) {
return this.hitBox.intersects(other.hitBox); return this.hitBox.intersects(other.hitBox);
} }
public void addCollidableGameObject(List<CollidableGameObject> objects){
objectsToCollideWith.addAll(objects);
}
} }

View File

@ -27,7 +27,6 @@ public abstract class GameObject implements Cloneable{
/** The responsible GamePlayManager */ /** The responsible GamePlayManager */
protected GamePlayManager gamePlayManager; protected GamePlayManager gamePlayManager;
/** /**
* Create a game object with default values. * Create a game object with default values.
* *
@ -52,8 +51,10 @@ public abstract class GameObject implements Cloneable{
* Update the status and the position is applicable. * Update the status and the position is applicable.
*/ */
public void update(){ public void update(){
if(this instanceof MovingGameObject){ if(this instanceof MovingGameObject ){
((MovingGameObject)this).updatePosition(); if(!(Bubble.class.isAssignableFrom(this.getClass()) && gamePlayManager.movementFroze)){
((MovingGameObject)this).updatePosition();
}
} }
updateStatus(); updateStatus();
} }

View File

@ -2,11 +2,16 @@ package de.thdeg.greiner.superpangworld.graphics.helper;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
/**
* Storage for helper values to be used throughout the program.
*/
public class HelperValues { public class HelperValues {
public static final int FRAME_BORDER_WIDTH = 8; /** The width of the frame border */
public static final int FRAME_WINDOW_HEIGHT = 432; public static final double FRAME_BORDER_WIDTH = 8;
/** The height of the main game window */
public static final double FRAME_WINDOW_HEIGHT = 432;
/** The games gravity */
public static Point2D BUBBLE_GRAVITY = new Point2D.Double(0,0.2); public static Point2D BUBBLE_GRAVITY = new Point2D.Double(0,0.2);
} }

View File

@ -0,0 +1,61 @@
package de.thdeg.greiner.superpangworld.graphics.immovable;
import de.thdeg.greiner.superpangworld.gameview.GameView;
import de.thdeg.greiner.superpangworld.graphics.base.GameObject;
import de.thdeg.greiner.superpangworld.graphics.helper.HelperValues;
import java.awt.*;
/**
* The game overlay for display text or color on the screen.
*/
public class GameOverlay extends GameObject {
/** The message to display */
private String message;
/** The color to display */
private Color color;
/**
* Create the game overlay.
*
* @param gameView the game view to display the elements on
*/
public GameOverlay(GameView gameView){
super(gameView);
}
/**
* Show a message on the screen.
*
* @param message the message to display
*/
public void showMessage(String message){
gameView.setTimer("DisplayMessage","Overlay",3000);
this.message = message;
}
/**
* Display a color of a set amount of time on the main game screen.
*
* @param color the color to display
* @param duration the duration to display
*/
public void flashScreen(Color color, int duration){
gameView.setTimer("FlashScreen","Overlay",duration);
this.color = color;
}
@Override
public void addToCanvas() {
if(!gameView.timerExpired("DisplayMessage","Overlay")){
gameView.addTextToCanvas(message,GameView.WIDTH/2.0, GameView.HEIGHT/2.0,30, Color.BLACK,0);
}
if(!gameView.timerExpired("FlashScreen","Overlay")){
gameView.addRectangleToCanvas(HelperValues.FRAME_BORDER_WIDTH,HelperValues.FRAME_BORDER_WIDTH,GameView.WIDTH - (2*HelperValues.FRAME_BORDER_WIDTH),HelperValues.FRAME_WINDOW_HEIGHT+1,1,true,color);
}
}
@Override
protected void updateStatus() {}
}

View File

@ -5,13 +5,13 @@ import de.thdeg.greiner.superpangworld.graphics.base.*;
import java.awt.*; import java.awt.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.UUID;
/** /**
* A harpoon which can be fired upwards by the player. * A harpoon which can be fired upwards by the player.
*/ */
public class Harpoon extends CollidingGameObject implements MovingGameObject { public class Harpoon extends CollidingGameObject implements MovingGameObject {
/** The pixel string representing a harpoon */
private final static String HARPOON = private final static String HARPOON =
" B \n"+ " B \n"+
" BBB \n"+ " BBB \n"+
@ -27,7 +27,7 @@ public class Harpoon extends CollidingGameObject implements MovingGameObject {
*/ */
public Harpoon(GameView gameView, ArrayList<CollidableGameObject> objectsToCollideWith){ public Harpoon(GameView gameView, ArrayList<CollidableGameObject> objectsToCollideWith){
super(gameView, objectsToCollideWith); super(gameView, objectsToCollideWith);
speedInPixel = 1; speedInPixel = 2;
size = 3; size = 3;
width = (int) (7 * size); width = (int) (7 * size);
height = (int) (5 * size); height = (int) (5 * size);
@ -46,13 +46,7 @@ public class Harpoon extends CollidingGameObject implements MovingGameObject {
@Override @Override
public void reactToCollision(CollidableGameObject otherObject) { public void reactToCollision(CollidableGameObject otherObject) {
if(Bubble.class.isAssignableFrom(otherObject.getClass())){
Bubble bubble = (Bubble) otherObject;
if(!bubble.isSpawning()){
gamePlayManager.destroy((Bubble)otherObject);
gamePlayManager.destroy(this);
}
}
} }
/** /**

View File

@ -3,7 +3,10 @@ package de.thdeg.greiner.superpangworld.graphics.moveable;
import de.thdeg.greiner.superpangworld.gameview.GameView; import de.thdeg.greiner.superpangworld.gameview.GameView;
import de.thdeg.greiner.superpangworld.graphics.base.Bubble; import de.thdeg.greiner.superpangworld.graphics.base.Bubble;
import de.thdeg.greiner.superpangworld.graphics.base.CollidableGameObject; import de.thdeg.greiner.superpangworld.graphics.base.CollidableGameObject;
import de.thdeg.greiner.superpangworld.graphics.base.Position;
import java.awt.*;
import java.awt.geom.Point2D;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
@ -20,25 +23,36 @@ public class HexagonalBubble extends Bubble {
public HexagonalBubble(GameView gameView, ArrayList<CollidableGameObject> objectsToCollideWith) { public HexagonalBubble(GameView gameView, ArrayList<CollidableGameObject> objectsToCollideWith) {
super(gameView,objectsToCollideWith); super(gameView,objectsToCollideWith);
size=0.6; size=1;
speedInPixel = 2; speedInPixel = 2;
width = (int) (128 * size); width = (int) (128 * size);
height = (int) (128 * size); height = (int) (128 * size);
this.hitBox.width = width-20; this.hitBox.width = width;
this.hitBox.height = height-20; this.hitBox.height = height;
}
public HexagonalBubble(GameView gameView, ArrayList<CollidableGameObject> objectsToCollideWith, double size, double speedInPixel, Position position, boolean spawning, Point2D velocity){
super(gameView, objectsToCollideWith, size, speedInPixel, position, spawning, velocity);
width = (int) (128 * size);
height = (int) (128 * size);
this.hitBox.width = width;
this.hitBox.height = height;
} }
@Override @Override
protected void updateHitBoxPosition() { protected void updateHitBoxPosition() {
hitBox.x = (int) (position.x+10); hitBox.x = (int) position.x;
hitBox.y = (int) (position.y+10); hitBox.y = (int) position.y;
} }
@Override @Override
public void addToCanvas() { public void addToCanvas() {
gameView.addImageToCanvas("hexagon.png",position.x,position.y,size,rotation); gameView.addImageToCanvas("hexagon.png",position.x,position.y,size,rotation);
gameView.addRectangleToCanvas(hitBox.x,hitBox.y,hitBox.width,hitBox.height,1,false, Color.GREEN);
} }
@Override @Override
@ -51,4 +65,20 @@ public class HexagonalBubble extends Bubble {
super.updatePosition(); super.updatePosition();
rotation = (rotation + 1) % 360; rotation = (rotation + 1) % 360;
} }
@Override
public void destroy() {
if (size > 0.125) {
Position positionLeft = new Position(position.x - (width/2.0/2.0), position.y);
Position positionRight = new Position(position.x + width - (width/2.0/2.0), position.y);
Point2D.Double velocityLeft = new Point2D.Double(Math.min(this.velocity.getX(),this.velocity.getX() * -1.0),Math.min(this.velocity.getY(),this.velocity.getY() * -1.0));
Point2D.Double velocityRight = new Point2D.Double(Math.max(this.velocity.getX(),this.velocity.getX() * -1.0),Math.min(this.velocity.getY(),this.velocity.getY() * -1.0));
Bubble bubbleLeft = new HexagonalBubble(gameView, new ArrayList<>(), size/2, speedInPixel, positionLeft, false, velocityLeft);
Bubble bubbleRight = new HexagonalBubble(gameView, new ArrayList<>(), size/2, speedInPixel, positionRight, false, velocityRight);
gamePlayManager.addBubbles(bubbleLeft, bubbleRight);
}
}
} }

View File

@ -3,7 +3,6 @@ package de.thdeg.greiner.superpangworld.graphics.moveable;
import de.thdeg.greiner.superpangworld.gameview.GameView; import de.thdeg.greiner.superpangworld.gameview.GameView;
import de.thdeg.greiner.superpangworld.graphics.base.CollidableGameObject; import de.thdeg.greiner.superpangworld.graphics.base.CollidableGameObject;
import java.awt.*;
import java.util.Objects; import java.util.Objects;
/** /**
@ -13,10 +12,11 @@ public class PlayerObject extends CollidableGameObject implements Cloneable {
/** Flag, if the player is shooting */ /** Flag, if the player is shooting */
private boolean shooting; private boolean shooting;
/** Flag, if the player is moving right */
private boolean movingRight; private boolean movingRight;
/** Flag, if the player is moving left */
private boolean movingLeft; private boolean movingLeft;
/** Flag, to rotate between standing and walking when moving around */
private boolean movingStanding; private boolean movingStanding;
/** /**

View File

@ -5,6 +5,7 @@ import de.thdeg.greiner.superpangworld.graphics.base.Bubble;
import de.thdeg.greiner.superpangworld.graphics.base.CollidableGameObject; import de.thdeg.greiner.superpangworld.graphics.base.CollidableGameObject;
import de.thdeg.greiner.superpangworld.graphics.base.Position; import de.thdeg.greiner.superpangworld.graphics.base.Position;
import java.awt.geom.Point2D;
import java.util.ArrayList; import java.util.ArrayList;
@ -28,6 +29,26 @@ public class RoundBubble extends Bubble {
" RRRWWR \n"+ " RRRWWR \n"+
" RRRR \n"; " RRRR \n";
/** The pixel art for the bubble */
private final static String WHITE_BUBBLE =
" WWWW \n"+
" WWWWWW \n"+
" WWWWWWWW \n"+
" WWWWWWWWWW \n"+
"WWWWWWWWWWWW\n"+
"WWWWWWWWWWWW\n"+
"WWWWWWWWWWWW\n"+
"WWWWWWWWWWWW\n"+
" WWWWWWWWWW \n"+
" WWWWWWWW \n"+
" WWWWWW \n"+
" WWWW \n";
/** Flag for switching between red and white if the ball should blink */
private boolean redBubble;
/** Flag, if the bubble freezes the time when destroyed */
protected boolean timeFreeze;
/** /**
* Create a round bubble with default values. * Create a round bubble with default values.
* *
@ -38,6 +59,7 @@ public class RoundBubble extends Bubble {
super(gameView,objectsToCollideWith); super(gameView,objectsToCollideWith);
this.hitBox.width = width-20; this.hitBox.width = width-20;
this.hitBox.height = height-20; this.hitBox.height = height-20;
redBubble = true;
} }
/** /**
@ -49,11 +71,37 @@ public class RoundBubble extends Bubble {
* @param speedInPixel the speed in Pixel per tick * @param speedInPixel the speed in Pixel per tick
* @param position the Position * @param position the Position
* @param spawning flag, if the bubble is spawning or not * @param spawning flag, if the bubble is spawning or not
* @param velocity the initial velocity of the bubble
*/ */
public RoundBubble(GameView gameView, ArrayList<CollidableGameObject> objectsToCollideWith, double size, double speedInPixel, Position position, boolean spawning){ public RoundBubble(GameView gameView, ArrayList<CollidableGameObject> objectsToCollideWith, double size, double speedInPixel, Position position, boolean spawning, Point2D velocity){
super(gameView, objectsToCollideWith, size, speedInPixel, position, spawning); super(gameView, objectsToCollideWith, size, speedInPixel, position, spawning, velocity);
this.hitBox.width = (int) (width*0.8); this.hitBox.width = (int) (width*0.8);
this.hitBox.height = (int) (height*0.8); this.hitBox.height = (int) (height*0.8);
redBubble = true;
if(size < 2.5){
if(random.nextInt(100)<10){
timeFreeze = true;
}
}
}
@Override
public void destroy() {
if (size > 1.25) {
Position positionLeft = new Position(position.x - (width/2.0/2.0), position.y);
Position positionRight = new Position(position.x + width - (width/2.0/2.0), position.y);
Point2D.Double velocityLeft = new Point2D.Double(Math.min(this.velocity.getX(),this.velocity.getX() * -1.0),Math.min(this.velocity.getY(),this.velocity.getY() * -1.0));
Point2D.Double velocityRight = new Point2D.Double(Math.max(this.velocity.getX(),this.velocity.getX() * -1.0),Math.min(this.velocity.getY(),this.velocity.getY() * -1.0));
Bubble bubbleLeft = new RoundBubble(gameView, new ArrayList<>(), size/2, speedInPixel, positionLeft, false, velocityLeft);
Bubble bubbleRight = new RoundBubble(gameView, new ArrayList<>(), size/2, speedInPixel, positionRight, false, velocityRight);
gamePlayManager.addBubbles(bubbleLeft, bubbleRight);
}else if(timeFreeze){
gamePlayManager.freezeMovement(1000);
System.out.println("FREEZING");
}
} }
@Override @Override
@ -67,7 +115,19 @@ public class RoundBubble extends Bubble {
*/ */
@Override @Override
public void addToCanvas(){ public void addToCanvas(){
gameView.addBlockImageToCanvas(RED_BUBBLE, getPosition().x, getPosition().y,size, rotation); if(gameView.timerExpired("BubbleBlink"+spawnID,"Bubble"+spawnID)){
redBubble = !redBubble;
gameView.setTimer("BubbleBlink"+spawnID,"Bubble"+spawnID,100);
}
if(!timeFreeze){
gameView.addBlockImageToCanvas(RED_BUBBLE, getPosition().x, getPosition().y,size, rotation);
}else{
if(redBubble){
gameView.addBlockImageToCanvas(RED_BUBBLE, getPosition().x, getPosition().y,size, rotation);
}else{
gameView.addBlockImageToCanvas(WHITE_BUBBLE, getPosition().x, getPosition().y,size, rotation);
}
}
} }
@Override @Override

View File

@ -3,6 +3,7 @@ package de.thdeg.greiner.superpangworld.graphics.moveable;
import de.thdeg.greiner.superpangworld.gameview.GameView; import de.thdeg.greiner.superpangworld.gameview.GameView;
import de.thdeg.greiner.superpangworld.graphics.base.Bubble; import de.thdeg.greiner.superpangworld.graphics.base.Bubble;
import de.thdeg.greiner.superpangworld.graphics.base.CollidableGameObject; import de.thdeg.greiner.superpangworld.graphics.base.CollidableGameObject;
import de.thdeg.greiner.superpangworld.graphics.base.Position;
import java.util.ArrayList; import java.util.ArrayList;
@ -26,6 +27,51 @@ public class SpecialBubble extends Bubble {
" GGGWWG \n"+ " GGGWWG \n"+
" GGGG \n"; " GGGG \n";
/** The pixel art for the bubble */
private final static String STAR =
" OO \n"+
" OYYO \n"+
" OYYO \n"+
" OOYYOO \n"+
"OOOOOYYYYOOOOO\n"+
"OYYYYYYYYYYYYO\n"+
" OOYYYYYYYOO \n"+
" OOYYYYYYOO \n"+
" OYYYYYYO \n"+
" OYYYYYYYYO \n"+
" OYYYOOYYYO \n"+
" OYYYO OYYYO \n"+
" OYOO OOYO \n"+
" OO OO \n";
/** The pixel art for the bubble */
private final static String CLOCK =
" YY YY \n"+
"YYYY OOO YYYY\n"+
"YYYOOWWWOOYYY\n"+
" YOBWWWWWWOY \n"+
" OWBWWWBWO \n"+
" OWWWBWBWWWO \n"+
" OWWWWBWWWWO \n"+
" OWWWWWWWWWO \n"+
" OWWWWWWWO \n"+
" OWWWWWWWO \n"+
" OOWWWOO \n"+
" OOOOO \n"+
" O O \n"+
" O O \n";
/** Flag, if the bubble is in freeze state or in burst state */
public boolean freezeState;
/** The height of the star pixel art */
private final static int STAR_HEIGHT = 14;
/** The width of the star pixel art */
private final static int STAR_WIDTH = 14;
/** The height of the clock pixel art */
private final static int CLOCK_HEIGHT = 14;
/** The width of the clock pixel art */
private final static int CLOCK_WIDTH = 13;
/** /**
* Create a special bubble with default values. * Create a special bubble with default values.
* *
@ -34,11 +80,21 @@ public class SpecialBubble extends Bubble {
*/ */
public SpecialBubble(GameView gameView, ArrayList<CollidableGameObject> objectsToCollideWith) { public SpecialBubble(GameView gameView, ArrayList<CollidableGameObject> objectsToCollideWith) {
super(gameView,objectsToCollideWith); super(gameView,objectsToCollideWith);
this.freezeState = true;
this.hitBox.width = width-20; this.hitBox.width = width-20;
this.hitBox.height = height-20; this.hitBox.height = height-20;
} }
@Override
public void destroy() {
gamePlayManager.specialBubblePresent = false;
if(freezeState){
gamePlayManager.freezeMovement(3000);
}else{
gamePlayManager.destroyAllBubbles();
}
}
@Override @Override
protected void updateHitBoxPosition() { protected void updateHitBoxPosition() {
hitBox.x = (int) (position.x+10); hitBox.x = (int) (position.x+10);
@ -48,6 +104,15 @@ public class SpecialBubble extends Bubble {
@Override @Override
public void addToCanvas() { public void addToCanvas() {
gameView.addBlockImageToCanvas(GREEN_BUBBLE,position.x,position.y,size, rotation); gameView.addBlockImageToCanvas(GREEN_BUBBLE,position.x,position.y,size, rotation);
Position iconPosition = new Position(position.x + (width/2.0),position.y + (width/2.0));
int IconBlockSize = 3;
if(freezeState){
gameView.addBlockImageToCanvas(CLOCK,iconPosition.x - (CLOCK_WIDTH*IconBlockSize/2.0),iconPosition.y - (CLOCK_HEIGHT*IconBlockSize/2.0),IconBlockSize, 0);
}else{
gameView.addBlockImageToCanvas(STAR,iconPosition.x - (STAR_WIDTH*IconBlockSize/2.0),iconPosition.y - (STAR_HEIGHT*IconBlockSize/2.0),IconBlockSize, 0);
}
} }
@Override @Override