Skip to content

Commit 8310bca

Browse files
committed
refactor: move collision detection and power-up creation logic
1 parent 54fc916 commit 8310bca

File tree

6 files changed

+132
-113
lines changed

6 files changed

+132
-113
lines changed

client/src/main/java/io/exterminator3618/client/components/Ball.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public void resetToCenter(Paddle paddle) {
7070
isStuckToPaddle = true;
7171
stuckOffsetX = 0; // Reset offset to center
7272
setVelocity(0,0);
73+
7374
}
7475

7576
/**

client/src/main/java/io/exterminator3618/client/components/ExtraLifePowerUp.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
public class ExtraLifePowerUp extends PowerUp {
77

88
public ExtraLifePowerUp(int x, int y) {
9-
super("Extra Life", 0.0f , x, y, Constants.POWERUP_WIDTH, Constants.POWERUP_HEIGHT, "extra_life_power_up");
9+
super("Extra Life", 0.0f , x, y, Constants.POWERUP_WIDTH, Constants.POWERUP_HEIGHT, "extra_live_power_up");
1010
}
1111

1212
@Override

client/src/main/java/io/exterminator3618/client/components/PowerUp.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import static java.lang.Math.random;
44

5-
import io.exterminator3618.client.screens.GameScreen;
65
import static io.exterminator3618.client.Constants.POWERUP_FALL_SPEED;
6+
import io.exterminator3618.client.screens.GameScreen;
77

88
public abstract class PowerUp extends MovableObject {
99
private String type;
@@ -58,7 +58,11 @@ public void update(float deltaTime) {
5858
}
5959

6060
public static PowerUp createRandomPowerUp(int x, int y){
61-
switch ((int) (random() * 6)) {
61+
62+
int randomIndex = (int) (random() * 6);
63+
64+
65+
switch (randomIndex) {
6266
case 0:
6367
return new WidenPaddlePowerUp(x, y);
6468
case 1:
@@ -72,7 +76,8 @@ public static PowerUp createRandomPowerUp(int x, int y){
7276
case 5:
7377
return new SlowBallPowerUp(x, y);
7478
default:
75-
return null;
79+
// Safety fallback
80+
return new WidenPaddlePowerUp(x, y);
7681
}
7782
}
7883

client/src/main/java/io/exterminator3618/client/components/WidenPaddlePowerUp.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
public class WidenPaddlePowerUp extends PowerUp {
1111

1212
public WidenPaddlePowerUp(int x, int y) {
13-
super("Widen Paddle", 10.0f, x, y, Constants.POWERUP_WIDTH, Constants.POWERUP_HEIGHT, "extend_paddle_power_up");
13+
super("Widen Paddle", 10.0f, x, y, Constants.POWERUP_WIDTH, Constants.POWERUP_HEIGHT, "expand_paddle_power_up");
1414
}
1515

1616
@Override

client/src/main/java/io/exterminator3618/client/screens/GameScreen.java

Lines changed: 28 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import java.util.List;
66
import java.util.Random;
77

8-
import io.exterminator3618.client.utils.*;
98
import org.slf4j.Logger;
109
import org.slf4j.LoggerFactory;
1110

@@ -49,12 +48,13 @@
4948
import io.exterminator3618.client.components.StrongBrick;
5049
import io.exterminator3618.client.components.TextButton;
5150
import io.exterminator3618.client.components.WidenPaddlePowerUp;
52-
import io.exterminator3618.client.utils.SoundManager;
5351
import io.exterminator3618.client.utils.Assets;
5452
import io.exterminator3618.client.utils.GameSaveData;
5553
import io.exterminator3618.client.utils.LevelLoader;
54+
import static io.exterminator3618.client.utils.Physics.checkBallBrickCollisions;
5655
import static io.exterminator3618.client.utils.Physics.checkPowerUpCollision;
5756
import io.exterminator3618.client.utils.Renderer;
57+
import io.exterminator3618.client.utils.SoundManager;
5858

5959
/**
6060
* Main LibGDX application for the Exterminator3618 client. It owns the renderer
@@ -142,6 +142,7 @@ public void loadLevel(int levelNumber, Ball oldball) {
142142
));
143143

144144
ball.setStuckToPaddle(true);
145+
ball.setBallSpeed(1f);
145146
}
146147

147148

@@ -371,7 +372,7 @@ public void render(float deltaTime) {
371372

372373
updatePowerUps(deltaTime);
373374
updateActivePowerUps(deltaTime);
374-
checkBallBrickCollisions();
375+
checkBallBrickCollisions(this);
375376
ball.checkPaddleCollision(paddle);
376377

377378

@@ -494,11 +495,11 @@ public void dispose() {
494495
log.info("Game disposed");
495496
}
496497

497-
private void gotoVictoryScreen() {
498+
public void gotoVictoryScreen() {
498499
game.launchScreen(new VictoryScreen(game));
499500
}
500501

501-
private void gotoWinLevelScreen(int level) {
502+
public void gotoWinLevelScreen(int level) {
502503
game.launchScreen(new WinLevelScreen(game, level, this));
503504
}
504505

@@ -601,81 +602,6 @@ private void updateActivePowerUps(float deltaTime) {
601602
}
602603

603604

604-
/**
605-
* Checks for collisions between any ball and all bricks.
606-
* Handles brick destruction and ball bouncing.
607-
* This corrected version iterates through each brick and checks against all balls.
608-
*/
609-
private void checkBallBrickCollisions() {
610-
if (bricks.isEmpty()) {
611-
return;
612-
}
613-
614-
List<Ball> allBalls = new ArrayList<>(extraBalls);
615-
allBalls.add(ball);
616-
617-
Iterator<Brick> brickIterator = bricks.iterator();
618-
while (brickIterator.hasNext()) {
619-
Brick brick = brickIterator.next();
620-
621-
if (brick.isDestroyed()) {
622-
brickIterator.remove();
623-
continue;
624-
}
625-
626-
for (Ball currentBall : allBalls) {
627-
if (currentBall.collidesWith(brick)) {
628-
currentBall.handleBrickCollision(brick);
629-
boolean wasDestroyed = brick.takeHit();
630-
631-
if (wasDestroyed) {
632-
ball.incrementCombo();
633-
score += 10 * ball.getComboCount();
634-
if ("multiball".equals(brick.getType())) {
635-
spawnExtraBalls(brick.getX() + brick.getWidth() / 2, brick.getY());
636-
} else if ("powerup_brick".equals(brick.getType())) {
637-
PowerUp powerUp = PowerUp.createRandomPowerUp(brick.getX() + brick.getWidth() / 2 - Constants.POWERUP_WIDTH / 2,
638-
brick.getY() + brick.getHeight() / 2 - Constants.POWERUP_HEIGHT / 2);
639-
powerUps.add(powerUp);
640-
log.debug("PowerUp created at position ({}, {})", powerUp.getX(), powerUp.getY());
641-
} else if ("strong".equals(brick.getType())) {
642-
score += 10 * ball.getComboCount();
643-
}
644-
645-
brickIterator.remove();
646-
647-
if (levelClear()) {
648-
649-
gotoWinLevelScreen(currentLevel);
650-
soundManager.play("sound/collected_and_level.wav");
651-
ball.resetToCenter(paddle);
652-
int nextLevel = currentLevel + 1;
653-
if (currentLevel > Constants.Level) {
654-
gotoVictoryScreen();
655-
656-
} else {
657-
currentLevel = nextLevel;
658-
loadLevel(currentLevel, ball);
659-
}
660-
}
661-
662-
// log
663-
log.debug("Brick destroyed! Remaining bricks: {}", bricks.size());
664-
} else {
665-
// Gạch vẫn còn máu
666-
log.debug("Brick hit! Remaining HP: {}/{}", brick.getHitPoints(),
667-
brick.getType().equals("strong") ? 3 : 1);
668-
}
669-
670-
// 3. QUAN TRỌNG: Thoát khỏi vòng lặp kiểm tra bóng
671-
// Vì viên gạch này đã được xử lý va chạm rồi.
672-
// Điều này ngăn một viên gạch bị nhiều bóng phá hủy trong cùng một frame.
673-
break;
674-
}
675-
}
676-
}
677-
}
678-
679605
public Paddle getPaddle(){
680606
return paddle;
681607
}
@@ -700,27 +626,6 @@ public List<Ball> getExtraBalls() {
700626
return extraBalls;
701627
}
702628

703-
public int getScore() {
704-
return score;
705-
}
706-
707-
public int getCurrentLevel() {
708-
return currentLevel;
709-
}
710-
711-
public SoundManager getSoundManager() {
712-
return soundManager;
713-
}
714-
715-
public boolean isPowerUpTypeExist(String type) {
716-
for (PowerUp powerUp : powerUps) {
717-
if (powerUp.getType().equals(type)) {
718-
return true;
719-
}
720-
}
721-
return false;
722-
}
723-
724629
public boolean levelClear() {
725630
if (bricks.isEmpty()) {
726631
return true;
@@ -733,8 +638,28 @@ public boolean levelClear() {
733638
return true;
734639
}
735640

736-
public List<Ball> getExtraBall(){
737-
return extraBalls;
641+
public List<Brick> getBricks() {
642+
return bricks;
643+
}
644+
645+
public void addScore(int points) {
646+
this.score += points;
647+
}
648+
649+
public void addPowerUp(PowerUp powerUp) {
650+
this.powerUps.add(powerUp);
651+
}
652+
653+
public int getCurrentLevel() {
654+
return currentLevel;
655+
}
656+
657+
public void setCurrentLevel(int level) {
658+
this.currentLevel = level;
659+
}
660+
661+
public SoundManager getSoundManager() {
662+
return soundManager;
738663
}
739664

740665
}

client/src/main/java/io/exterminator3618/client/utils/Physics.java

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package io.exterminator3618.client.utils;
22

3-
import io.exterminator3618.client.Constants;
3+
import java.util.ArrayList;
4+
import java.util.Iterator;
5+
import java.util.List;
6+
47
import org.slf4j.Logger;
58
import org.slf4j.LoggerFactory;
69

10+
import io.exterminator3618.client.Constants;
711
import io.exterminator3618.client.components.Ball;
812
import io.exterminator3618.client.components.Brick;
913
import io.exterminator3618.client.components.GameObject;
1014
import io.exterminator3618.client.components.Paddle;
1115
import io.exterminator3618.client.components.PowerUp;
1216
import io.exterminator3618.client.components.SolidBrick;
17+
import io.exterminator3618.client.screens.GameScreen;
1318

1419
/**
1520
* Physics engine for handling all collision detection and response in the game.
@@ -63,10 +68,6 @@ public static boolean checkBallBrickCollision(Ball ball, Brick brick) {
6368

6469
return checkAABBCollision(ball, brick);
6570
}
66-
67-
public static boolean checkPaddlePowerUpCollision(Paddle paddle, PowerUp powerUp) {
68-
return checkAABBCollision(paddle, powerUp);
69-
}
7071

7172
/**
7273
* Precise collision detection: only center bottom of ball with top of paddle.
@@ -343,4 +344,91 @@ public static double getBallCurrentSpeed(Ball ball) {
343344
public static boolean checkPowerUpCollision(PowerUp powerUp, Paddle paddle) {
344345
return checkAABBCollision(powerUp, paddle);
345346
}
347+
348+
/**
349+
* Checks for collisions between any ball and all bricks.
350+
* Handles brick destruction and ball bouncing.
351+
* This corrected version iterates through each brick and checks against all balls.
352+
*
353+
* @param gameScreen the GameScreen instance to access game state
354+
*/
355+
public static void checkBallBrickCollisions(GameScreen gameScreen) {
356+
List<Brick> bricks = gameScreen.getBricks();
357+
if (bricks == null || bricks.isEmpty()) {
358+
return;
359+
}
360+
361+
List<Ball> allBalls = new ArrayList<>(gameScreen.getExtraBalls());
362+
allBalls.add(gameScreen.getBall());
363+
364+
Iterator<Brick> brickIterator = bricks.iterator();
365+
while (brickIterator.hasNext()) {
366+
Brick brick = brickIterator.next();
367+
368+
if (brick.isDestroyed()) {
369+
brickIterator.remove();
370+
continue;
371+
}
372+
373+
for (Ball currentBall : allBalls) {
374+
if (currentBall.collidesWith(brick)) {
375+
currentBall.handleBrickCollision(brick);
376+
boolean wasDestroyed = brick.takeHit();
377+
378+
if (wasDestroyed) {
379+
Ball mainBall = gameScreen.getBall();
380+
mainBall.incrementCombo();
381+
gameScreen.addScore(10 * mainBall.getComboCount());
382+
383+
if ("multiball".equals(brick.getType())) {
384+
gameScreen.spawnExtraBalls(brick.getX() + brick.getWidth() / 2, brick.getY());
385+
} else if ("powerup_brick".equals(brick.getType())) {
386+
PowerUp powerUp = PowerUp.createRandomPowerUp(
387+
brick.getX() + brick.getWidth() / 2 - Constants.POWERUP_WIDTH / 2,
388+
brick.getY() + brick.getHeight() / 2 - Constants.POWERUP_HEIGHT / 2);
389+
if (powerUp != null) {
390+
gameScreen.addPowerUp(powerUp);
391+
log.debug("PowerUp {} created at position ({}, {})", powerUp.getType(), powerUp.getX(), powerUp.getY());
392+
} else {
393+
log.warn("Failed to create power-up at position ({}, {})",
394+
brick.getX() + brick.getWidth() / 2, brick.getY() + brick.getHeight() / 2);
395+
}
396+
} else if ("strong".equals(brick.getType())) {
397+
gameScreen.addScore(10 * mainBall.getComboCount());
398+
}
399+
400+
brickIterator.remove();
401+
402+
// Check if level is cleared AFTER removing brick but BEFORE loading next level
403+
// This ensures power-ups spawned from the last brick are not lost
404+
boolean levelCleared = gameScreen.levelClear();
405+
if (levelCleared) {
406+
gameScreen.gotoWinLevelScreen(gameScreen.getCurrentLevel());
407+
gameScreen.getSoundManager().play("sound/collected_and_level.wav");
408+
mainBall.resetToCenter(gameScreen.getPaddle());
409+
int nextLevel = gameScreen.getCurrentLevel() + 1;
410+
if (gameScreen.getCurrentLevel() > Constants.Level) {
411+
gameScreen.gotoVictoryScreen();
412+
} else {
413+
gameScreen.setCurrentLevel(nextLevel);
414+
gameScreen.loadLevel(nextLevel, mainBall);
415+
}
416+
}
417+
418+
// log
419+
log.debug("Brick destroyed! Remaining bricks: {}", bricks.size());
420+
} else {
421+
// Gạch vẫn còn máu
422+
log.debug("Brick hit! Remaining HP: {}/{}", brick.getHitPoints(),
423+
brick.getType().equals("strong") ? 3 : 1);
424+
}
425+
426+
// 3. QUAN TRỌNG: Thoát khỏi vòng lặp kiểm tra bóng
427+
// Vì viên gạch này đã được xử lý va chạm rồi.
428+
// Điều này ngăn một viên gạch bị nhiều bóng phá hủy trong cùng một frame.
429+
break;
430+
}
431+
}
432+
}
433+
}
346434
}

0 commit comments

Comments
 (0)