create-template-html-css 1.9.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/COMPONENTS-GALLERY.html +735 -747
- package/README.md +179 -2
- package/bin/cli.js +15 -3
- package/package.json +1 -1
- package/templates/blackjack/index.html +97 -0
- package/templates/blackjack/script.js +381 -0
- package/templates/blackjack/style.css +452 -0
- package/templates/breakout/index.html +56 -0
- package/templates/breakout/script.js +387 -0
- package/templates/breakout/style.css +239 -0
- package/templates/connect-four/index.html +78 -0
- package/templates/connect-four/script.js +413 -0
- package/templates/connect-four/style.css +426 -0
- package/templates/dice-game/index.html +99 -0
- package/templates/dice-game/script.js +291 -0
- package/templates/dice-game/style.css +403 -0
- package/templates/flappy-bird/index.html +47 -0
- package/templates/flappy-bird/script.js +394 -0
- package/templates/flappy-bird/style.css +243 -0
- package/templates/game-2048/index.html +59 -0
- package/templates/game-2048/script.js +269 -0
- package/templates/game-2048/style.css +281 -0
- package/templates/pong/index.html +90 -0
- package/templates/pong/script.js +364 -0
- package/templates/pong/style.css +371 -0
- package/templates/rock-paper-scissors/index.html +84 -0
- package/templates/rock-paper-scissors/script.js +199 -0
- package/templates/rock-paper-scissors/style.css +295 -0
- package/templates/simon-says/index.html +64 -0
- package/templates/simon-says/script.js +206 -0
- package/templates/simon-says/style.css +250 -0
- package/templates/slot-machine/index.html +112 -0
- package/templates/slot-machine/script.js +238 -0
- package/templates/slot-machine/style.css +464 -0
- package/templates/tetris/index.html +84 -0
- package/templates/tetris/script.js +447 -0
- package/templates/tetris/style.css +286 -0
- package/templates/whack-a-mole/index.html +85 -0
- package/templates/whack-a-mole/script.js +172 -0
- package/{demo-games/snake-game → templates/whack-a-mole}/style.css +114 -97
- package/PUBLISH-GUIDE.md +0 -112
- package/create-template-html-css-1.8.0.tgz +0 -0
- package/demo-games/guess-number/index.html +0 -71
- package/demo-games/guess-number/script.js +0 -216
- package/demo-games/guess-number/style.css +0 -337
- package/demo-games/memory-game/index.html +0 -50
- package/demo-games/memory-game/script.js +0 -216
- package/demo-games/memory-game/style.css +0 -288
- package/demo-games/snake-game/index.html +0 -61
- package/demo-games/snake-game/script.js +0 -360
- package/demo-games/tic-tac-toe/index.html +0 -57
- package/demo-games/tic-tac-toe/script.js +0 -156
- package/demo-games/tic-tac-toe/style.css +0 -244
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Flappy Bird</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<div class="header">
|
|
12
|
+
<h1>🐦 Flappy Bird</h1>
|
|
13
|
+
<p class="subtitle">Flap between the pipes!</p>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="game-info">
|
|
17
|
+
<div class="info-item">
|
|
18
|
+
<span class="label">Score:</span>
|
|
19
|
+
<span id="score" class="value">0</span>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="info-item">
|
|
22
|
+
<span class="label">High Score:</span>
|
|
23
|
+
<span id="highScore" class="value">0</span>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<canvas id="gameCanvas"></canvas>
|
|
28
|
+
|
|
29
|
+
<div class="controls">
|
|
30
|
+
<button id="startBtn" class="btn btn-primary">Start Game</button>
|
|
31
|
+
<button id="resetBtn" class="btn btn-danger">Reset</button>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div class="instructions">
|
|
35
|
+
<h3>🎮 How to Play?</h3>
|
|
36
|
+
<ul>
|
|
37
|
+
<li>Click on screen / spacebar / up arrow to jump</li>
|
|
38
|
+
<li>Pass between the pipes without crashing</li>
|
|
39
|
+
<li>Each pipe you pass scores a point</li>
|
|
40
|
+
<li>The game gets faster as you progress!</li>
|
|
41
|
+
</ul>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<script src="script.js"></script>
|
|
46
|
+
</body>
|
|
47
|
+
</html>
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
// Game constants
|
|
2
|
+
const canvas = document.getElementById('gameCanvas');
|
|
3
|
+
const ctx = canvas.getContext('2d');
|
|
4
|
+
|
|
5
|
+
canvas.width = 400;
|
|
6
|
+
canvas.height = 600;
|
|
7
|
+
|
|
8
|
+
// Game state
|
|
9
|
+
let gameState = {
|
|
10
|
+
running: false,
|
|
11
|
+
score: 0,
|
|
12
|
+
highScore: parseInt(localStorage.getItem('flappyHighScore')) || 0,
|
|
13
|
+
gameSpeed: 2
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Bird
|
|
17
|
+
const bird = {
|
|
18
|
+
x: 100,
|
|
19
|
+
y: canvas.height / 2,
|
|
20
|
+
width: 34,
|
|
21
|
+
height: 24,
|
|
22
|
+
velocity: 0,
|
|
23
|
+
gravity: 0.5,
|
|
24
|
+
jump: -9,
|
|
25
|
+
rotation: 0
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Pipes
|
|
29
|
+
let pipes = [];
|
|
30
|
+
const pipeConfig = {
|
|
31
|
+
width: 60,
|
|
32
|
+
gap: 180,
|
|
33
|
+
minHeight: 50,
|
|
34
|
+
maxHeight: canvas.height - 250,
|
|
35
|
+
distance: 250
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
let frameCount = 0;
|
|
39
|
+
let lastPipeFrame = 0;
|
|
40
|
+
|
|
41
|
+
// Background animation
|
|
42
|
+
let bgX = 0;
|
|
43
|
+
|
|
44
|
+
// Input handling
|
|
45
|
+
function handleJump() {
|
|
46
|
+
if (!gameState.running) return;
|
|
47
|
+
bird.velocity = bird.jump;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Mouse/Touch control
|
|
51
|
+
canvas.addEventListener('click', handleJump);
|
|
52
|
+
canvas.addEventListener('touchstart', (e) => {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
handleJump();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Keyboard control
|
|
58
|
+
document.addEventListener('keydown', (e) => {
|
|
59
|
+
if (e.key === ' ' || e.key === 'ArrowUp') {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
handleJump();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Create pipe
|
|
66
|
+
function createPipe() {
|
|
67
|
+
const height = Math.floor(Math.random() * (pipeConfig.maxHeight - pipeConfig.minHeight)) + pipeConfig.minHeight;
|
|
68
|
+
|
|
69
|
+
pipes.push({
|
|
70
|
+
x: canvas.width,
|
|
71
|
+
topHeight: height,
|
|
72
|
+
bottomY: height + pipeConfig.gap,
|
|
73
|
+
passed: false
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Update bird
|
|
78
|
+
function updateBird() {
|
|
79
|
+
bird.velocity += bird.gravity;
|
|
80
|
+
bird.y += bird.velocity;
|
|
81
|
+
|
|
82
|
+
// Rotation based on velocity
|
|
83
|
+
bird.rotation = Math.min(Math.max(bird.velocity * 3, -30), 90);
|
|
84
|
+
|
|
85
|
+
// Ground and ceiling collision
|
|
86
|
+
if (bird.y + bird.height > canvas.height || bird.y < 0) {
|
|
87
|
+
gameOver();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Update pipes
|
|
92
|
+
function updatePipes() {
|
|
93
|
+
// Create new pipe
|
|
94
|
+
if (frameCount - lastPipeFrame > pipeConfig.distance / gameState.gameSpeed) {
|
|
95
|
+
createPipe();
|
|
96
|
+
lastPipeFrame = frameCount;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Update and remove pipes
|
|
100
|
+
for (let i = pipes.length - 1; i >= 0; i--) {
|
|
101
|
+
pipes[i].x -= gameState.gameSpeed;
|
|
102
|
+
|
|
103
|
+
// Check if passed
|
|
104
|
+
if (!pipes[i].passed && pipes[i].x + pipeConfig.width < bird.x) {
|
|
105
|
+
pipes[i].passed = true;
|
|
106
|
+
gameState.score++;
|
|
107
|
+
updateScore();
|
|
108
|
+
|
|
109
|
+
// Increase speed slightly
|
|
110
|
+
gameState.gameSpeed += 0.05;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Remove off-screen pipes
|
|
114
|
+
if (pipes[i].x + pipeConfig.width < 0) {
|
|
115
|
+
pipes.splice(i, 1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check collision
|
|
121
|
+
function checkCollision() {
|
|
122
|
+
for (const pipe of pipes) {
|
|
123
|
+
// Check if bird is in pipe's x range
|
|
124
|
+
if (bird.x + bird.width > pipe.x && bird.x < pipe.x + pipeConfig.width) {
|
|
125
|
+
// Check collision with top pipe
|
|
126
|
+
if (bird.y < pipe.topHeight) {
|
|
127
|
+
gameOver();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Check collision with bottom pipe
|
|
131
|
+
if (bird.y + bird.height > pipe.bottomY) {
|
|
132
|
+
gameOver();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Draw background
|
|
140
|
+
function drawBackground() {
|
|
141
|
+
// Sky gradient
|
|
142
|
+
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
|
|
143
|
+
gradient.addColorStop(0, '#87CEEB');
|
|
144
|
+
gradient.addColorStop(1, '#E0F6FF');
|
|
145
|
+
ctx.fillStyle = gradient;
|
|
146
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
147
|
+
|
|
148
|
+
// Clouds
|
|
149
|
+
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
|
|
150
|
+
for (let i = 0; i < 3; i++) {
|
|
151
|
+
const x = (bgX + i * 200) % canvas.width;
|
|
152
|
+
drawCloud(x, 50 + i * 100);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Ground
|
|
156
|
+
ctx.fillStyle = '#DEB887';
|
|
157
|
+
ctx.fillRect(0, canvas.height - 50, canvas.width, 50);
|
|
158
|
+
|
|
159
|
+
// Grass
|
|
160
|
+
ctx.fillStyle = '#90EE90';
|
|
161
|
+
ctx.fillRect(0, canvas.height - 60, canvas.width, 10);
|
|
162
|
+
|
|
163
|
+
bgX -= 0.5;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Draw cloud
|
|
167
|
+
function drawCloud(x, y) {
|
|
168
|
+
ctx.beginPath();
|
|
169
|
+
ctx.arc(x, y, 20, 0, Math.PI * 2);
|
|
170
|
+
ctx.arc(x + 25, y, 25, 0, Math.PI * 2);
|
|
171
|
+
ctx.arc(x + 50, y, 20, 0, Math.PI * 2);
|
|
172
|
+
ctx.fill();
|
|
173
|
+
ctx.closePath();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Draw bird
|
|
177
|
+
function drawBird() {
|
|
178
|
+
ctx.save();
|
|
179
|
+
ctx.translate(bird.x + bird.width / 2, bird.y + bird.height / 2);
|
|
180
|
+
ctx.rotate((bird.rotation * Math.PI) / 180);
|
|
181
|
+
|
|
182
|
+
// Bird body
|
|
183
|
+
ctx.fillStyle = '#FFD700';
|
|
184
|
+
ctx.beginPath();
|
|
185
|
+
ctx.ellipse(0, 0, bird.width / 2, bird.height / 2, 0, 0, Math.PI * 2);
|
|
186
|
+
ctx.fill();
|
|
187
|
+
|
|
188
|
+
// Bird eye
|
|
189
|
+
ctx.fillStyle = 'white';
|
|
190
|
+
ctx.beginPath();
|
|
191
|
+
ctx.arc(10, -5, 6, 0, Math.PI * 2);
|
|
192
|
+
ctx.fill();
|
|
193
|
+
|
|
194
|
+
ctx.fillStyle = 'black';
|
|
195
|
+
ctx.beginPath();
|
|
196
|
+
ctx.arc(12, -5, 3, 0, Math.PI * 2);
|
|
197
|
+
ctx.fill();
|
|
198
|
+
|
|
199
|
+
// Bird beak
|
|
200
|
+
ctx.fillStyle = '#FF6347';
|
|
201
|
+
ctx.beginPath();
|
|
202
|
+
ctx.moveTo(15, 0);
|
|
203
|
+
ctx.lineTo(25, -3);
|
|
204
|
+
ctx.lineTo(25, 3);
|
|
205
|
+
ctx.closePath();
|
|
206
|
+
ctx.fill();
|
|
207
|
+
|
|
208
|
+
// Bird wing
|
|
209
|
+
ctx.fillStyle = '#FFA500';
|
|
210
|
+
ctx.beginPath();
|
|
211
|
+
ctx.ellipse(-5, 5, 8, 12, Math.sin(frameCount * 0.2) * 0.3, 0, Math.PI * 2);
|
|
212
|
+
ctx.fill();
|
|
213
|
+
|
|
214
|
+
ctx.restore();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Draw pipes
|
|
218
|
+
function drawPipes() {
|
|
219
|
+
ctx.fillStyle = '#228B22';
|
|
220
|
+
ctx.strokeStyle = '#1a6b1a';
|
|
221
|
+
ctx.lineWidth = 3;
|
|
222
|
+
|
|
223
|
+
for (const pipe of pipes) {
|
|
224
|
+
// Top pipe
|
|
225
|
+
ctx.fillRect(pipe.x, 0, pipeConfig.width, pipe.topHeight);
|
|
226
|
+
ctx.strokeRect(pipe.x, 0, pipeConfig.width, pipe.topHeight);
|
|
227
|
+
|
|
228
|
+
// Top pipe cap
|
|
229
|
+
ctx.fillRect(pipe.x - 5, pipe.topHeight - 30, pipeConfig.width + 10, 30);
|
|
230
|
+
ctx.strokeRect(pipe.x - 5, pipe.topHeight - 30, pipeConfig.width + 10, 30);
|
|
231
|
+
|
|
232
|
+
// Bottom pipe
|
|
233
|
+
ctx.fillRect(pipe.x, pipe.bottomY, pipeConfig.width, canvas.height - pipe.bottomY);
|
|
234
|
+
ctx.strokeRect(pipe.x, pipe.bottomY, pipeConfig.width, canvas.height - pipe.bottomY);
|
|
235
|
+
|
|
236
|
+
// Bottom pipe cap
|
|
237
|
+
ctx.fillRect(pipe.x - 5, pipe.bottomY, pipeConfig.width + 10, 30);
|
|
238
|
+
ctx.strokeRect(pipe.x - 5, pipe.bottomY, pipeConfig.width + 10, 30);
|
|
239
|
+
|
|
240
|
+
// Add highlight
|
|
241
|
+
ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
|
|
242
|
+
ctx.fillRect(pipe.x, 0, pipeConfig.width / 3, pipe.topHeight);
|
|
243
|
+
ctx.fillRect(pipe.x, pipe.bottomY, pipeConfig.width / 3, canvas.height - pipe.bottomY);
|
|
244
|
+
ctx.fillStyle = '#228B22';
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Draw score
|
|
249
|
+
function drawGameScore() {
|
|
250
|
+
ctx.fillStyle = 'white';
|
|
251
|
+
ctx.strokeStyle = 'black';
|
|
252
|
+
ctx.lineWidth = 3;
|
|
253
|
+
ctx.font = 'bold 48px Arial';
|
|
254
|
+
ctx.textAlign = 'center';
|
|
255
|
+
ctx.strokeText(gameState.score, canvas.width / 2, 80);
|
|
256
|
+
ctx.fillText(gameState.score, canvas.width / 2, 80);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Game over
|
|
260
|
+
function gameOver() {
|
|
261
|
+
gameState.running = false;
|
|
262
|
+
|
|
263
|
+
if (gameState.score > gameState.highScore) {
|
|
264
|
+
gameState.highScore = gameState.score;
|
|
265
|
+
localStorage.setItem('flappyHighScore', gameState.highScore);
|
|
266
|
+
updateHighScore();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Draw game over screen
|
|
270
|
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
|
271
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
272
|
+
|
|
273
|
+
ctx.fillStyle = 'white';
|
|
274
|
+
ctx.strokeStyle = 'black';
|
|
275
|
+
ctx.lineWidth = 3;
|
|
276
|
+
ctx.font = 'bold 48px Arial';
|
|
277
|
+
ctx.textAlign = 'center';
|
|
278
|
+
ctx.strokeText('Game Over!', canvas.width / 2, canvas.height / 2 - 50);
|
|
279
|
+
ctx.fillText('Game Over!', canvas.width / 2, canvas.height / 2 - 50);
|
|
280
|
+
|
|
281
|
+
ctx.font = '24px Arial';
|
|
282
|
+
ctx.strokeText(`ניקוד: ${gameState.score}`, canvas.width / 2, canvas.height / 2 + 10);
|
|
283
|
+
ctx.fillText(`ניקוד: ${gameState.score}`, canvas.width / 2, canvas.height / 2 + 10);
|
|
284
|
+
|
|
285
|
+
ctx.strokeText(`שיא: ${gameState.highScore}`, canvas.width / 2, canvas.height / 2 + 50);
|
|
286
|
+
ctx.fillText(`שיא: ${gameState.highScore}`, canvas.width / 2, canvas.height / 2 + 50);
|
|
287
|
+
|
|
288
|
+
ctx.font = '18px Arial';
|
|
289
|
+
ctx.strokeText('לחץ "אתחל" להתחיל מחדש', canvas.width / 2, canvas.height / 2 + 100);
|
|
290
|
+
ctx.fillText('לחץ "אתחל" להתחיל מחדש', canvas.width / 2, canvas.height / 2 + 100);
|
|
291
|
+
|
|
292
|
+
document.getElementById('startBtn').disabled = false;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Update UI
|
|
296
|
+
function updateScore() {
|
|
297
|
+
document.getElementById('score').textContent = gameState.score;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function updateHighScore() {
|
|
301
|
+
document.getElementById('highScore').textContent = gameState.highScore;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Draw start screen
|
|
305
|
+
function drawStartScreen() {
|
|
306
|
+
drawBackground();
|
|
307
|
+
drawBird();
|
|
308
|
+
|
|
309
|
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
|
|
310
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
311
|
+
|
|
312
|
+
ctx.fillStyle = 'white';
|
|
313
|
+
ctx.strokeStyle = 'black';
|
|
314
|
+
ctx.lineWidth = 3;
|
|
315
|
+
ctx.font = 'bold 36px Arial';
|
|
316
|
+
ctx.textAlign = 'center';
|
|
317
|
+
ctx.strokeText('🐦 Flappy Bird', canvas.width / 2, canvas.height / 2 - 40);
|
|
318
|
+
ctx.fillText('🐦 Flappy Bird', canvas.width / 2, canvas.height / 2 - 40);
|
|
319
|
+
|
|
320
|
+
ctx.font = '20px Arial';
|
|
321
|
+
ctx.strokeText('לחץ "התחל משחק"', canvas.width / 2, canvas.height / 2 + 20);
|
|
322
|
+
ctx.fillText('לחץ "התחל משחק"', canvas.width / 2, canvas.height / 2 + 20);
|
|
323
|
+
|
|
324
|
+
ctx.strokeText('להתחיל!', canvas.width / 2, canvas.height / 2 + 50);
|
|
325
|
+
ctx.fillText('להתחיל!', canvas.width / 2, canvas.height / 2 + 50);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Game loop
|
|
329
|
+
function gameLoop() {
|
|
330
|
+
if (!gameState.running) return;
|
|
331
|
+
|
|
332
|
+
frameCount++;
|
|
333
|
+
|
|
334
|
+
updateBird();
|
|
335
|
+
updatePipes();
|
|
336
|
+
checkCollision();
|
|
337
|
+
|
|
338
|
+
drawBackground();
|
|
339
|
+
drawPipes();
|
|
340
|
+
drawBird();
|
|
341
|
+
drawGameScore();
|
|
342
|
+
|
|
343
|
+
requestAnimationFrame(gameLoop);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Start game
|
|
347
|
+
function startGame() {
|
|
348
|
+
gameState.running = true;
|
|
349
|
+
gameState.score = 0;
|
|
350
|
+
gameState.gameSpeed = 2;
|
|
351
|
+
|
|
352
|
+
bird.y = canvas.height / 2;
|
|
353
|
+
bird.velocity = 0;
|
|
354
|
+
bird.rotation = 0;
|
|
355
|
+
|
|
356
|
+
pipes = [];
|
|
357
|
+
frameCount = 0;
|
|
358
|
+
lastPipeFrame = 0;
|
|
359
|
+
|
|
360
|
+
updateScore();
|
|
361
|
+
|
|
362
|
+
document.getElementById('startBtn').disabled = true;
|
|
363
|
+
|
|
364
|
+
gameLoop();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Reset game
|
|
368
|
+
function resetGame() {
|
|
369
|
+
gameState.running = false;
|
|
370
|
+
gameState.score = 0;
|
|
371
|
+
gameState.gameSpeed = 2;
|
|
372
|
+
|
|
373
|
+
bird.y = canvas.height / 2;
|
|
374
|
+
bird.velocity = 0;
|
|
375
|
+
bird.rotation = 0;
|
|
376
|
+
|
|
377
|
+
pipes = [];
|
|
378
|
+
frameCount = 0;
|
|
379
|
+
lastPipeFrame = 0;
|
|
380
|
+
|
|
381
|
+
updateScore();
|
|
382
|
+
|
|
383
|
+
drawStartScreen();
|
|
384
|
+
|
|
385
|
+
document.getElementById('startBtn').disabled = false;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Event listeners
|
|
389
|
+
document.getElementById('startBtn').addEventListener('click', startGame);
|
|
390
|
+
document.getElementById('resetBtn').addEventListener('click', resetGame);
|
|
391
|
+
|
|
392
|
+
// Initialize
|
|
393
|
+
updateHighScore();
|
|
394
|
+
drawStartScreen();
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
* {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 0;
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
9
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
align-items: center;
|
|
14
|
+
padding: 20px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.container {
|
|
18
|
+
background: white;
|
|
19
|
+
border-radius: 20px;
|
|
20
|
+
padding: 30px;
|
|
21
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
22
|
+
max-width: 500px;
|
|
23
|
+
width: 100%;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.header {
|
|
27
|
+
text-align: center;
|
|
28
|
+
margin-bottom: 20px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.header h1 {
|
|
32
|
+
color: #2d3748;
|
|
33
|
+
font-size: 2.5rem;
|
|
34
|
+
margin-bottom: 5px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.subtitle {
|
|
38
|
+
color: #718096;
|
|
39
|
+
font-size: 1.1rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.game-info {
|
|
43
|
+
display: flex;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
gap: 20px;
|
|
46
|
+
margin-bottom: 20px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.info-item {
|
|
50
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
51
|
+
padding: 12px 30px;
|
|
52
|
+
border-radius: 10px;
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
align-items: center;
|
|
56
|
+
min-width: 130px;
|
|
57
|
+
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.label {
|
|
61
|
+
color: rgba(255, 255, 255, 0.9);
|
|
62
|
+
font-size: 0.85rem;
|
|
63
|
+
font-weight: 600;
|
|
64
|
+
margin-bottom: 5px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.value {
|
|
68
|
+
color: white;
|
|
69
|
+
font-size: 1.8rem;
|
|
70
|
+
font-weight: bold;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#gameCanvas {
|
|
74
|
+
display: block;
|
|
75
|
+
width: 100%;
|
|
76
|
+
max-width: 400px;
|
|
77
|
+
height: auto;
|
|
78
|
+
aspect-ratio: 2/3;
|
|
79
|
+
margin: 0 auto 20px;
|
|
80
|
+
border-radius: 10px;
|
|
81
|
+
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
touch-action: none;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.controls {
|
|
87
|
+
display: flex;
|
|
88
|
+
gap: 15px;
|
|
89
|
+
justify-content: center;
|
|
90
|
+
margin-bottom: 25px;
|
|
91
|
+
flex-wrap: wrap;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.btn {
|
|
95
|
+
padding: 12px 30px;
|
|
96
|
+
font-size: 1rem;
|
|
97
|
+
font-weight: 600;
|
|
98
|
+
border: none;
|
|
99
|
+
border-radius: 8px;
|
|
100
|
+
cursor: pointer;
|
|
101
|
+
transition: all 0.3s ease;
|
|
102
|
+
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
|
103
|
+
min-width: 140px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.btn:hover:not(:disabled) {
|
|
107
|
+
transform: translateY(-2px);
|
|
108
|
+
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.btn:active:not(:disabled) {
|
|
112
|
+
transform: translateY(0);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.btn:disabled {
|
|
116
|
+
opacity: 0.5;
|
|
117
|
+
cursor: not-allowed;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.btn-primary {
|
|
121
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
122
|
+
color: white;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.btn-danger {
|
|
126
|
+
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
|
127
|
+
color: white;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.instructions {
|
|
131
|
+
background: #f7fafc;
|
|
132
|
+
padding: 20px;
|
|
133
|
+
border-radius: 10px;
|
|
134
|
+
border: 2px solid #e2e8f0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.instructions h3 {
|
|
138
|
+
color: #2d3748;
|
|
139
|
+
margin-bottom: 12px;
|
|
140
|
+
font-size: 1.1rem;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.instructions ul {
|
|
144
|
+
list-style: none;
|
|
145
|
+
padding-right: 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.instructions li {
|
|
149
|
+
color: #4a5568;
|
|
150
|
+
margin-bottom: 8px;
|
|
151
|
+
padding-right: 20px;
|
|
152
|
+
position: relative;
|
|
153
|
+
line-height: 1.6;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.instructions li:before {
|
|
157
|
+
content: "•";
|
|
158
|
+
color: #667eea;
|
|
159
|
+
font-weight: bold;
|
|
160
|
+
position: absolute;
|
|
161
|
+
right: 0;
|
|
162
|
+
font-size: 1.2rem;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* Responsive */
|
|
166
|
+
@media (max-width: 768px) {
|
|
167
|
+
.container {
|
|
168
|
+
padding: 20px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.header h1 {
|
|
172
|
+
font-size: 2rem;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.game-info {
|
|
176
|
+
gap: 15px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.info-item {
|
|
180
|
+
min-width: 110px;
|
|
181
|
+
padding: 10px 20px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.value {
|
|
185
|
+
font-size: 1.5rem;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.controls {
|
|
189
|
+
gap: 10px;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.btn {
|
|
193
|
+
padding: 10px 20px;
|
|
194
|
+
min-width: 120px;
|
|
195
|
+
font-size: 0.9rem;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.instructions {
|
|
199
|
+
padding: 15px;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.instructions h3 {
|
|
203
|
+
font-size: 1rem;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.instructions li {
|
|
207
|
+
font-size: 0.9rem;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@media (max-width: 480px) {
|
|
212
|
+
.header h1 {
|
|
213
|
+
font-size: 1.5rem;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.subtitle {
|
|
217
|
+
font-size: 0.95rem;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.game-info {
|
|
221
|
+
flex-direction: column;
|
|
222
|
+
align-items: stretch;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.info-item {
|
|
226
|
+
flex-direction: row;
|
|
227
|
+
justify-content: space-between;
|
|
228
|
+
min-width: unset;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.value {
|
|
232
|
+
font-size: 1.3rem;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.controls {
|
|
236
|
+
flex-direction: column;
|
|
237
|
+
align-items: stretch;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.btn {
|
|
241
|
+
min-width: unset;
|
|
242
|
+
}
|
|
243
|
+
}
|