Refactor Part 1: ArrayLists

This commit is contained in:
naomi 2025-05-14 01:35:55 +00:00
parent 50cc8e2a7b
commit 475af2c03e
11 changed files with 938 additions and 775 deletions

View file

@ -1,12 +1,15 @@
package dev.boyfailure.quajra.vectorbreakout; package dev.boyfailure.quajra.vectorbreakout;
import java.util.Arrays;
public class Ball { public class Ball {
short ballX = 250; short ballX = 250;
short ballY = 300; short ballY = 300;
short ballWidth = 7; short ballWidth = 7;
short ballHeight = 7; short ballHeight = 7;
boolean isActive = false; private boolean isActive = false;
private boolean isInvincible = false;
byte ballColor = 0; byte ballColor = 0;
Hitbox hitBox; Hitbox hitBox;
@ -28,11 +31,13 @@ public class Ball {
public void spawnBall(boolean useChancey) { public void spawnBall(boolean useChancey) {
if (useChancey) { if (useChancey) {
this.ballX += (short) (gameState.chancey * 3); this.ballX += (short) (gameState.getChancey() * 3);
this.ballY += (short) gameState.chancey; this.ballY += (short) gameState.getChancey();
} }
gameState.ballsOnScreen++; gameState.ballsOnScreen++;
this.isActive = true; this.isActive = true;
gameState.lives--;
gameState.textElementList.get(4).setText(String.valueOf(gameState.lives));
} }
public void spawnBall(int x, int y) { public void spawnBall(int x, int y) {
@ -40,6 +45,8 @@ public class Ball {
this.ballY = (short) y; this.ballY = (short) y;
gameState.ballsOnScreen++; gameState.ballsOnScreen++;
this.isActive = true; this.isActive = true;
gameState.lives--;
gameState.textElementList.get(4).setText(String.valueOf(gameState.lives));
} }
public void moveBall() { public void moveBall() {
@ -55,7 +62,7 @@ public class Ball {
switch((byte) bounceType) { switch((byte) bounceType) {
case 0: case 0:
this.speedY = (byte) -(this.speedY); this.speedY = (byte) -(this.speedY);
switch (gameState.chancey) { switch (gameState.getChancey()) {
case 0: break; case 0: break;
case 1: this.speedX++; break; case 1: this.speedX++; break;
case 2: break; case 2: break;
@ -110,25 +117,26 @@ public class Ball {
this.ballY = 32767; this.ballY = 32767;
gameState.ballsOnScreen--; gameState.ballsOnScreen--;
if (gameState.ballsOnScreen <= 0) { if (gameState.ballsOnScreen <= 0) {
gameState.lives--; gameState.ballsOnScreen = 0;
if (gameState.lives <= 0) {
gameState.gameLose();
}
} }
if (gameState.lives <= 0) {
gameState.gameLose();
}
gameState.currentTextElements[1].setText(String.valueOf(gameState.lives));
} }
// Checks to see if the ball is in contact with bricks, paddles, or walls // Checks to see if the ball is in contact with bricks, paddles, or walls
public void checkCollision() { public void checkCollision() {
this.setInvincibilityState(true); // Delay bouncing again for a frame
// Check floor // Check floor
if (this.ballY >= gameState.gameResY) { if (this.ballY >= gameState.getInternalResY()) {
this.destroyBall(); this.destroyBall();
return; return;
} }
// Check ceiling // Check ceiling
else if (this.ballY <= 0) { else if (this.ballY <= 0) {
this.bounce((byte) 0); this.bounce(0);
this.ballY = 0; this.ballY = 0;
this.wallBounceAnimation(); this.wallBounceAnimation();
return; return;
@ -136,105 +144,92 @@ public class Ball {
// Check walls // Check walls
if (this.ballX <= 20) { if (this.ballX <= 20) {
this.bounce((byte) 1); this.bounce(1);
this.ballX = 22; this.ballX = 22;
this.wallBounceAnimation(); this.wallBounceAnimation();
return; return;
} }
else if ((this.ballX + this.ballWidth) >= (gameState.gameResX - 20 - this.ballWidth)) { else if ((this.ballX + this.ballWidth) >= (gameState.getInternalResX() - 20 - this.ballWidth)) {
this.bounce((byte) 1); this.bounce((byte) 1);
this.ballX = (short) (gameState.gameResX - 25 - this.ballWidth); this.ballX = (short) (gameState.getInternalResX() - 25 - this.ballWidth);
this.wallBounceAnimation(); this.wallBounceAnimation();
return; return;
} }
// Check bricks // Check bricks
if (gameState.currentLevel != null) { if (gameState.currentLevel != null) {
for (short i = 0; i < gameState.currentLevel.levelBricks.length; i++) { if (gameState.currentLevel.brickList != null) {
if (gameState.currentLevel.levelBricks[i] != null && !gameState.currentLevel.levelBricks[i].isBroken) { for (Brick brick : gameState.currentLevel.brickList) {
if (this.hitBox.collides(gameState.currentLevel.levelBricks[i].hitBox.bounds)) { if (this.hitBox.collides(brick.hitBox.getBounds())) {
gameState.currentLevel.levelBricks[i].breakBrick(); brick.breakBrick();
this.bounce((byte) 0); this.bounce(0);
return; gameState.brickCollector.add(brick);
return;
} }
} }
} }
} }
// Check paddles // Check paddles
for (byte i = 0; i < gameState.currentPaddles.length; i++) { for (Paddle paddle : gameState.paddleList) {
if (gameState.currentPaddles[i] != null) { if (this.hitBox.collides(paddle.hitBox.getBounds())) {
if (this.hitBox.collides(gameState.currentPaddles[i].hitBox.bounds)) { if (this.hitBox.collides(paddle.hitBoxLeft.getBounds())) {
this.bounce(2);
if (this.hitBox.collides(gameState.currentPaddles[i].hitBoxLeft.bounds)) {
this.bounce(2);
}
else if (this.hitBox.collides(gameState.currentPaddles[i].hitBoxRight.bounds)) {
this.bounce(3);
}
else {
this.bounce(0);
}
this.ballY -= 2;
// Draw particles
byte ballParticleGen = 0;
for (byte ii = 0; ii < gameState.currentParticles.length; ii++) {
if (gameState.currentParticles[ii] == null) {
ballParticleGen++;
switch(ballParticleGen) {
case 1:
gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX, this.ballY - 10, 0, -2, 1, 2, 7, 3);
gameState.currentParticles[ii].spawn(5);
break;
case 2:
gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX - 3, this.ballY - 10, -2, -4, 1, 2, 6, 3);
gameState.currentParticles[ii].spawn(5);
break;
case 3:
gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX + 10, this.ballY - 10, 2, -4, 1, 2, 8, 3);
gameState.currentParticles[ii].spawn(5);
break;
}
if (ballParticleGen < 3) {continue;}
else {return;}
}
}
return;
} }
else if (this.hitBox.collides(paddle.hitBoxRight.getBounds())) {
this.bounce(3);
}
else {
this.bounce(0);
}
this.ballY -= 2;
// Draw particles
Particle particle1, particle2, particle3;
particle1 = new Particle(gameState, 0, this.ballX, this.ballY - 10, 0, -2, 1, 2, 7, 3);
particle2 = new Particle(gameState, 0, this.ballX - 3, this.ballY - 10, -2, -4, 1, 2, 6, 3);
particle3 = new Particle(gameState, 0, this.ballX + 10, this.ballY - 10, 2, -4, 1, 2, 8, 3);
gameState.particleList.add(particle1);
gameState.particleList.add(particle2);
gameState.particleList.add(particle3);
particle1.spawn(5);
particle2.spawn(5);
particle3.spawn(5);
return; // Prevents bouncing multiple times
} }
} }
} }
private void wallBounceAnimation() { private void wallBounceAnimation() {
byte ballParticleGen = 0; Particle particle1, particle2, particle3, particle4;
for (byte ii = 0; ii < gameState.currentParticles.length; ii++) { particle1 = new Particle(gameState, 0, this.ballX - 3, this.ballY - 10, -2, -2, 1, 2, 6, 3);
if (gameState.currentParticles[ii] == null) { particle2 = new Particle(gameState, 0, this.ballX + 10, this.ballY - 10, 2, -2, 1, 2, 8, 3);
ballParticleGen++; particle3 = new Particle(gameState, 0, this.ballX - 3, this.ballY + 3, 2, 2, 1, 2, 2, 3);
switch(ballParticleGen) { particle4 = new Particle(gameState, 0, this.ballX + 10, this.ballY + 3, -2, 2, 1, 2, 4, 3);
case 1: // Up left gameState.particleList.add(particle1);
gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX - 3, this.ballY - 10, -2, -2, 1, 2, 6, 3); gameState.particleList.add(particle2);
gameState.currentParticles[ii].spawn(5); gameState.particleList.add(particle3);
break; gameState.particleList.add(particle4);
case 2: // Up right particle1.spawn(5);
gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX + 10, this.ballY - 10, 2, -2, 1, 2, 8, 3); particle2.spawn(5);
gameState.currentParticles[ii].spawn(5); particle3.spawn(5);
break; particle4.spawn(5);
case 3: // Down right
gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX - 3, this.ballY + 3, 2, 2, 1, 2, 2, 3);
gameState.currentParticles[ii].spawn(5);
break;
case 4: // Down left
gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX + 10, this.ballY + 3, -2, 2, 1, 2, 4, 3);
gameState.currentParticles[ii].spawn(5);
break;
}
if (ballParticleGen < 4) {continue;}
else {return;}
}
}
} }
public boolean getActiveState() {
return this.isActive;
}
public void setActiveState(boolean activeState) {
this.isActive = activeState;
}
public boolean getInvincibilityState() {
return this.isInvincible;
}
public void setInvincibilityState(boolean invincibilityState) {
this.isInvincible = invincibilityState;
}
} }

View file

@ -6,7 +6,7 @@ public class Brick {
short brickY; short brickY;
short brickWidth = 40; short brickWidth = 40;
short brickHeight = 17; short brickHeight = 17;
boolean isBroken = false; private boolean isBroken = false;
byte brickColor = 0; byte brickColor = 0;
byte scoreOnBreak = 10; byte scoreOnBreak = 10;
byte specialAbility = 0; byte specialAbility = 0;
@ -39,20 +39,15 @@ public class Brick {
if (!isBroken) { if (!isBroken) {
isBroken = true; isBroken = true;
gameState.score += this.scoreOnBreak + (gameState.level - 1); gameState.score += this.scoreOnBreak + (gameState.level - 1);
gameState.currentTextElements[0].setText(String.valueOf(gameState.score)); gameState.textElementList.get(3).setText(String.valueOf(gameState.score));
gameState.bricksOnScreen--; gameState.bricksOnScreen--;
switch(this.specialAbility) { switch(this.specialAbility) {
case 1: case 1:
gameState.lives++; gameState.lives++;
break; break;
case 2: case 2:
for (byte i = 0; i < gameState.currentBalls.length; i++) { Ball ball = new Ball(gameState, 300, 300, 7, 7, 0, true);
if (gameState.currentBalls[i] == null) { ball.spawnBall((this.brickX + (this.brickWidth / 2)), (this.brickY + (this.brickHeight / 2)));
gameState.currentBalls[i] = new Ball(gameState, 300, 300, 7, 7, 0, true);
gameState.currentBalls[i].spawnBall((this.brickX + (this.brickWidth / 2)), (this.brickY + (this.brickHeight / 2)));
return;
}
}
break; break;
default: default:
break; break;
@ -60,32 +55,27 @@ public class Brick {
} }
// Draw particles // Draw particles
byte brickParticleGen = 0; Particle particle1, particle2, particle3;
for (byte ii = 0; ii < gameState.currentParticles.length; ii++) { particle1 = new Particle(gameState, 0, this.brickX + 12, this.brickY + 3, 1, 1, this.brickColor, 2, 4, 1);
if (gameState.currentParticles[ii] == null) { particle2 = new Particle(gameState, 0, this.brickX + 20, this.brickY + 13, 1, 1, this.brickColor, 2, 3, 1);
brickParticleGen++; particle3 = new Particle(gameState, 0, this.brickX + 32, this.brickY + 7, 1, 1, this.brickColor, 2, 2, 1);
switch(brickParticleGen) { gameState.particleList.add(particle1);
case 1: gameState.particleList.add(particle2);
gameState.currentParticles[ii] = new Particle(gameState, 0, this.brickX + 12, this.brickY + 3, 1, 1, this.brickColor, 2, 4, 1); gameState.particleList.add(particle3);
gameState.currentParticles[ii].spawn(5); particle1.spawn(5);
break; particle2.spawn(5);
case 2: particle3.spawn(5);
gameState.currentParticles[ii] = new Particle(gameState, 0, this.brickX + 20, this.brickY + 13, 1, 1, this.brickColor, 2, 3, 1);
gameState.currentParticles[ii].spawn(5);
break;
case 3:
gameState.currentParticles[ii] = new Particle(gameState, 0, this.brickX + 32, this.brickY + 7, 1, 1, this.brickColor, 2, 2, 1);
gameState.currentParticles[ii].spawn(5);
break;
}
if (brickParticleGen < 3) {continue;}
else {return;}
}
}
this.brickX = 32767; this.brickX = 32767;
this.brickY = 32767; this.brickY = 32767;
this.hitBox.moveTo(32760, 32760, 2, 2); this.hitBox.moveTo(32760, 32760, 2, 2);
} }
public boolean getBrokenState() {
return this.isBroken;
}
public void setBrokenState(boolean brokenState) {
this.isBroken = brokenState;
}
} }

View file

@ -4,35 +4,48 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.awt.event.KeyListener; import java.awt.event.KeyListener;
import java.util.ArrayList;
public class GameState { public class GameState {
public String gameName = "Vector Breakout"; public String gameName = "Vector Breakout";
public String gameVersion = "20250511"; public String gameVersion = "20250511";
public boolean isGameStarted = false; private boolean isGameStarted = false;
public boolean isPaused = false; private boolean isPaused = false;
public boolean debugMenuEnabled = false; private boolean debugMenuEnabled = false;
public boolean isLost = false; public boolean isLost = false;
public boolean movingLeft = false; public boolean movingLeft = false;
public boolean movingRight = false; public boolean movingRight = false;
public boolean confettiMode = false; public boolean confettiMode = false;
public boolean collectorActive = false;
public byte lives = 5; public byte lives = 5;
public byte chancey = 0; private byte chancey = 0;
private final byte maxChanceyValue = 16;
public byte ballsOnScreen = 0; public byte ballsOnScreen = 0;
public byte targetFrameRate = 60; private short targetFrameRate = 60;
public byte actualFrames = 0; public short actualFrames = 0;
public byte actualFrameRate = 0; private short actualFrameRate = 0;
public byte gameTickRate = 60; public byte gameTickRate = 60;
public short gameResX = 800; private short internalResX = 800;
public short gameResY = 600; private short internalResY = 600;
private short windowResX = 1024;
private short windowResY = 768;
public short level = 1; public short level = 1;
public short bricksOnScreen = 0; public short bricksOnScreen = 0;
public int score = 0; public int score = 0;
public long frameCounter = 0; private long frameCounter = 0;
public Ball[] currentBalls = new Ball[10];
public Particle[] currentParticles = new Particle[127]; public ArrayList<Ball> ballList;
public Paddle[] currentPaddles = new Paddle[2]; public ArrayList<Particle> particleList;
public TextElement[] currentTextElements = new TextElement[63]; public ArrayList<Paddle> paddleList;
public ArrayList<TextElement> textElementList;
public ArrayList<Ball> ballCollector;
public ArrayList<Particle> particleCollector;
public ArrayList<Paddle> paddleCollector;
public ArrayList<TextElement> textElementCollector;
public ArrayList<Brick> brickCollector;
public Level currentLevel; public Level currentLevel;
// Debug stuffs // Debug stuffs
@ -41,20 +54,13 @@ public class GameState {
public short debugStartLevel = 1; public short debugStartLevel = 1;
public GameState() { public GameState() {
this.initComponents();
this.showTitleScreen(); this.showTitleScreen();
} }
public KeyListener getKeyListener() { public KeyListener getKeyListener() {
return new GameKeyListener(); return new GameKeyListener();
} }
public byte getGameTickRate() {
return gameTickRate;
}
public byte getTargetFrameRate() {
return targetFrameRate;
}
public ActionListener getGameStateUpdateActionListener() { public ActionListener getGameStateUpdateActionListener() {
return new GameStateUpdateListener(); return new GameStateUpdateListener();
@ -63,62 +69,48 @@ public class GameState {
public ActionListener getGameTickActionListener() { public ActionListener getGameTickActionListener() {
return new GameTickActionListener(); return new GameTickActionListener();
} }
class GameTickActionListener implements ActionListener { class GameTickActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
onGameTick(); onGameTick();
} }
} }
class GameKeyListener implements KeyListener { class GameKeyListener implements KeyListener {
public void keyPressed(KeyEvent e) { public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()) { switch(e.getKeyCode()) {
case 27: // Escape case 27: // Escape
debugMenuEnabled = !debugMenuEnabled; toggleDebugMenu();
break; break;
case 10: // Enter case 10: // Enter
if (isGameStarted) { if (GameState.this.getGameStartedState()) {
isPaused = !isPaused; GameState.this.togglePausedState();
if (!isPaused) { if (!GameState.this.getPausedState()) {
for (byte i = 0; i < currentTextElements.length; i++) { GameState.this.textElementList.get(6).setVisibility(false);
if (currentTextElements[i] != null && "PAUSED".equals(currentTextElements[i].text)) {
currentTextElements[i].cull();
return;
}
}
} }
else { else {
for (byte i = 0; i < currentTextElements.length; i++) { GameState.this.textElementList.get(6).setVisibility(true);
if (currentTextElements[i] == null) {
currentTextElements[i] = new TextElement(GameState.this, "PAUSED", 5, 142, 310, 1, 4);
currentTextElements[i].activate();
return;
}
}
} }
} }
break; break;
case 35: // End case 35: // End
if (isLost || !isGameStarted) {isGameStarted = true;newGame(true);return;} if (isLost || !GameState.this.getGameStartedState()) {GameState.this.setGameStartedState(true);GameState.this.newGame(true);}
break; break;
case 32: // Space case 32: // Space
if (isLost || !isGameStarted) {isGameStarted = true;newGame();return;} if (isLost || !GameState.this.getGameStartedState()) {GameState.this.setGameStartedState(true);GameState.this.newGame();}
if (!isPaused) { else if (!GameState.this.getPausedState()) {
if (ballsOnScreen < 1) { if (ballsOnScreen < 1) {
for (byte i = 0; i < currentBalls.length; i++) { Ball newBall = new Ball(GameState.this, 200, 300, 7, 7, 0, true);
if (currentBalls[i] == null) { GameState.this.ballList.add(newBall);
currentBalls[i] = new Ball(GameState.this, 200, 300, 7, 7, 0, true); newBall.spawnBall(true);
currentBalls[i].spawnBall(true);
if (!debugSpawnAllBalls) {return;}
}
}
} }
} }
break; break;
case 37: // Left case 37: // Left
if (isGameStarted) {movingLeft = true;} if (GameState.this.getGameStartedState()) {movingLeft = true;}
break; break;
case 39: // Right case 39: // Right
if (isGameStarted) {movingRight = true;} if (GameState.this.getGameStartedState()) {movingRight = true;}
break; break;
} }
} }
@ -137,117 +129,201 @@ public class GameState {
class GameStateUpdateListener implements ActionListener { class GameStateUpdateListener implements ActionListener {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
bricksOnScreen = 0;
actualFrameRate = actualFrames;
actualFrames = 0;
if (currentLevel != null) {
for (short i = 0; i < currentLevel.levelBricks.length; i++) {
if (currentLevel.levelBricks[i] != null) {
if (currentLevel.levelBricks[i].isBroken) {
currentLevel.levelBricks[i] = null;
}
else {bricksOnScreen++;}
}
}
}
for (byte i = 0; i < currentBalls.length; i++) {
if (currentBalls[i] != null) {
if (currentBalls[i].isActive == false) {
currentBalls[i] = null;
}
}
}
for (byte i = 0; i < currentParticles.length; i++) {
if (currentParticles[i] != null) {
if (currentParticles[i].isActive == false) {
currentParticles[i] = null;
}
}
}
for (byte i = 0; i < currentTextElements.length; i++) {
if (currentTextElements[i] != null) {
if (currentTextElements[i].active == false) {
currentTextElements[i] = null;
}
}
}
if (bricksOnScreen <= 0 && isGameStarted && !isLost) { GameState.this.collectorActive = true;
level++; GameState.this.ballCollector = new ArrayList<>();
currentLevel = new Level(GameState.this, level); GameState.this.particleCollector = new ArrayList<>();
currentLevel.startLevel(); GameState.this.paddleCollector = new ArrayList<>();
GameState.this.textElementCollector = new ArrayList<>();
GameState.this.brickCollector = new ArrayList<>();
bricksOnScreen = 0;
actualFrameRate = actualFrames;
actualFrames = 0;
if (GameState.this.currentLevel != null) {
if (GameState.this.currentLevel.brickList != null) {
for (Brick brick : GameState.this.currentLevel.brickList) {
if (brick.getBrokenState()) {
GameState.this.brickCollector.add(brick);
//GameState.this.currentLevel.brickList.remove(brick);
}
else {
bricksOnScreen++;
}
}
} }
} }
for (Ball ball : GameState.this.ballList) {
if (!ball.getActiveState()) {
//GameState.this.ballList.remove(ball);
GameState.this.ballCollector.add(ball);
}
}
for (Particle particle: GameState.this.particleList) {
if (!particle.getActiveState()) {
//GameState.this.particleList.remove(particle);
GameState.this.particleCollector.add(particle);
}
}
if (bricksOnScreen <= 0 && GameState.this.getGameStartedState() && !isLost) {
level++;
GameState.this.currentLevel = new Level(GameState.this, level);
GameState.this.currentLevel.constructLevel();
}
GameState.this.ballList.removeAll(GameState.this.ballCollector);
GameState.this.paddleList.removeAll(GameState.this.paddleCollector);
GameState.this.particleList.removeAll(GameState.this.particleCollector);
GameState.this.textElementList.removeAll(GameState.this.textElementCollector);
if (GameState.this.currentLevel != null) {
GameState.this.currentLevel.brickList.removeAll(GameState.this.brickCollector);
}
GameState.this.collectorActive = false;
}
} }
public void incrementFrameCounter() { public void incrementFrameCounter() {
this.frameCounter++; this.frameCounter++;
} }
public long getFrameCounter() {
public void onGameTick() { return this.frameCounter;
// i will implement something better than "chancey" eventually }
chancey++;
if (chancey >= 16) { public void incrementChancey() {
chancey = 0; this.chancey++;
if (this.chancey >= this.maxChanceyValue) {
this.chancey = 0;
} }
}
public byte getChancey() {
return this.chancey;
}
public byte getGameTickRate() {
return this.gameTickRate;
}
public short getTargetFrameRate() {
return this.targetFrameRate;
}
public short getActualFrameRate() {
return this.actualFrameRate;
}
public boolean getPausedState() {
return this.isPaused;
}
public void togglePausedState() {
this.isPaused = !this.isPaused;
}
public boolean getDebugMenuState() {
return this.debugMenuEnabled;
}
public void toggleDebugMenu() {
this.debugMenuEnabled = !this.debugMenuEnabled;
}
public boolean getGameStartedState() {
return this.isGameStarted;
}
public void setGameStartedState(boolean startState) {
this.isGameStarted = startState;
}
public short getInternalResX() {
return this.internalResX;
}
public short getInternalResY() {
return this.internalResY;
}
public short getWindowResX() {
return this.windowResX;
}
public short getWindowResY() {
return this.windowResY;
}
public void setWindowResX(short resolution) {
this.windowResX = resolution;
}
public void setWindowResY(short resolution) {
this.windowResY = resolution;
}
public void onGameTick() {
this.incrementChancey();
if (isPaused) {return;} if (this.getPausedState()) {return;}
// Move player if keys are held // Move player if keys are held
for (byte i = 0; i < currentPaddles.length; i++) { for (Paddle paddle : this.paddleList) {
if (currentPaddles[i] != null) { if (this.movingLeft) {
if (movingLeft) { paddle.movePaddle(false);
currentPaddles[i].movePaddle(false); }
} else if (this.movingRight) {
else if (movingRight) { paddle.movePaddle(true);
currentPaddles[i].movePaddle(true); }
} else {
else { paddle.paddleSpeed = 0;
currentPaddles[i].paddleSpeed = 0;
}
} }
} }
// Ball logic (movement, collision checks) // Ball logic (movement, collision checks)
for (byte i = 0; i < currentBalls.length; i++) { try {
if (currentBalls[i] != null) { for (Ball ball : this.ballList) {
if (currentBalls[i].isActive) { if (ball.getActiveState()) {
currentBalls[i].moveBall(); ball.moveBall();
} }
} }
} catch(java.util.ConcurrentModificationException except) {
System.err.println("ConcurrentModificationException, onGameTick() Ball iteration\n"
+ "Resetting ballList ArrayList.");
// except.printStackTrace();
this.ballList = new ArrayList<>();
} }
// Particles // Particles
for (byte i = 0; i < currentParticles.length; i++) { for (Particle particle : this.particleList) {
if (currentParticles[i] != null) { if (particle.getActiveState()) {
if (currentParticles[i].isActive) { particle.update();
currentParticles[i].update();
}
} }
} }
} }
public void showTitleScreen() { public void showTitleScreen() {
currentTextElements[0] = new TextElement(this, "VECTOR BREAKOUT", 2.5f, 67, 280, 15, 2); textElementList.get(0).activateTimer();
currentTextElements[1] = new TextElement(this, "MMXXV BOYFAILURE.DEV", 0.75f, 259, 315, 15, 1); textElementList.get(1).activateTimer();
currentTextElements[2] = new TextElement(this, "PRESS SPACE TO BEGIN", 1, 215, 345, 15, 2); textElementList.get(2).activateTimer();
currentTextElements[0].activate();
currentTextElements[1].activate();
currentTextElements[2].activate();
} }
public void gameLose() { public void gameLose() {
if (currentLevel != null) { if (this.currentLevel != null) {
for (short i = 0; i < currentLevel.levelBricks.length; i++) { for (Brick brick : this.currentLevel.brickList) {
currentLevel.levelBricks[i] = null; brick.setBrokenState(true);
this.brickCollector.add(brick);
} }
} }
for (byte i = 0; i < currentBalls.length; i++) { for (Ball ball : this.ballList) {
currentBalls[i] = null; ball.setActiveState(false);
this.ballCollector.add(ball);
} }
isLost = true; for (Paddle paddle : this.paddleList) {
ballsOnScreen = 0; paddle.setActiveState(false);
this.paddleCollector.add(paddle);
}
this.isLost = true;
this.ballsOnScreen = 0;
this.textElementList.get(3).setVisibility(false);
this.textElementList.get(4).setVisibility(false);
this.textElementList.get(5).setVisibility(false);
this.textElementList.get(8).setVisibility(true);
this.textElementList.get(9).setVisibility(true);
this.getGameStateUpdateActionListener().actionPerformed(new ActionEvent(this, 0, ""));
} }
public void newGame() { public void newGame() {
@ -255,9 +331,6 @@ public class GameState {
} }
public void newGame(boolean debugLevel) { public void newGame(boolean debugLevel) {
for (byte i = 0; i < currentTextElements.length; i++) {
currentTextElements[i] = null;
}
lives = 5; lives = 5;
isLost = false; isLost = false;
score = 0; score = 0;
@ -267,17 +340,53 @@ public class GameState {
ballsOnScreen = 0; ballsOnScreen = 0;
movingLeft = false; movingLeft = false;
movingRight = false; movingRight = false;
if (debugUseGiantPaddle) {currentPaddles[0] = new Paddle(this, 0, 500, 800, 60, 0);} if (debugUseGiantPaddle) {this.paddleList.add(new Paddle(this, 0, 500, 800, 60, 0));}
else {currentPaddles[0] = new Paddle(this, 350, 500, 100, 15, 0);} else {this.paddleList.add(new Paddle(this, 350, 500, 100, 15, 0));}
currentTextElements[0] = new TextElement(this, String.valueOf(score), 1, 20, -13, 1, 2); if (this.debugUseGiantPaddle) {
currentTextElements[1] = new TextElement(this, String.valueOf(lives), 1, 200, -13, 1, 2); this.paddleList.add(new Paddle(this, 0, 500, 800, 60, 0));
currentTextElements[2] = new TextElement(this, String.valueOf(level), 1, 380, -13, 1, 2); }
currentTextElements[0].activate(); else {
currentTextElements[1].activate(); this.paddleList.add(new Paddle(this, 350, 500, 100, 15, 0));
currentTextElements[2].activate(); }
this.textElementList.get(0).setVisibility(false);
this.textElementList.get(1).setVisibility(false);
this.textElementList.get(2).setVisibility(false);
this.textElementList.get(3).setVisibility(true);
this.textElementList.get(4).setVisibility(true);
this.textElementList.get(5).setVisibility(true);
textElementList.get(3).setText(String.valueOf(this.score));
textElementList.get(4).setText(String.valueOf(this.lives));
textElementList.get(5).setText(String.valueOf(this.level));
this.textElementList.get(8).setVisibility(false);
this.textElementList.get(9).setVisibility(false);
if (debugStartLevel > 1) {level = debugStartLevel;} if (debugStartLevel > 1) {level = debugStartLevel;}
currentLevel = new Level(this, level); this.currentLevel = new Level(this, level);
currentLevel.startLevel(); this.currentLevel.constructLevel();
this.updateStaticTextElements();
}
public void initComponents() {
this.ballList = new ArrayList<>();
this.particleList = new ArrayList<>();
this.paddleList = new ArrayList<>();
this.textElementList = new ArrayList<>();
// List of indices
textElementList.add(new TextElement(this, "VECTOR BREAKOUT", 2.5f, 67, 280, 15, 2)); // 0 - Title
textElementList.add(new TextElement(this, "MMXXV BOYFAILURE.DEV", 0.75f, 259, 315, 15, 1)); // 1 - Copyright
textElementList.add(new TextElement(this, "PRESS SPACE TO BEGIN", 1, 215, 345, 15, 2)); // 2 - Space
textElementList.add(new TextElement(this, String.valueOf(score), 1, 20, -13, 1, 2)); // 3 - Score
textElementList.add(new TextElement(this, String.valueOf(lives), 1, 200, -13, 1, 2)); // 4 - Lives
textElementList.add(new TextElement(this, String.valueOf(level), 1, 380, -13, 1, 2)); // 5 - Level
textElementList.add(new TextElement(this, "PAUSED", 5, 142, 310, 1, 4)); // 6 - Paused
textElementList.add(new TextElement(this, "Level Name", 1, 24, 480, 15, 2, 3000)); // 7 - Level Name
textElementList.add(new TextElement(this, " GAME OVER ", 2.5f, 67, 280, 10, 2)); // 8 - Lose
textElementList.add(new TextElement(this, "PRESS SPACE TO RETRY", 1, 215, 315, 10, 1)); // 9 - Retry
} }
public void updateStaticTextElements() {
textElementList.get(3).setText(String.valueOf(score));
textElementList.get(4).setText(String.valueOf(lives));
textElementList.get(5).setText(String.valueOf(level));
}
} }

View file

@ -4,7 +4,7 @@ import java.awt.geom.Rectangle2D;
public class Hitbox { public class Hitbox {
Rectangle2D bounds; private Rectangle2D bounds;
byte padding = 0; byte padding = 0;
public Hitbox(int x, int y, int x2, int y2, int padding) { public Hitbox(int x, int y, int x2, int y2, int padding) {
@ -25,4 +25,11 @@ public class Hitbox {
this.bounds.setRect(x - this.padding, y - this.padding, x2 + padding, y2 + padding); this.bounds.setRect(x - this.padding, y - this.padding, x2 + padding, y2 + padding);
} }
public Rectangle2D getBounds() {
return this.bounds;
}
public void setBounds(int x, int y, int x2, int y2) {
this.bounds = new Rectangle2D.Float(x - this.padding, y - this.padding, x2 + this.padding, y2 + this.padding);
}
} }

View file

@ -1,5 +1,7 @@
package dev.boyfailure.quajra.vectorbreakout; package dev.boyfailure.quajra.vectorbreakout;
import java.util.ArrayList;
public class Level { public class Level {
short levelID; short levelID;
@ -7,10 +9,11 @@ public class Level {
String levelName = "LEVEL"; String levelName = "LEVEL";
byte[] levelLayout; // 19 bricks per row byte[] levelLayout; // 19 bricks per row
byte[] colorMap; byte[] colorMap;
Brick[] levelBricks;
ArrayList<Brick> brickList;
private GameState gameState; private GameState gameState;
public Level(GameState gameState, int id) { public Level(GameState gameState, int id) {
this.gameState = gameState; this.gameState = gameState;
this.levelID = (short) id; this.levelID = (short) id;
@ -190,50 +193,50 @@ public class Level {
} }
} }
public void startLevel() { public void constructLevel() {
this.brickList = new ArrayList<>();
// Brick placement // Brick placement
short brickLeftPlacement = 20; short brickLeftPlacement = 20;
if (this.levelLayout != null) { for (int i = 0; i < this.levelLayout.length; i++) {
this.levelBricks = new Brick[this.levelLayout.length]; switch (this.levelLayout[i]) {
for (int i = 0; i < this.levelLayout.length; i++) { case 1:
switch (this.levelLayout[i]) { this.brickList.add(new Brick(gameState, brickLeftPlacement, (((i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 4, 0));
case 1: break;
this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 4, 0); case 2:
break; this.brickList.add(new Brick(gameState, brickLeftPlacement, (((i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 0));
case 2: break;
this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 0); case 3:
break; this.brickList.add(new Brick(gameState, brickLeftPlacement, (((i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 16, 0));
case 3: break;
this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 16, 0); case 4:
break; this.brickList.add(new Brick(gameState, brickLeftPlacement, (((i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 32, 0));
case 4: break;
this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 32, 0); case 5:
break; this.brickList.add(new Brick(gameState, brickLeftPlacement, (((i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 64, 0));
case 5: break;
this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 64, 0); case 6:
break; this.brickList.add(new Brick(gameState, brickLeftPlacement, (((i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 0));
case 6: break;
this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 0); case 7:
break; this.brickList.add(new Brick(gameState, brickLeftPlacement, (((i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 0));
case 7: break;
this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 0); case 16: // Spawns a ball
break; this.brickList.add(new Brick(gameState, brickLeftPlacement, (((i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 2));
case 16: // Spawns a ball break;
this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 2); default:
break; break;
default:
this.levelBricks[i] = null;
break;
}
brickLeftPlacement += 40;
if (brickLeftPlacement >= 760) {brickLeftPlacement = 20;}
} }
brickLeftPlacement += 40;
if (brickLeftPlacement >= 760) {brickLeftPlacement = 20;}
} }
gameState.bricksOnScreen = (short) this.levelBricks.length; this.startLevel();
gameState.currentTextElements[2].setText(String.valueOf(this.levelID)); }
gameState.currentTextElements[3] = new TextElement(gameState, this.levelName, 1, 24, 480, 15, 2, 3000);
gameState.currentTextElements[3].activate(); public void startLevel() {
gameState.bricksOnScreen = (short) this.brickList.size();
gameState.textElementList.set(7, new TextElement(gameState, this.levelName, 1, 24, 480, 15, 2, 3000));
gameState.textElementList.get(7).activateTimer();
} }
} }

View file

@ -1,9 +1,13 @@
package dev.boyfailure.quajra.vectorbreakout; package dev.boyfailure.quajra.vectorbreakout;
import java.util.ArrayList;
public class Menu { public class Menu {
public String menuTitle = "Menu"; public String menuTitle = "Menu";
public byte index = 0; public byte index = 0;
private ArrayList<TextElement> textElementList;
private GameState gameState; private GameState gameState;
@ -11,4 +15,30 @@ public class Menu {
this.gameState = gameState; this.gameState = gameState;
} }
/**
* Adds a TextElement to the Menu.
* @param text The text that will be displayed.
* @param textScale The size of the text on screen.
* @param x The x coordinate of the text's origin point.
* @param y The y coordinate of the text's origin point.
* @param textColor The color of the text.
* @param vectorScale The scale of the text.
*/
public void addTextElement(String text, float textScale, int x, int y, int textColor, float vectorScale) {
textElementList.add(new TextElement(this.gameState, text, textScale, x, y, textColor, vectorScale));
}
/**
* Gets the TextElement at the specified index in the Menu.
* @param index
* @return The TextElement at the index.
*/
public TextElement getTextElement(int index) {
return textElementList.get(index);
}
public void activate() {
}
} }

View file

@ -33,7 +33,7 @@ public class Paddle {
if (direction) {this.paddleX += (this.paddleSpeed * this.speedMultiplier);} if (direction) {this.paddleX += (this.paddleSpeed * this.speedMultiplier);}
else {this.paddleX -= (this.paddleSpeed * this.speedMultiplier);} else {this.paddleX -= (this.paddleSpeed * this.speedMultiplier);}
if (this.paddleX <= 20) {this.paddleX = 20;} if (this.paddleX <= 20) {this.paddleX = 20;}
else if (this.paddleX >= (gameState.gameResX - this.paddleWidth - 20)) {this.paddleX = (short) (gameState.gameResX - this.paddleWidth - 20);} else if (this.paddleX >= (gameState.getInternalResX() - this.paddleWidth - 20)) {this.paddleX = (short) (gameState.getInternalResX() - this.paddleWidth - 20);}
this.hitBox.moveTo(this.paddleX, this.paddleY, this.paddleWidth, this.paddleHeight); this.hitBox.moveTo(this.paddleX, this.paddleY, this.paddleWidth, this.paddleHeight);
this.hitBoxLeft.moveTo(this.paddleX, this.paddleY, this.paddleWidth / 5, this.paddleHeight); this.hitBoxLeft.moveTo(this.paddleX, this.paddleY, this.paddleWidth / 5, this.paddleHeight);
@ -46,4 +46,11 @@ public class Paddle {
this.paddleY = 32767; this.paddleY = 32767;
} }
public boolean getActiveState() {
return this.isActive;
}
public void setActiveState(boolean activeState) {
this.isActive = activeState;
}
} }

View file

@ -54,5 +54,9 @@ public class Particle {
this.isActive = false; this.isActive = false;
} }
} }
public boolean getActiveState() {
return this.isActive;
}
} }

View file

@ -6,7 +6,8 @@ import java.util.TimerTask;
public class TextElement { public class TextElement {
String text; String text;
boolean active = false; boolean active = true;
private boolean isVisible = false;
float textScale = 1; float textScale = 1;
float vectorScale = 1; float vectorScale = 1;
byte textColor; byte textColor;
@ -25,6 +26,7 @@ public class TextElement {
* @param x The x coordinate of the text's origin point. * @param x The x coordinate of the text's origin point.
* @param y The y coordinate of the text's origin point. * @param y The y coordinate of the text's origin point.
* @param textColor The color of the text. * @param textColor The color of the text.
* @param vectorScale The scale of the text.
*/ */
public TextElement(GameState gameState, String text, float textScale, int x, int y, int textColor, float vectorScale) { public TextElement(GameState gameState, String text, float textScale, int x, int y, int textColor, float vectorScale) {
this.gameState = gameState; this.gameState = gameState;
@ -44,6 +46,7 @@ public class TextElement {
* @param x The x coordinate of the text's origin point. * @param x The x coordinate of the text's origin point.
* @param y The y coordinate of the text's origin point. * @param y The y coordinate of the text's origin point.
* @param textColor The color of the text. * @param textColor The color of the text.
* @param vectorScale The scale of the text.
* @param duration How long the text will stay on screen, in milliseconds. * @param duration How long the text will stay on screen, in milliseconds.
*/ */
public TextElement(GameState gameState, String text, float textScale, int x, int y, int textColor, float vectorScale, int duration) { public TextElement(GameState gameState, String text, float textScale, int x, int y, int textColor, float vectorScale, int duration) {
@ -57,29 +60,34 @@ public class TextElement {
this.duration = (short) duration; this.duration = (short) duration;
this.creationTime = System.currentTimeMillis(); this.creationTime = System.currentTimeMillis();
} }
public boolean getActiveState() {
return this.active;
}
/** /**
* Prepares the TextElement for rendering. * Prepares the TextElement for rendering.
*/ */
public void activate() { public void activateTimer() {
this.active = true; this.active = true;
long testLong = this.creationTime; long testLong = this.creationTime;
if (this.duration > 0) { if (this.duration > 0) {
Timer levelTitleGTFO = new Timer(); Timer levelTitleGTFO = new Timer();
levelTitleGTFO.schedule(new TimerTask() { levelTitleGTFO.schedule(new TimerTask() {
public void run() { public void run() {
for (byte i = 0; i < gameState.currentTextElements.length; i++) { TextElement.this.setVisibility(false);
if (gameState.currentTextElements[i] != null) {
if (gameState.currentTextElements[i].creationTime == testLong) {
gameState.currentTextElements[i].cull();
}
}
}
} }
}, this.duration); }, this.duration);
} }
this.setVisibility(true);
} }
public boolean getVisibility() {
return this.isVisible;
}
public void setVisibility(boolean visibility) {
this.isVisible = visibility;
}
/** /**
* Prepares the TextElement for culling. * Prepares the TextElement for culling.
*/ */

View file

@ -28,7 +28,7 @@ public class VectorBreakout {
// Set the game window's properties // Set the game window's properties
gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameFrame.setTitle(gameState.gameName); gameFrame.setTitle(gameState.gameName);
gameFrame.setSize(gameState.gameResX, gameState.gameResY); gameFrame.setSize(gameState.getInternalResX(), gameState.getInternalResY());
gameFrame.getContentPane().setBackground(Color.black); gameFrame.getContentPane().setBackground(Color.black);
gameFrame.getContentPane().add(gameCanvas); gameFrame.getContentPane().add(gameCanvas);
gameFrame.setVisible(true); gameFrame.setVisible(true);
@ -38,8 +38,10 @@ public class VectorBreakout {
// Adapts vector drawing scale when the window is resized // Adapts vector drawing scale when the window is resized
gameFrame.addComponentListener(new ComponentAdapter() { gameFrame.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent componentEvent) { public void componentResized(ComponentEvent componentEvent) {
GameDisplay.beamScaleX = gameFrame.getBounds().width / 800f; gameState.setWindowResX((short) gameFrame.getBounds().width);
GameDisplay.beamScaleY = gameFrame.getBounds().height / 600f; gameState.setWindowResY((short) gameFrame.getBounds().height);
gameCanvas.setBeamScaleX(gameState.getWindowResX() / 800f);
gameCanvas.setBeamScaleY(gameState.getWindowResY() / 600f);
} }
}); });