Skip to content

Commit 75d2992

Browse files
committed
air hockey online
1 parent 8768121 commit 75d2992

File tree

5 files changed

+91
-71
lines changed

5 files changed

+91
-71
lines changed

07-Ping-Pong-Game/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
<button id="pause-btn" class="btn">Pause</button>
3939
<button id="restart-btn" class="btn">Restart</button>
4040
</div>
41-
<p class="control">Control: Player Left(W and S) | Player Right(↑ and ↓)</p>
41+
<p class="control">Control: Player Left(W and S)/mouse/touch | Player Right(↑ and ↓)/mouse/touch</p>
4242
<!-- Add a modal element with an ID -->
4343
<div class="modal" tabindex="-1" role="dialog" id="message-modal">
4444
<div class="modal-dialog" role="document">

07-Ping-Pong-Game/style.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
canvas {
3838
background: #000;
39-
width: 80vmin;
39+
width: 95vmin;
4040
}
4141
body {
4242
overscroll-behavior-y: contain;

31-Air-Hockey-Game/index.html

Lines changed: 81 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<html>
33

44
<head>
5-
<title>Ping Pong Game</title>
5+
<title>Air Hockey Game</title>
66
<meta charset="UTF-8">
77
<meta name="viewport" content="user-scalable=no maximum-scale=1.0">
88
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" />
@@ -61,6 +61,7 @@
6161
<style>
6262
canvas {
6363
border: 1px solid black;
64+
width: 95vmin;
6465
}
6566
</style>
6667
</head>
@@ -74,7 +75,7 @@
7475
<button id="pause-btn" class="btn">Pause</button>
7576
<button id="restart-btn" class="btn">Restart</button>
7677
</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>
7879
<!-- Add a modal element with an ID -->
7980
<div class="modal" tabindex="-1" role="dialog" id="message-modal">
8081
<div class="modal-dialog" role="document">
@@ -133,10 +134,10 @@ <h5 id="message"></h5>
133134
var ballSpeedY = 4;
134135

135136
// 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;
140141
var leftPaddleX = 0;
141142
var rightPaddleX = canvas.width;
142143
var paddleSpeed = 10;
@@ -145,25 +146,18 @@ <h5 id="message"></h5>
145146
// Define score properties
146147
var leftPlayerScore = 0;
147148
var rightPlayerScore = 0;
148-
var maxScore = 100;
149+
var maxScore = 10;
149150
var touchYstartLeft = null;
150151
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;
153154
var touchYstartRight = null;
154155
var touchXstartRight = null;
155-
var previousYPositionRight = canvas.height / 2 ;
156+
var previousYPositionRight = canvas.height / 2;
156157
var previousXPositionRight = canvas.width - edgeWidth;
157158
const canvasBounds = canvas.getBoundingClientRect();
158159
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+
167161

168162
// Listen for keyboard events
169163
document.addEventListener("keydown", keyDownHandler);
@@ -220,35 +214,49 @@ <h5 id="message"></h5>
220214
}
221215
}
222216

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+
}
242250

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);
248256

249-
// 检查是否碰撞
250-
return distance <= r + R;
251-
}
257+
// 检查是否碰撞
258+
return distance <= r + R;
259+
}
252260

253261
// Update game state
254262
function update() {
@@ -298,11 +306,13 @@ <h5 id="message"></h5>
298306
ballSpeedX = -ballSpeedX;
299307
}*/
300308

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+
}
306316

307317
// Check if ball collides with right paddle
308318
/*if (
@@ -312,14 +322,16 @@ <h5 id="message"></h5>
312322
) {
313323
ballSpeedX = -ballSpeedX;
314324
}*/
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+
}
320332
// 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) {
323335
rightPlayerScore++;
324336
reset();
325337
} else if (ballX > canvas.width) {
@@ -362,17 +374,17 @@ <h5 id="message"></h5>
362374
ctx.clearRect(0, 0, canvas.width, canvas.height);
363375
var x = edgeWidth;
364376
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;
367379
var borderWidth = 6;
368380
ctx.strokeStyle = ballColor; // 边框颜色
369381
ctx.lineWidth = borderWidth; // 边框宽度
370382
// 绘制矩形框
371383
ctx.strokeRect(x, y, width, height);
372384
ctx.fillStyle = 'black'; // 填充颜色为黑色
373385
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;
376388
var coverHeight = goalWidth;
377389
// 绘制黑色矩形
378390
ctx.fillRect(coverX, coverY, coverWidth, coverHeight);
@@ -395,14 +407,14 @@ <h5 id="message"></h5>
395407

396408
// Draw left paddle
397409
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);
399411
ctx.fillStyle = ballColor;
400412
ctx.fill();
401413
ctx.closePath();
402414

403415
// Draw right paddle
404416
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);
406418
ctx.fillStyle = ballColor;
407419
ctx.fill();
408420
ctx.closePath();
@@ -442,11 +454,11 @@ <h5 id="message"></h5>
442454
}
443455
}
444456
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; }
446458
return;
447459
} else {//如果是鼠标 touchXstart等于鼠标的x坐标
448460
if (e.pageX < canvasMiddle) {
449-
if (touchYstartLeft == null) { touchYstartLeft = e.pageY;touchXstartLeft = e.pageX; }
461+
if (touchYstartLeft == null) { touchYstartLeft = e.pageY; touchXstartLeft = e.pageX; }
450462
} else {
451463
if (touchYstartRight == null) { touchYstartRight = e.pageY; touchXstartRight = e.pageX; }
452464
}
@@ -512,7 +524,7 @@ <h5 id="message"></h5>
512524
leftPaddleY = Math.max(0, Math.min(canvas.height - paddleHeight, leftPaddleY));
513525
let distXLeft = leftTouch.pageX - touchXstartLeft;
514526
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));
516528
}
517529
} catch (e) { }
518530

@@ -524,7 +536,7 @@ <h5 id="message"></h5>
524536
rightPaddleY = Math.max(0, Math.min(canvas.height - paddleHeight, rightPaddleY));
525537
let distXRight = rightTouch.pageX - touchXstartRight;
526538
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));
528540
}
529541
} catch (e) { }
530542
}
@@ -541,7 +553,7 @@ <h5 id="message"></h5>
541553
leftPaddleY = Math.max(0, Math.min(canvas.height - paddleHeight, leftPaddleY));
542554
let distXLeft = mouseX - touchXstartLeft;
543555
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));
545557
}
546558
} else {
547559
if (touchYstartRight !== null) {
@@ -550,7 +562,7 @@ <h5 id="message"></h5>
550562
rightPaddleY = Math.max(0, Math.min(canvas.height - paddleHeight, rightPaddleY));
551563
let distXRight = mouseX - touchXstartRight;
552564
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));
554566
}
555567
}
556568
}

index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ <h1>Game List</h1> </br>
173173
<td>30</td>
174174
<td><a href="30-Guess-Number-Game/">Guess Number Game</a>: Guess a 4-digit number with A for right position and B for right number. Only eight chances. A good game for logical thinking.🧠🤖</td>
175175
</tr>
176+
<tr>
177+
<td>31</td>
178+
<td><a href="31-Air-Hockey-Game/">Air Hockey Game</a>: A ping-pong like game with two players. control with mouse or keyboard or multi-touch screen. Play with your friends in this classic game. 🏀🏑</td>
179+
</tr>
176180
</tbody>
177181
</table>
178182
</body>

indexCN.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ <h1>游戏列表</h1> </br>
173173
<td>30</td>
174174
<td><a href="30-Guess-Number-Game/">猜数字游戏</a>:猜测一个 4 位数字,A 表示位置正确,B 表示数字正确。只有八次机会。一个锻炼逻辑思维的好游戏。🧠🤖</td>
175175
</tr>
176+
<tr>
177+
<td>31</td>
178+
<td><a href="31-Air-Hockey-Game/">空气曲棍球游戏</a>:一个类似乒乓球的游戏,用键盘或鼠标或多触点屏幕来进行对战吧!🏀 🏑</td>
179+
</tr>
176180
</tbody>
177181
</table>
178182
</body>

0 commit comments

Comments
 (0)