@@ -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