Skip to content
This repository was archived by the owner on Dec 24, 2025. It is now read-only.

Commit 240a68d

Browse files
committed
Update physics and wall logic for Ball–Brick–Paddle collisions
1 parent eba320e commit 240a68d

2 files changed

Lines changed: 258 additions & 268 deletions

File tree

src/main/java/com/github/codestorm/bounceverse/core/systems/PhysicSystem.java

Lines changed: 152 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -42,210 +42,204 @@ public void apply() {
4242
// Bullet vs Brick
4343
physicWorld.addCollisionHandler(
4444
new CollisionHandler(EntityType.BULLET, EntityType.BRICK) {
45-
@Override
46-
protected void onCollisionBegin(Entity bullet, Entity brick) {
47-
final var atk = bullet.getComponentOptional(Attack.class);
48-
if (atk.isEmpty()) {
49-
return;
50-
}
51-
atk.get().execute(List.of(brick));
52-
}
53-
});
45+
@Override
46+
protected void onCollisionBegin(Entity bullet, Entity brick) {
47+
final var atk = bullet.getComponentOptional(Attack.class);
48+
if (atk.isEmpty()) {
49+
return;
50+
}
51+
atk.get().execute(List.of(brick));
52+
}
53+
});
5454

5555
// Ball vs Brick
5656
physicWorld.addCollisionHandler(
5757
new CollisionHandler(EntityType.BALL, EntityType.BRICK) {
58-
@Override
59-
protected void onCollisionBegin(Entity ball, Entity brick) {
60-
// collision
61-
final var physics = ball.getComponent(PhysicsComponent.class);
62-
63-
final var collisionDirection = Utils.Collision.getCollisionDirection(ball, brick);
64-
65-
if (collisionDirection == null) {
66-
// Fallback theo overlap depth
67-
double dx = ball.getCenter().getX() - brick.getCenter().getX();
68-
double dy = ball.getCenter().getY() - brick.getCenter().getY();
69-
double overlapX = (ball.getWidth() + brick.getWidth()) / 2.0 - Math.abs(dx);
70-
double overlapY = (ball.getHeight() + brick.getHeight()) / 2.0 - Math.abs(dy);
71-
72-
if (overlapX < overlapY) {
73-
// Va ngang mạnh hơn -> lật X
74-
physics.setLinearVelocity(-physics.getVelocityX(), physics.getVelocityY());
75-
} else {
76-
// Va dọc mạnh hơn -> lật Y
77-
physics.setLinearVelocity(physics.getVelocityX(), -physics.getVelocityY());
78-
}
79-
80-
// damage
81-
ball.getComponent(Attack.class).execute(List.of(brick));
82-
return;
83-
}
58+
@Override
59+
protected void onCollisionBegin(Entity ball, Entity brick) {
60+
// collision
61+
final var physics = ball.getComponent(PhysicsComponent.class);
62+
63+
final var collisionDirection = Utils.Collision.getCollisionDirection(ball, brick);
64+
65+
if (collisionDirection == null) {
66+
// Fallback theo overlap depth
67+
double dx = ball.getCenter().getX() - brick.getCenter().getX();
68+
double dy = ball.getCenter().getY() - brick.getCenter().getY();
69+
double overlapX = (ball.getWidth() + brick.getWidth()) / 2.0 - Math.abs(dx);
70+
double overlapY = (ball.getHeight() + brick.getHeight()) / 2.0 - Math.abs(dy);
71+
72+
if (overlapX < overlapY) {
73+
// Va ngang mạnh hơn -> lật X
74+
physics.setLinearVelocity(-physics.getVelocityX(), physics.getVelocityY());
75+
} else {
76+
// Va dọc mạnh hơn -> lật Y
77+
physics.setLinearVelocity(physics.getVelocityX(), -physics.getVelocityY());
78+
}
79+
80+
// damage
81+
ball.getComponent(Attack.class).execute(List.of(brick));
82+
return;
83+
}
8484

85-
switch (collisionDirection) {
86-
case UP, DOWN ->
87-
physics.setLinearVelocity(physics.getVelocityX(), -physics.getVelocityY());
88-
case LEFT, RIGHT ->
89-
physics.setLinearVelocity(-physics.getVelocityX(), physics.getVelocityY());
90-
}
85+
switch (collisionDirection) {
86+
case UP, DOWN ->
87+
physics.setLinearVelocity(physics.getVelocityX(), -physics.getVelocityY());
88+
case LEFT, RIGHT ->
89+
physics.setLinearVelocity(-physics.getVelocityX(), physics.getVelocityY());
90+
}
9191

92-
// damage
93-
ball.getComponent(Attack.class).execute(List.of(brick));
94-
}
95-
});
92+
// damage
93+
ball.getComponent(Attack.class).execute(List.of(brick));
94+
}
95+
});
9696

9797
// Ball vs Paddle
9898
physicWorld.addCollisionHandler(
9999
new CollisionHandler(EntityType.BALL, EntityType.PADDLE) {
100-
@Override
101-
protected void onCollisionBegin(Entity ball, Entity paddle) {
102-
final var physics = ball.getComponent(PhysicsComponent.class);
100+
@Override
101+
protected void onCollisionBegin(Entity ball, Entity paddle) {
102+
final var physics = ball.getComponent(PhysicsComponent.class);
103103

104-
double vx = physics.getVelocityX();
105-
double vy = physics.getVelocityY();
106-
if (vx == 0 && vy == 0) {
107-
return;
108-
}
104+
double vx = physics.getVelocityX();
105+
double vy = physics.getVelocityY();
106+
if (vx == 0 && vy == 0) {
107+
return;
108+
}
109109

110-
double ballCenterX = ball.getCenter().getX();
111-
double paddleCenterX = paddle.getCenter().getX();
112-
double halfPaddleWidth = paddle.getWidth() / 2.0;
113-
double relative = 0;
114-
if (halfPaddleWidth != 0) {
115-
relative = (ballCenterX - paddleCenterX) / halfPaddleWidth; // -1 .. 1
116-
if (relative < -1) {
117-
relative = -1;
118-
}
119-
if (relative > 1) {
120-
relative = 1;
121-
}
122-
}
110+
double ballCenterX = ball.getCenter().getX();
111+
double paddleCenterX = paddle.getCenter().getX();
112+
double halfPaddleWidth = paddle.getWidth() / 2.0;
113+
double relative = 0;
114+
if (halfPaddleWidth != 0) {
115+
relative = (ballCenterX - paddleCenterX) / halfPaddleWidth; // -1 .. 1
116+
if (relative < -1) {
117+
relative = -1;
118+
}
119+
if (relative > 1) {
120+
relative = 1;
121+
}
122+
}
123123

124-
double speed = Math.hypot(vx, vy);
125-
double maxBounceAngle = Math.toRadians(75);
126-
double angle = relative * maxBounceAngle;
124+
double speed = Math.hypot(vx, vy);
125+
double maxBounceAngle = Math.toRadians(75);
126+
double angle = relative * maxBounceAngle;
127127

128-
double newVx = speed * Math.sin(angle);
129-
double newVy = -Math.abs(speed * Math.cos(angle));
128+
double newVx = speed * Math.sin(angle);
129+
double newVy = -Math.abs(speed * Math.cos(angle));
130130

131-
physics.setLinearVelocity(newVx, newVy);
132-
}
133-
});
131+
physics.setLinearVelocity(newVx, newVy);
132+
}
133+
});
134134

135135
// Ball vs Wall
136136
physicWorld.addCollisionHandler(
137137
new CollisionHandler(EntityType.BALL, EntityType.WALL) {
138-
@Override
139-
protected void onCollisionBegin(Entity ball, Entity wall) {
140-
final DirectionUnit collisionDirection = Utils.Collision.getCollisionDirection(ball, wall);
138+
@Override
139+
protected void onCollisionBegin(Entity ball, Entity wall) {
140+
final DirectionUnit collisionDirection = Utils.Collision.getCollisionDirection(ball, wall);
141141

142-
final var physics = ball.getComponent(PhysicsComponent.class);
143-
double vx = physics.getVelocityX();
144-
double vy = physics.getVelocityY();
142+
final var physics = ball.getComponent(PhysicsComponent.class);
143+
double vx = physics.getVelocityX();
144+
double vy = physics.getVelocityY();
145145

146-
if (collisionDirection == null) {
147-
return;
148-
}
149-
150-
switch (collisionDirection) {
151-
case UP -> {
152-
// chạm trần -> đi xuống
153-
physics.setLinearVelocity(vx, Math.abs(vy));
154-
}
155-
case DOWN -> {
156-
int lives = GameVars.decrementLives();
157-
158-
physics.setLinearVelocity(0, 0);
159-
160-
// reset ball lên giữa paddle
161-
var paddles = FXGL.getGameWorld().getEntitiesByType(EntityType.PADDLE);
162-
if (!paddles.isEmpty()) {
163-
Entity paddle = paddles.iterator().next();
164-
double px = paddle.getCenter().getX();
165-
double py = paddle.getY();
166-
double bw = ball.getWidth();
167-
double bh = ball.getHeight();
168-
169-
ball.setPosition(px - bw / 2.0, py - bh - 2.0);
170-
}
171-
172-
if (lives <= 0) {
173-
FXGL.getGameController().exit();
146+
if (collisionDirection == null) {
174147
return;
175148
}
176149

177-
double speed = Math.max(150, Math.hypot(vx, vy));
178-
FXGL.getGameTimer().runOnceAfter(()
179-
-> ball.getComponent(PhysicsComponent.class)
150+
switch (collisionDirection) {
151+
case UP -> {
152+
// chạm trần -> đi xuống
153+
physics.setLinearVelocity(vx, Math.abs(vy));
154+
}
155+
case DOWN -> {
156+
int lives = GameVars.decrementLives();
157+
158+
physics.setLinearVelocity(0, 0);
159+
160+
// reset ball lên giữa paddle
161+
var paddles = FXGL.getGameWorld().getEntitiesByType(EntityType.PADDLE);
162+
if (!paddles.isEmpty()) {
163+
Entity paddle = paddles.iterator().next();
164+
double px = paddle.getCenter().getX();
165+
double py = paddle.getY();
166+
double bw = ball.getWidth();
167+
double bh = ball.getHeight();
168+
169+
ball.setPosition(px - bw / 2.0, py - bh - 2.0);
170+
}
171+
172+
if (lives <= 0) {
173+
FXGL.getGameController().exit();
174+
return;
175+
}
176+
177+
double speed = Math.max(150, Math.hypot(vx, vy));
178+
FXGL.getGameTimer().runOnceAfter(() -> ball.getComponent(PhysicsComponent.class)
180179
.setLinearVelocity(0, -Math.abs(speed)),
181-
new Duration(200)
182-
);
183-
}
184-
case LEFT -> {
185-
// 1) Ngắt X để tránh engine override
186-
physics.setLinearVelocity(0, vy);
187-
188-
// 2) Đặt ra ngoài tường (dựa vào biên phải của wall)
189-
ball.setX(wall.getRightX() + 2.0);
190-
191-
// 3) Sau 5ms, set lại tốc độ với độ lớn ổn định
192-
FXGL.getGameTimer().runOnceAfter(() -> {
193-
double speed = Math.max(150, Math.hypot(vx, vy));
194-
physics.setLinearVelocity(Math.abs(speed), vy);
195-
}, Duration.millis(5));
196-
}
197-
case RIGHT -> {
198-
physics.setLinearVelocity(0, vy);
199-
200-
// biên trái của wall trừ bề rộng ball
201-
ball.setX(wall.getX() - ball.getWidth() - 2.0);
202-
203-
FXGL.getGameTimer().runOnceAfter(() -> {
204-
double speed = Math.max(150, Math.hypot(vx, vy));
205-
physics.setLinearVelocity(-Math.abs(speed), vy);
206-
}, Duration.millis(5));
207-
}
208-
default -> {
180+
new Duration(200));
181+
}
182+
case LEFT -> {
183+
// 1) Ngắt X để tránh engine override
184+
physics.setLinearVelocity(0, vy);
185+
186+
// 2) Đặt ra ngoài tường (dựa vào biên phải của wall)
187+
ball.setX(wall.getRightX() + 2.0);
188+
189+
// 3) Sau 5ms, set lại tốc độ với độ lớn ổn định
190+
FXGL.getGameTimer().runOnceAfter(() -> {
191+
double speed = Math.max(150, Math.hypot(vx, vy));
192+
physics.setLinearVelocity(Math.abs(speed), vy);
193+
}, Duration.millis(5));
194+
}
195+
case RIGHT -> {
196+
physics.setLinearVelocity(0, vy);
197+
198+
// biên trái của wall trừ bề rộng ball
199+
ball.setX(wall.getX() - ball.getWidth() - 2.0);
200+
201+
FXGL.getGameTimer().runOnceAfter(() -> {
202+
double speed = Math.max(150, Math.hypot(vx, vy));
203+
physics.setLinearVelocity(-Math.abs(speed), vy);
204+
}, Duration.millis(5));
205+
}
206+
default -> {
207+
}
208+
}
209209
}
210-
}
211-
}
212-
}
213-
);
210+
});
214211

215-
// Paddle vs Wall
216-
physicWorld.addCollisionHandler(
217-
new CollisionHandler(EntityType.PADDLE, EntityType.WALL) {
212+
// Paddle vs Wall
213+
physicWorld.addCollisionHandler(new CollisionHandler(EntityType.PADDLE, EntityType.WALL) {
218214
@Override
219215
protected void onCollisionBegin(Entity paddle, Entity wall) {
220-
// Tính hướng va chạm
221-
final var dir = Utils.Collision.getCollisionDirection(paddle, wall);
222-
if (dir == null) {
216+
var dir = Utils.Collision.getCollisionDirection(paddle, wall);
217+
if (dir == null)
223218
return;
224-
}
225219

220+
var physics = paddle.getComponent(PhysicsComponent.class);
226221
switch (dir) {
227222
case LEFT -> {
228-
// Paddle va vào tường trái -> đặt sát mép phải của wall
229-
paddle.setX(wall.getRightX());
223+
paddle.setX(wall.getRightX() + 0.5);
224+
physics.setVelocityX(0);
230225
}
231226
case RIGHT -> {
232-
// Paddle va vào tường phải -> đặt sát mép trái của wall
233-
paddle.setX(wall.getX() - paddle.getWidth());
227+
paddle.setX(wall.getX() - paddle.getWidth() - 0.5);
228+
physics.setVelocityX(0);
234229
}
235230
default -> {
236-
// Bỏ qua UP/DOWN cho paddle
237231
}
238232
}
239233
}
240-
}
241-
);
234+
});
242235

243236
}
244237

245238
/**
246239
* Lazy-loaded singleton holder. <br>
247240
* Follow
248-
* <a href= "https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom">
241+
* <a href=
242+
* "https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom">
249243
* Initialization-on-demand holder idiom</a>.
250244
*/
251245
private static final class Holder {

0 commit comments

Comments
 (0)