2
2
< html >
3
3
4
4
< head >
5
- < title > Ping Pong Game</ title >
5
+ < title > Air Hockey Game</ title >
6
6
< meta charset ="UTF-8 ">
7
7
< meta name ="viewport " content ="user-scalable=no maximum-scale=1.0 ">
8
8
< link rel ="stylesheet " href ="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css " />
61
61
< style >
62
62
canvas {
63
63
border : 1px solid black;
64
+ width : 95vmin ;
64
65
}
65
66
</ style >
66
67
</ head >
74
75
< button id ="pause-btn " class ="btn "> Pause</ button >
75
76
< button id ="restart-btn " class ="btn "> Restart</ button >
76
77
</ div >
77
- < p class ="control "> Control: Player Left(W and S) | Player Right(↑ and ↓) </ p >
78
+ < p class ="control "> Control: Player Left(W S A D)/mouse/touch | Player Right(↑ ↓ ← ↓)/mouse/touch </ p >
78
79
<!-- Add a modal element with an ID -->
79
80
< div class ="modal " tabindex ="-1 " role ="dialog " id ="message-modal ">
80
81
< div class ="modal-dialog " role ="document ">
@@ -133,10 +134,10 @@ <h5 id="message"></h5>
133
134
var ballSpeedY = 4 ;
134
135
135
136
// Define paddle properties
136
- var paddleHeight = 80 ;
137
- var paddleWidth = paddleHeight / 2 ;
138
- var leftPaddleY = canvas . height / 2 ;
139
- var rightPaddleY = canvas . height / 2 ;
137
+ var paddleHeight = 40 ;
138
+ var paddleWidth = paddleHeight / 2 ;
139
+ var leftPaddleY = canvas . height / 2 ;
140
+ var rightPaddleY = canvas . height / 2 ;
140
141
var leftPaddleX = 0 ;
141
142
var rightPaddleX = canvas . width ;
142
143
var paddleSpeed = 10 ;
@@ -145,25 +146,18 @@ <h5 id="message"></h5>
145
146
// Define score properties
146
147
var leftPlayerScore = 0 ;
147
148
var rightPlayerScore = 0 ;
148
- var maxScore = 100 ;
149
+ var maxScore = 10 ;
149
150
var touchYstartLeft = null ;
150
151
var touchXstartLeft = null ;
151
- var previousYPositionLeft = canvas . height / 2 ;
152
- var previousXPositionLeft = 0 + edgeWidth ;
152
+ var previousYPositionLeft = canvas . height / 2 ;
153
+ var previousXPositionLeft = 0 + edgeWidth ;
153
154
var touchYstartRight = null ;
154
155
var touchXstartRight = null ;
155
- var previousYPositionRight = canvas . height / 2 ;
156
+ var previousYPositionRight = canvas . height / 2 ;
156
157
var previousXPositionRight = canvas . width - edgeWidth ;
157
158
const canvasBounds = canvas . getBoundingClientRect ( ) ;
158
159
const canvasMiddle = canvasBounds . width / 2 + canvasBounds . left ;
159
- var effectiveBounds = {
160
- left : canvasBounds . left + edgeWidth ,
161
- right : canvasBounds . right - edgeWidth ,
162
- top : canvasBounds . top + edgeWidth ,
163
- bottom : canvasBounds . bottom - edgeWidth ,
164
- width : canvasBounds . width - 2 * edgeWidth ,
165
- height : canvasBounds . height - 2 * edgeWidth ,
166
- } ;
160
+
167
161
168
162
// Listen for keyboard events
169
163
document . addEventListener ( "keydown" , keyDownHandler ) ;
@@ -220,35 +214,49 @@ <h5 id="message"></h5>
220
214
}
221
215
}
222
216
223
- function calculateCollision ( sx , sy , px , py , qx , qy , r , R ) {
224
- // 计算小球到大球的单位法向量
225
- const dx = px - qx ;
226
- const dy = py - qy ;
227
- const distance = Math . sqrt ( dx * dx + dy * dy ) ;
228
-
229
- // 单位法向量
230
- const nx = dx / distance ;
231
- const ny = dy / distance ;
232
-
233
- // 计算小球在法向量方向的速度分量(投影)
234
- const vDotN = sx * nx + sy * ny ;
235
-
236
- // 计算法向量方向的反射速度分量
237
- const reflectedVx = sx - 2 * vDotN * nx ;
238
- const reflectedVy = sy - 2 * vDotN * ny ;
239
-
240
- return { reflectedVx, reflectedVy } ;
241
- }
217
+ function calculateCollision ( sx , sy , px , py , qx , qy , r , R ) {
218
+ // 计算小球到大球的单位法向量
219
+ const dx = px - qx ;
220
+ const dy = py - qy ;
221
+ const distance = Math . sqrt ( dx * dx + dy * dy ) ;
222
+
223
+ // 单位法向量
224
+ const nx = dx / distance ;
225
+ const ny = dy / distance ;
226
+
227
+ // 计算小球在法向量方向的速度分量(投影)
228
+ const vDotN = sx * nx + sy * ny ;
229
+ // 如果接近切线方向,避免出现反射到对侧的现象
230
+ if ( Math . abs ( vDotN ) < 0.5 ) {
231
+ // 处理切线碰撞的特例:此时仅改变法线方向的速度分量
232
+ const tangentialSx = sy * nx ;
233
+ const tangentialSy = - sx * ny ;
234
+ return {
235
+ reflectedVx : tangentialSx ,
236
+ reflectedVy : tangentialSy ,
237
+ newPx : px ,
238
+ newPy : py
239
+ } ;
240
+ }
241
+ // 计算法向量方向的反射速度分量
242
+ const reflectedVx = sx - 2 * vDotN * nx ;
243
+ const reflectedVy = sy - 2 * vDotN * ny ;
244
+ // 位置修正:将小球移出大球的表面
245
+ const overlap = r + R - distance ;
246
+ const newPx = px + nx * overlap ;
247
+ const newPy = py + ny * overlap ;
248
+ return { reflectedVx, reflectedVy, newPx, newPy } ;
249
+ }
242
250
243
- function hasCollided ( px , py , qx , qy , r , R ) {
244
- // 计算两个球中心的距离
245
- const dx = px - qx ;
246
- const dy = py - qy ;
247
- const distance = Math . sqrt ( dx * dx + dy * dy ) ;
251
+ function hasCollided ( px , py , qx , qy , r , R ) {
252
+ // 计算两个球中心的距离
253
+ const dx = px - qx ;
254
+ const dy = py - qy ;
255
+ const distance = Math . sqrt ( dx * dx + dy * dy ) ;
248
256
249
- // 检查是否碰撞
250
- return distance <= r + R ;
251
- }
257
+ // 检查是否碰撞
258
+ return distance <= r + R ;
259
+ }
252
260
253
261
// Update game state
254
262
function update ( ) {
@@ -298,11 +306,13 @@ <h5 id="message"></h5>
298
306
ballSpeedX = -ballSpeedX;
299
307
}*/
300
308
301
- if ( hasCollided ( ballX , ballY , leftPaddleX , leftPaddleY , ballRadius , paddleHeight / 2 ) ) {
302
- var ballMove = calculateCollision ( ballSpeedX , ballSpeedY , ballX , ballY , leftPaddleX , leftPaddleY , ballRadius , paddleHeight / 2 ) ;
303
- ballSpeedX = ballMove . reflectedVx ;
304
- ballSpeedY = ballMove . reflectedVy ;
305
- }
309
+ if ( hasCollided ( ballX , ballY , leftPaddleX , leftPaddleY , ballRadius , paddleHeight / 2 ) ) {
310
+ var ballMove = calculateCollision ( ballSpeedX , ballSpeedY , ballX , ballY , leftPaddleX , leftPaddleY , ballRadius , paddleHeight / 2 ) ;
311
+ ballSpeedX = ballMove . reflectedVx ;
312
+ ballSpeedY = ballMove . reflectedVy ;
313
+ ballX = ballMove . newPx ;
314
+ ballY = ballMove . newPy ;
315
+ }
306
316
307
317
// Check if ball collides with right paddle
308
318
/*if (
@@ -312,14 +322,16 @@ <h5 id="message"></h5>
312
322
) {
313
323
ballSpeedX = -ballSpeedX;
314
324
}*/
315
- if ( hasCollided ( ballX , ballY , rightPaddleX , rightPaddleY , ballRadius , paddleHeight / 2 ) ) {
316
- var ballMove = calculateCollision ( ballSpeedX , ballSpeedY , ballX , ballY , rightPaddleX , rightPaddleY , ballRadius , paddleHeight / 2 ) ;
317
- ballSpeedX = ballMove . reflectedVx ;
318
- ballSpeedY = ballMove . reflectedVy ;
319
- }
325
+ if ( hasCollided ( ballX , ballY , rightPaddleX , rightPaddleY , ballRadius , paddleHeight / 2 ) ) {
326
+ var ballMove = calculateCollision ( ballSpeedX , ballSpeedY , ballX , ballY , rightPaddleX , rightPaddleY , ballRadius , paddleHeight / 2 ) ;
327
+ ballSpeedX = ballMove . reflectedVx ;
328
+ ballSpeedY = ballMove . reflectedVy ;
329
+ ballX = ballMove . newPx ;
330
+ ballY = ballMove . newPy ;
331
+ }
320
332
// Check if ball goes out of bounds on left or right side of canvas
321
- if ( ballY > canvas . height / 2 - paddleHeight && ballY < canvas . height / 2 + paddleHeight ) {
322
- if ( ballX < effectiveBounds ) {
333
+ if ( ballY > canvas . height / 2 - goalWidth / 2 && ballY < canvas . height / 2 + goalWidth / 2 ) {
334
+ if ( ballX < 0 ) {
323
335
rightPlayerScore ++ ;
324
336
reset ( ) ;
325
337
} else if ( ballX > canvas . width ) {
@@ -362,17 +374,17 @@ <h5 id="message"></h5>
362
374
ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
363
375
var x = edgeWidth ;
364
376
var y = edgeWidth ;
365
- var width = canvas . width - edgeWidth * 2 ;
366
- var height = canvas . height - edgeWidth * 2 ;
377
+ var width = canvas . width - edgeWidth * 2 ;
378
+ var height = canvas . height - edgeWidth * 2 ;
367
379
var borderWidth = 6 ;
368
380
ctx . strokeStyle = ballColor ; // 边框颜色
369
381
ctx . lineWidth = borderWidth ; // 边框宽度
370
382
// 绘制矩形框
371
383
ctx . strokeRect ( x , y , width , height ) ;
372
384
ctx . fillStyle = 'black' ; // 填充颜色为黑色
373
385
var coverX = - edgeWidth ;
374
- var coverY = canvas . height / 2 - goalWidth / 2 ;
375
- var coverWidth = canvas . width + edgeWidth ;
386
+ var coverY = canvas . height / 2 - goalWidth / 2 ;
387
+ var coverWidth = canvas . width + edgeWidth ;
376
388
var coverHeight = goalWidth ;
377
389
// 绘制黑色矩形
378
390
ctx . fillRect ( coverX , coverY , coverWidth , coverHeight ) ;
@@ -395,14 +407,14 @@ <h5 id="message"></h5>
395
407
396
408
// Draw left paddle
397
409
ctx . beginPath ( ) ;
398
- ctx . arc ( leftPaddleX , leftPaddleY , paddleHeight / 2 , Math . PI * 3 / 2 , Math . PI * 5 / 2 ) ;
410
+ ctx . arc ( leftPaddleX , leftPaddleY , paddleHeight / 2 , 0 , Math . PI * 2 ) ; //Math.PI * 3 / 2, Math.PI * 5 / 2);
399
411
ctx . fillStyle = ballColor ;
400
412
ctx . fill ( ) ;
401
413
ctx . closePath ( ) ;
402
414
403
415
// Draw right paddle
404
416
ctx . beginPath ( ) ;
405
- ctx . arc ( rightPaddleX , rightPaddleY , paddleHeight / 2 , Math . PI * 1 / 2 , Math . PI * 3 / 2 ) ;
417
+ ctx . arc ( rightPaddleX , rightPaddleY , paddleHeight / 2 , 0 , Math . PI * 2 ) ; //Math.PI * 1 / 2 , Math.PI * 3 / 2);
406
418
ctx . fillStyle = ballColor ;
407
419
ctx . fill ( ) ;
408
420
ctx . closePath ( ) ;
@@ -442,11 +454,11 @@ <h5 id="message"></h5>
442
454
}
443
455
}
444
456
if ( touchYstartLeft == null && leftPt . pageY != - 1 ) { touchYstartLeft = leftPt . pageY ; touchXstartLeft = leftPt . pageX ; }
445
- if ( touchYstartRight == null && rightPt . pageY != - 1 ) { touchYstartRight = rightPt . pageY ; touchXstartRight = rightPt . pageX ; }
457
+ if ( touchYstartRight == null && rightPt . pageY != - 1 ) { touchYstartRight = rightPt . pageY ; touchXstartRight = rightPt . pageX ; }
446
458
return ;
447
459
} else { //如果是鼠标 touchXstart等于鼠标的x坐标
448
460
if ( e . pageX < canvasMiddle ) {
449
- if ( touchYstartLeft == null ) { touchYstartLeft = e . pageY ; touchXstartLeft = e . pageX ; }
461
+ if ( touchYstartLeft == null ) { touchYstartLeft = e . pageY ; touchXstartLeft = e . pageX ; }
450
462
} else {
451
463
if ( touchYstartRight == null ) { touchYstartRight = e . pageY ; touchXstartRight = e . pageX ; }
452
464
}
@@ -512,7 +524,7 @@ <h5 id="message"></h5>
512
524
leftPaddleY = Math . max ( 0 , Math . min ( canvas . height - paddleHeight , leftPaddleY ) ) ;
513
525
let distXLeft = leftTouch . pageX - touchXstartLeft ;
514
526
leftPaddleX = previousXPositionLeft + distXLeft ;
515
- leftPaddleX = Math . max ( 0 , Math . min ( canvas . width / 2 , leftPaddleX ) ) ;
527
+ leftPaddleX = Math . max ( 0 , Math . min ( canvas . width / 2 , leftPaddleX ) ) ;
516
528
}
517
529
} catch ( e ) { }
518
530
@@ -524,7 +536,7 @@ <h5 id="message"></h5>
524
536
rightPaddleY = Math . max ( 0 , Math . min ( canvas . height - paddleHeight , rightPaddleY ) ) ;
525
537
let distXRight = rightTouch . pageX - touchXstartRight ;
526
538
rightPaddleX = previousXPositionRight + distXRight ;
527
- rightPaddleX = Math . min ( canvas . width , Math . max ( canvas . width / 2 , rightPaddleX ) ) ;
539
+ rightPaddleX = Math . min ( canvas . width , Math . max ( canvas . width / 2 , rightPaddleX ) ) ;
528
540
}
529
541
} catch ( e ) { }
530
542
}
@@ -541,7 +553,7 @@ <h5 id="message"></h5>
541
553
leftPaddleY = Math . max ( 0 , Math . min ( canvas . height - paddleHeight , leftPaddleY ) ) ;
542
554
let distXLeft = mouseX - touchXstartLeft ;
543
555
leftPaddleX = previousXPositionLeft + distXLeft ;
544
- leftPaddleX = Math . max ( 0 , Math . min ( canvas . width - paddleWidth , leftPaddleX ) ) ;
556
+ leftPaddleX = Math . max ( 0 , Math . min ( canvas . width / 2 , leftPaddleX ) ) ;
545
557
}
546
558
} else {
547
559
if ( touchYstartRight !== null ) {
@@ -550,7 +562,7 @@ <h5 id="message"></h5>
550
562
rightPaddleY = Math . max ( 0 , Math . min ( canvas . height - paddleHeight , rightPaddleY ) ) ;
551
563
let distXRight = mouseX - touchXstartRight ;
552
564
rightPaddleX = previousXPositionRight + distXRight ;
553
- rightPaddleX = Math . max ( 0 , Math . min ( canvas . width - paddleWidth , rightPaddleX ) ) ;
565
+ rightPaddleX = Math . min ( canvas . width , Math . max ( canvas . width / 2 , rightPaddleX ) ) ;
554
566
}
555
567
}
556
568
}
0 commit comments