ProPerNounpYK commited on
Commit
4c4af5a
·
verified ·
1 Parent(s): f55aec5

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +350 -497
index.html CHANGED
@@ -1,305 +1,292 @@
1
  <!DOCTYPE html>
2
- <html>
3
  <head>
4
- <style>
5
- body {
6
- margin: 0;
7
- overflow: hidden;
8
- background: #1a1a1a;
9
- display: flex;
10
- justify-content: center;
11
- align-items: center;
12
- min-height: 100vh;
13
- }
14
-
15
- #introCanvas {
16
- cursor: pointer;
17
- }
18
-
19
- #gameCanvas {
20
- display: none;
21
- background-image: url('stage 1.jpg');
22
- background-size: cover;
23
- }
24
-
25
- #message {
26
- position: fixed;
27
- bottom: 550px;
28
- left: 0;
29
- width: 100%;
30
- color: white;
31
- text-align: center;
32
- font-size: 30px;
33
- font-family: Arial;
34
- display: none;
35
- }
36
-
37
- #stats {
38
- position: fixed;
39
- top: 20px;
40
- left: 20px;
41
- color: white;
42
- font-family: Arial;
43
- font-size: 28px;
44
- display: none;
45
- }
46
-
47
- #villainCount {
48
- position: fixed;
49
- top: 20px;
50
- right: 20px;
51
- color: white;
52
- font-family: Arial;
53
- font-size: 28px;
54
- display: none;
55
- }
56
-
57
- #healthBar {
58
- width: 300px;
59
- height: 30px;
60
- background: #333;
61
- margin-top: 5px;
62
- }
63
-
64
- #healthFill {
65
- width: 100%;
66
- height: 100%;
67
- background: red;
68
- transition: width 0.3s;
69
- }
70
-
71
- #bulletCount, #enemyCount {
72
- font-size: 28px;
73
- }
74
-
75
- #replayButton {
76
- position: fixed;
77
- top: 50%;
78
- left: 50%;
79
- transform: translate(-50%, -50%);
80
- padding: 15px 30px;
81
- font-size: 20px;
82
- font-family: Arial;
83
- background-color: white;
84
- color: black;
85
- border: none;
86
- border-radius: 5px;
87
- display: none;
88
- cursor: pointer;
89
- }
90
-
91
- #replayButton:hover {
92
- background-color: lightgray;
93
- }
94
- </style>
 
 
 
95
  </head>
96
  <body>
97
- <canvas id="introCanvas"></canvas>
98
- <canvas id="gameCanvas"></canvas>
99
- <div id="message"></div>
100
- <div id="stats">
101
- Health:<div id="healthBar"><div id="healthFill"></div></div>
 
102
  Bullets: <span id="bulletCount">35</span>
103
- </div>
104
- <div id="villainCount">Villains: <span id="enemyCount">30</span></div>
105
- <button id="replayButton">Replay</button>
106
-
107
- <audio id="bgMusic" src="cinematic-time-lapse-115672.mp3" loop></audio>
108
- <audio id="playerShootSound" src="Maincharactergunsound.mp3"></audio>
109
- <audio id="enemyShootSound" src="Renovatorsgunshot.mp3"></audio>
110
- <audio id="playerHitSound" src="Maincharactershot.mp3"></audio>
111
- <audio id="enemyHitSound" src="Renovatorsgetshot.mp3"></audio>
112
- <script>
113
-
114
- const introCanvas = document.getElementById('introCanvas');
115
- const ctx = introCanvas.getContext('2d');
116
-
117
- let particles = [];
118
- const phrases = [
119
- { text: 'Hello everyone.', size: 65, duration: 5000 },
120
- { text: 'You have been selected as the next\nTimewalker for our team of Space Guardians.', size: 50, duration: 7000 },
121
- { text: 'Repair the collapsing timelines\nand return safely.', size: 55, duration: 6000 },
122
- { text: 'Good luck.\n- From the Space Guardians Leader', sizes: [65, 45], spacing: 45, duration: 6000 },
123
- { text: 'TimeWalker', size: 65, duration: 3000 },
124
- { text: 'Chapter 1', size: 65, duration: null }
125
- ];
126
-
127
- let currentPhraseIndex = 0;
128
- let animationFrame;
129
- let mouseX = 0;
130
- let mouseY = 0;
131
-
132
- introCanvas.width = window.innerWidth;
133
- introCanvas.height = window.innerHeight;
134
-
135
- class Particle {
136
- constructor(x, y, targetX, targetY) {
137
- this.x = Math.random() * introCanvas.width;
138
- this.y = Math.random() * introCanvas.height;
139
- this.targetX = targetX;
140
- this.targetY = targetY;
141
- this.dx = (Math.random() - 0.5) * 8;
142
- this.dy = (Math.random() - 0.5) * 8;
143
- this.radius = 2;
144
- this.alpha = 1;
145
- this.fadeSpeed = 0.02;
146
- }
147
-
148
- draw() {
149
- ctx.beginPath();
150
- ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
151
- ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`;
152
- ctx.fill();
153
- ctx.closePath();
154
- }
155
-
156
- update() {
157
- const distance = Math.hypot(mouseX - this.x, mouseY - this.y);
158
-
159
- if(distance < 100) {
160
- this.x += this.dx;
161
- this.y += this.dy;
162
- } else {
163
- this.x += (this.targetX - this.x) * 0.1;
164
- this.y += (this.targetY - this.y) * 0.1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  }
166
- }
167
- }
168
-
169
- function createParticles(phrase) {
170
- let newParticles = [];
171
- ctx.clearRect(0, 0, introCanvas.width, introCanvas.height);
172
-
173
- const lines = phrase.text.split('\n');
174
-
175
- lines.forEach((line, index) => {
176
- const size = phrase.sizes ? phrase.sizes[index] : phrase.size;
177
- ctx.font = `${size}px Arial`;
178
- ctx.fillStyle = 'white';
179
- ctx.textAlign = 'center';
180
- ctx.textBaseline = 'middle';
181
-
182
- const spacing = phrase.spacing || size * 1.2;
183
- const y = (introCanvas.height/2 - 20) + (index - (lines.length-1)/2) * spacing;
184
- ctx.fillText(line, introCanvas.width/2, y);
185
- });
186
-
187
- const imageData = ctx.getImageData(0, 0, introCanvas.width, introCanvas.height).data;
188
-
189
- for(let y = 0; y < introCanvas.height; y += 4) {
190
- for(let x = 0; x < introCanvas.width; x += 4) {
191
- const index = (y * introCanvas.width + x) * 4;
192
- const alpha = imageData[index + 3];
193
-
194
- if(alpha > 128) {
195
- newParticles.push(new Particle(x, y, x, y));
 
 
 
 
 
 
 
 
 
 
196
  }
197
  }
198
- }
199
-
200
- ctx.clearRect(0, 0, introCanvas.width, introCanvas.height);
201
- return newParticles;
202
- }
203
- function transition() {
204
- if(currentPhraseIndex < phrases.length - 1) {
205
- currentPhraseIndex++;
206
- const currentPhrase = phrases[currentPhraseIndex];
207
-
208
- particles.forEach(particle => {
209
- particle.dx = (Math.random() - 0.5) * 20;
210
- particle.dy = (Math.random() - 0.5) * 20;
211
- const fadeOut = setInterval(() => {
212
- particle.alpha -= particle.fadeSpeed;
213
- if(particle.alpha <= 0) clearInterval(fadeOut);
214
- }, 50);
215
- });
216
 
217
- setTimeout(() => {
218
- particles = createParticles(currentPhrase);
219
  particles.forEach(particle => {
220
- particle.alpha = 0;
221
- const fadeIn = setInterval(() => {
222
- particle.alpha += particle.fadeSpeed;
223
- if(particle.alpha >= 1) clearInterval(fadeIn);
224
- }, 50);
225
  });
226
- }, 800);
227
-
228
- if(currentPhrase.duration) {
229
- setTimeout(transition, currentPhrase.duration);
230
  }
231
- } else {
232
- // 마지막 문구가 표시된 후 1초 후에 게임 시작
233
- setTimeout(() => {
234
- cancelAnimationFrame(animationFrame); // 인트로 애니메이션 중지
235
- introCanvas.style.display = 'none';
236
- document.getElementById('gameCanvas').style.display = 'block';
237
- document.getElementById('stats').style.display = 'block';
238
- document.getElementById('villainCount').style.display = 'block';
239
- document.getElementById('message').style.display = 'block';
240
- startGame(); // 게임 시작
241
- }, 1000);
242
- }
243
- }
244
-
245
- function animate() {
246
- ctx.clearRect(0, 0, introCanvas.width, introCanvas.height);
247
- particles.forEach(particle => {
248
- particle.update();
249
- particle.draw();
250
- });
251
- animationFrame = requestAnimationFrame(animate);
252
- }
253
-
254
- introCanvas.addEventListener('mousemove', (e) => {
255
- mouseX = e.clientX;
256
- mouseY = e.clientY;
257
- });
258
-
259
- window.addEventListener('resize', () => {
260
- introCanvas.width = window.innerWidth;
261
- introCanvas.height = window.innerHeight;
262
- particles = createParticles(phrases[currentPhraseIndex]);
263
- });
264
-
265
- // 인트로 애니메이션 시작
266
- particles = createParticles(phrases[0]);
267
- particles.forEach(particle => {
268
- particle.alpha = 0;
269
- const fadeIn = setInterval(() => {
270
- particle.alpha += particle.fadeSpeed;
271
- if(particle.alpha >= 1) clearInterval(fadeIn);
272
- }, 50);
273
- });
274
-
275
- animate();
276
- setTimeout(transition, phrases[0].duration);
277
- playAudioForDuration('reflected-light-147979.mp3', 31000);
278
-
279
- function startGame() {
280
- // 여기서부터 게임 코드 시작
281
- const gameCanvas = document.getElementById('gameCanvas');
282
- const gameCtx = gameCanvas.getContext('2d');
283
-
284
- // ... [이전에 보여드린 게임 코드 전체]
285
- }
286
- function startGame() {
287
- const gameCanvas = document.getElementById('gameCanvas');
288
- const gameCtx = gameCanvas.getContext('2d');
289
-
290
- // 게임 상태 변수
291
- let enemies = [];
292
- let bullets = [];
293
- let enemyBullets = [];
294
- let enemiesRemaining = 30;
295
- let mouseX = 0;
296
- let gameStarted = false;
297
- let gameOver = false;
298
 
299
- // 플레이어 객체 초기화
300
- const player = {
301
- x: gameCanvas.width / 2.3,
302
- y: gameCanvas.height - 260,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  width: 250,
304
  height: 250,
305
  speed: 10,
@@ -313,236 +300,102 @@ function startGame() {
313
  imageShootRight: 'gunshotright.png',
314
  imageHitLeft: 'ExplosionLeft.png',
315
  imageHitRight: 'ExplosionRight.png'
316
- };
317
- player.image.src = player.imageRight;
318
 
319
- // 게임 시작 메시지 시퀀스
320
- setTimeout(() => {
321
- message.textContent = "Click on the character";
322
- setTimeout(() => {
323
- message.textContent = "You can move your character using the arrow keys.";
324
- setTimeout(() => {
325
- message.textContent = "The character's direction follows the mouse cursor.";
326
- setTimeout(() => {
327
- message.textContent = "Press the space bar to shoot a bullet.";
328
- setTimeout(() => {
329
- message.textContent = "Your mission is to defeat the Renovators who have attacked New York. Defeat them all and save New York in 2030!";
330
- setTimeout(() => {
331
- message.textContent = "";
332
- gameStarted = true;
333
- }, 5000);
334
- }, 3000);
335
- }, 4000);
336
- }, 4000);
337
- }, 4000);
338
- }, 0);
339
- // 적 생성 함수
340
- function spawnEnemy() {
341
- if (!gameStarted || gameOver) {
342
- return;
343
- }
344
 
345
- if (enemies.length >= 10) {
346
- return;
347
- }
 
 
 
 
 
 
348
 
349
  const side = Math.random() < 0.5 ? 0 : gameCanvas.width;
350
  const enemy = {
351
- x: side,
352
- y: Math.random() * (gameCanvas.height - 150),
353
- width: 250,
354
- height: 250,
355
- speed: side === 0 ? 1 : -1,
356
- image: new Image(),
357
- imageLeft: 'Renovators Default Right.png',
358
- imageRight: 'RenovatorsDefaultLeft.png',
359
- imageShootLeft: 'RenovatorsgunLeft.png',
360
- imageShootRight: 'RenovatorsGunRight.png',
361
- imageHitLeft: 'RenovatorsExplodeLeft.png',
362
- imageHitRight: 'RenovatorsExplodeRight.png',
363
- lastShootTime: Date.now()
364
  };
365
  enemy.image.src = side === 0 ? enemy.imageLeft : enemy.imageRight;
366
  enemies.push(enemy);
367
- }
368
 
369
- // 총알 발사 함수
370
- function enemyShoot(enemy) {
371
  const now = Date.now();
372
- if (now - enemy.lastShootTime >= 3000 && !gameOver) {
373
- enemy.image.src = enemy.speed > 0 ? enemy.imageShootRight : enemy.imageShootLeft;
374
- enemyBullets.push({
375
- x: enemy.x + enemy.width / 2,
376
- y: enemy.y + enemy.height / 2.5,
377
- width: 10,
378
- height: 5,
379
- speed: enemy.speed > 0 ? 5 : -5
380
- });
381
- enemy.lastShootTime = now;
382
- enemyShootSound.play();
383
  }
384
- }
385
-
386
- // 충돌 감지 함수
387
- function checkCollision(rect1, rect2) {
388
- return (
389
- rect1.x + rect1.width * 0.2 < rect2.x + rect2.width * 0.8 &&
390
- rect1.x + rect1.width * 0.8 > rect2.x + rect2.width * 0.2 &&
391
- rect1.y + rect1.height * 0.2 < rect2.y + rect2.height * 0.8 &&
392
- rect1.y + rect1.height * 0.8 > rect2.y + rect2.height * 0.2
393
- );
394
- }
395
- // 메인 게임 업데이트 루프
396
- function updateGame() {
397
- gameCtx.clearRect(0, 0, gameCanvas.width, gameCanvas.height);
398
-
399
- // 플레이어 업데이트
400
- player.direction = mouseX > player.x ? 1 : -1;
401
- player.image.src = player.direction === 1 ? player.imageRight : player.imageLeft;
402
- gameCtx.drawImage(player.image, player.x, player.y, player.width, player.height);
403
 
404
- // 총알 업데이트
 
405
  bullets.forEach((bullet, index) => {
406
- bullet.x += bullet.speed;
407
- gameCtx.fillStyle = 'yellow';
408
- gameCtx.fillRect(bullet.x, bullet.y, 10, 5);
409
  });
 
410
 
411
- // 적 업데이트
 
412
  enemies.forEach((enemy, enemyIndex) => {
413
- enemy.x += enemy.speed;
414
- gameCtx.drawImage(enemy.image, enemy.x, enemy.y, enemy.width, enemy.height);
415
-
416
- // 충돌 체크 및 처리
417
- if (checkCollision(player, enemy)) {
418
- handlePlayerEnemyCollision(enemy, enemyIndex);
419
- }
420
-
421
- // 총알 충돌 체크
422
- bullets.forEach((bullet, bulletIndex) => {
423
- if (checkCollision(bullet, enemy)) {
424
- handleBulletEnemyCollision(bullet, bulletIndex, enemy, enemyIndex);
425
- }
426
- });
427
-
428
- enemyShoot(enemy);
429
  });
 
430
 
431
- // 총알 업데이트
 
 
 
 
 
 
 
 
432
  enemyBullets.forEach((bullet, index) => {
433
- bullet.x += bullet.speed;
434
- gameCtx.fillStyle = 'orange';
435
- gameCtx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
436
-
437
- if (checkBulletPlayerCollision(bullet)) {
438
- handleBulletPlayerCollision(bullet, index);
439
- }
440
  });
441
 
442
- // 화면 밖 총알 제거
443
- cleanupBullets();
444
-
445
- // 게임 상태 체크
446
- if (!gameOver) {
447
- requestAnimationFrame(updateGame);
448
- }
449
- }
450
-
451
- // 이벤트 리스너
452
- window.addEventListener('keydown', handleKeyPress);
453
- window.addEventListener('mousemove', (e) => {
454
- mouseX = e.clientX;
455
- });
456
-
457
- replayButton.addEventListener('click', () => {
458
- location.reload();
459
- });
460
-
461
- // 게임 시작
462
- setInterval(spawnEnemy, 2000);
463
- bgMusic.play();
464
- updateGame();
465
- }
466
- // 충돌 처리 헬퍼 함수들
467
- function handlePlayerEnemyCollision(enemy, enemyIndex) {
468
- player.health--;
469
- healthFill.style.width = (player.health / 5 * 100) + '%';
470
- player.image.src = player.direction === 1 ? player.imageHitRight : player.imageHitLeft;
471
- enemies.splice(enemyIndex, 1);
472
- playerHitSound.play();
473
-
474
- if (player.health <= 0) {
475
- endGame("Game Over! Go back to the past and change the current result!");
476
- }
477
- }
478
-
479
- function handleBulletEnemyCollision(bullet, bulletIndex, enemy, enemyIndex) {
480
- enemy.image.src = enemy.speed > 0 ? enemy.imageHitRight : enemy.imageHitLeft;
481
- setTimeout(() => {
482
- enemies.splice(enemyIndex, 1);
483
- bullets.splice(bulletIndex, 1);
484
- enemiesRemaining--;
485
- enemyCount.textContent = enemiesRemaining;
486
-
487
- if (enemiesRemaining <= 0) {
488
- endGame("You have successfully protected New York. Return to the time machine.");
489
- }
490
- }, 100);
491
- enemyHitSound.play();
492
- }
493
-
494
- function handleKeyPress(e) {
495
  if (!gameOver) {
496
- switch (e.key) {
497
- case 'ArrowUp':
498
- if (player.y > 0) player.y -= player.speed;
499
- break;
500
- case 'ArrowDown':
501
- if (player.y < gameCanvas.height - player.height) player.y += player.speed;
502
- break;
503
- case 'ArrowLeft':
504
- if (player.x > 0) player.x -= player.speed;
505
- break;
506
- case 'ArrowRight':
507
- if (player.x < gameCanvas.width - player.width) player.x += player.speed;
508
- break;
509
- case ' ':
510
- handlePlayerShoot();
511
- break;
512
- }
513
- }
514
- }
515
-
516
- function handlePlayerShoot() {
517
- if (player.bullets > 0) {
518
- player.image.src = player.direction === 1 ? player.imageShootRight : player.imageShootLeft;
519
- bullets.push({
520
- x: player.x + player.width / 2,
521
- y: player.y + player.height / 2.5,
522
- width: 10,
523
- height: 5,
524
- speed: player.direction * 10
525
- });
526
- player.bullets--;
527
- bulletCount.textContent = player.bullets;
528
- playerShootSound.play();
529
  }
530
- }
531
 
532
- function endGame(messageText) {
533
- gameOver = true;
534
- message.textContent = messageText;
535
- replayButton.style.display = 'block';
536
  }
537
-
538
- // 인트로 애니메이션이 완료된 후 게임 시작
539
- function startGameAfterIntro() {
540
- document.getElementById('introCanvas').style.display = 'none';
541
- document.getElementById('gameCanvas').style.display = 'block';
542
- document.getElementById('message').style.display = 'block';
543
- document.getElementById('stats').style.display = 'block';
544
- document.getElementById('villainCount').style.display = 'block';
545
- bgMusic.play();
546
- updateGame();
547
- setInterval(spawnEnemy, 2000);
548
- }
 
1
  <!DOCTYPE html>
2
+ <html lang="ko">
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Space Guardians Game</title>
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ overflow: hidden;
11
+ background: #1a1a1a;
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
+ min-height: 100vh;
16
+ }
17
+
18
+ #introCanvas {
19
+ cursor: pointer;
20
+ }
21
+
22
+ #gameCanvas {
23
+ display: none;
24
+ background-image: url('stage 1.jpg');
25
+ background-size: cover;
26
+ }
27
+
28
+ #message {
29
+ position: fixed;
30
+ bottom: 550px;
31
+ left: 0;
32
+ width: 100%;
33
+ color: white;
34
+ text-align: center;
35
+ font-size: 30px;
36
+ font-family: Arial;
37
+ display: none;
38
+ }
39
+
40
+ #stats {
41
+ position: fixed;
42
+ top: 20px;
43
+ left: 20px;
44
+ color: white;
45
+ font-family: Arial;
46
+ font-size: 28px;
47
+ display: none;
48
+ }
49
+
50
+ #villainCount {
51
+ position: fixed;
52
+ top: 20px;
53
+ right: 20px;
54
+ color: white;
55
+ font-family: Arial;
56
+ font-size: 28px;
57
+ display: none;
58
+ }
59
+
60
+ #healthBar {
61
+ width: 300px;
62
+ height: 30px;
63
+ background: #333;
64
+ margin-top: 5px;
65
+ }
66
+
67
+ #healthFill {
68
+ width: 100%;
69
+ height: 100%;
70
+ background: red;
71
+ transition: width 0.3s;
72
+ }
73
+
74
+ #bulletCount, #enemyCount {
75
+ font-size: 28px;
76
+ }
77
+
78
+ #replayButton {
79
+ position: fixed;
80
+ top: 50%;
81
+ left: 50%;
82
+ transform: translate(-50%, -50%);
83
+ padding: 15px 30px;
84
+ font-size: 20px;
85
+ font-family: Arial;
86
+ background-color: white;
87
+ color: black;
88
+ border: none;
89
+ border-radius: 5px;
90
+ display: none;
91
+ cursor: pointer;
92
+ }
93
+
94
+ #replayButton:hover {
95
+ background-color: lightgray;
96
+ }
97
+ </style>
98
  </head>
99
  <body>
100
+ <canvas id="introCanvas"></canvas>
101
+ <canvas id="gameCanvas"></canvas>
102
+ <div id="message"></div>
103
+ <div id="stats">
104
+ Health:
105
+ <div id="healthBar"><div id="healthFill"></div></div>
106
  Bullets: <span id="bulletCount">35</span>
107
+ </div>
108
+ <div id="villainCount">Villains: <span id="enemyCount">30</span></div>
109
+ <button id="replayButton">Replay</button>
110
+
111
+ <audio id="bgMusic" src="cinematic-time-lapse-115672.mp3" loop></audio>
112
+ <audio id="playerShootSound" src="Maincharactergunsound.mp3"></audio>
113
+ <audio id="enemyShootSound" src="Renovatorsgunshot.mp3"></audio>
114
+ <audio id="playerHitSound" src="Maincharactershot.mp3"></audio>
115
+ <audio id="enemyHitSound" src="Renovatorsgetshot.mp3"></audio>
116
+
117
+ <script>
118
+ const introCanvas = document.getElementById('introCanvas');
119
+ const ctx = introCanvas.getContext('2d');
120
+ const message = document.getElementById('message');
121
+ const healthFill = document.getElementById('healthFill');
122
+ const bulletCount = document.getElementById('bulletCount');
123
+ const enemyCount = document.getElementById('enemyCount');
124
+ const replayButton = document.getElementById('replayButton');
125
+ const gameCanvas = document.getElementById('gameCanvas');
126
+ const gameCtx = gameCanvas.getContext('2d');
127
+
128
+ let particles = [];
129
+ let currentPhraseIndex = 0;
130
+ let animationFrame;
131
+ let mouseX = 0;
132
+ let mouseY = 0;
133
+ let gameStarted = false;
134
+ let gameOver = false;
135
+
136
+ introCanvas.width = window.innerWidth;
137
+ introCanvas.height = window.innerHeight;
138
+ gameCanvas.width = window.innerWidth;
139
+ gameCanvas.height = window.innerHeight;
140
+
141
+ // Particle class to generate text effect during intro
142
+ class Particle {
143
+ constructor(x, y, targetX, targetY) {
144
+ this.x = Math.random() * introCanvas.width;
145
+ this.y = Math.random() * introCanvas.height;
146
+ this.targetX = targetX;
147
+ this.targetY = targetY;
148
+ this.dx = (Math.random() - 0.5) * 8;
149
+ this.dy = (Math.random() - 0.5) * 8;
150
+ this.radius = 2;
151
+ this.alpha = 1;
152
+ this.fadeSpeed = 0.02;
153
+ }
154
+
155
+ draw() {
156
+ ctx.beginPath();
157
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
158
+ ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`;
159
+ ctx.fill();
160
+ ctx.closePath();
161
+ }
162
+
163
+ update() {
164
+ const distance = Math.hypot(mouseX - this.x, mouseY - this.y);
165
+
166
+ if (distance < 100) {
167
+ this.x += this.dx;
168
+ this.y += this.dy;
169
+ } else {
170
+ this.x += (this.targetX - this.x) * 0.1;
171
+ this.y += (this.targetY - this.y) * 0.1;
172
+ }
173
+ }
174
+ }
175
+
176
+ function createParticles(phrase) {
177
+ let newParticles = [];
178
+ ctx.clearRect(0, 0, introCanvas.width, introCanvas.height);
179
+
180
+ const lines = phrase.text.split('\n');
181
+
182
+ lines.forEach((line, index) => {
183
+ const size = phrase.sizes ? phrase.sizes[index] : phrase.size;
184
+ ctx.font = `${size}px Arial`;
185
+ ctx.fillStyle = 'white';
186
+ ctx.textAlign = 'center';
187
+ ctx.textBaseline = 'middle';
188
+
189
+ const spacing = phrase.spacing || size * 1.2;
190
+ const y = (introCanvas.height / 2 - 20) + (index - (lines.length - 1) / 2) * spacing;
191
+ ctx.fillText(line, introCanvas.width / 2, y);
192
+ });
193
+
194
+ const imageData = ctx.getImageData(0, 0, introCanvas.width, introCanvas.height).data;
195
+
196
+ for (let y = 0; y < introCanvas.height; y += 4) {
197
+ for (let x = 0; x < introCanvas.width; x += 4) {
198
+ const index = (y * introCanvas.width + x) * 4;
199
+ const alpha = imageData[index + 3];
200
+
201
+ if (alpha > 128) {
202
+ newParticles.push(new Particle(x, y, x, y));
203
+ }
204
+ }
205
+ }
206
+
207
+ ctx.clearRect(0, 0, introCanvas.width, introCanvas.height);
208
+ return newParticles;
209
  }
210
+
211
+ function transition() {
212
+ if (currentPhraseIndex < phrases.length - 1) {
213
+ currentPhraseIndex++;
214
+ const currentPhrase = phrases[currentPhraseIndex];
215
+
216
+ particles.forEach(particle => {
217
+ particle.dx = (Math.random() - 0.5) * 20;
218
+ particle.dy = (Math.random() - 0.5) * 20;
219
+ const fadeOut = setInterval(() => {
220
+ particle.alpha -= particle.fadeSpeed;
221
+ if (particle.alpha <= 0) clearInterval(fadeOut);
222
+ }, 50);
223
+ });
224
+
225
+ setTimeout(() => {
226
+ particles = createParticles(currentPhrase);
227
+ particles.forEach(particle => {
228
+ particle.alpha = 0;
229
+ const fadeIn = setInterval(() => {
230
+ particle.alpha += particle.fadeSpeed;
231
+ if (particle.alpha >= 1) clearInterval(fadeIn);
232
+ }, 50);
233
+ });
234
+ }, 800);
235
+
236
+ if (currentPhrase.duration) {
237
+ setTimeout(transition, currentPhrase.duration);
238
+ }
239
+ } else {
240
+ // 마지막 문구가 표시된 후 1초 후에 게임 시작
241
+ setTimeout(() => {
242
+ cancelAnimationFrame(animationFrame); // 인트로 애니메이션 중지
243
+ introCanvas.style.display = 'none';
244
+ gameCanvas.style.display = 'block';
245
+ document.getElementById('stats').style.display = 'block';
246
+ document.getElementById('villainCount').style.display = 'block';
247
+ document.getElementById('message').style.display = 'block';
248
+ startGame(); // 게임 시작
249
+ }, 1000);
250
  }
251
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
+ function animate() {
254
+ ctx.clearRect(0, 0, introCanvas.width, introCanvas.height);
255
  particles.forEach(particle => {
256
+ particle.update();
257
+ particle.draw();
 
 
 
258
  });
259
+ animationFrame = requestAnimationFrame(animate);
 
 
 
260
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
+ window.addEventListener('mousemove', (e) => {
263
+ mouseX = e.clientX;
264
+ mouseY = e.clientY;
265
+ });
266
+
267
+ window.addEventListener('click', () => {
268
+ if (!gameStarted) {
269
+ introCanvas.style.display = 'none';
270
+ gameCanvas.style.display = 'block';
271
+ startGame();
272
+ }
273
+ });
274
+
275
+ const phrases = [
276
+ { text: 'Space Guardians Game\nComing soon!', size: 80, duration: 2000 },
277
+ { text: 'The fate of New York is in your hands.', size: 60, duration: 2000 },
278
+ { text: 'Defeat the Renovators and save the city in 2030!', size: 60, duration: 3000 }
279
+ ];
280
+
281
+ let particles = createParticles(phrases[0]);
282
+
283
+ animate(); // 시작 시 인트로 애니메이션
284
+
285
+ // 게임 시작 함수
286
+ function startGame() {
287
+ const player = {
288
+ x: gameCanvas.width / 2,
289
+ y: gameCanvas.height - 250,
290
  width: 250,
291
  height: 250,
292
  speed: 10,
 
300
  imageShootRight: 'gunshotright.png',
301
  imageHitLeft: 'ExplosionLeft.png',
302
  imageHitRight: 'ExplosionRight.png'
303
+ };
 
304
 
305
+ player.image.src = player.imageRight;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
 
307
+ let enemies = [];
308
+ let bullets = [];
309
+ let enemyBullets = [];
310
+ let enemiesRemaining = 30;
311
+
312
+ let gameOver = false;
313
+
314
+ function spawnEnemy() {
315
+ if (enemies.length >= 10) return;
316
 
317
  const side = Math.random() < 0.5 ? 0 : gameCanvas.width;
318
  const enemy = {
319
+ x: side,
320
+ y: Math.random() * (gameCanvas.height - 150),
321
+ width: 250,
322
+ height: 250,
323
+ speed: side === 0 ? 1 : -1,
324
+ image: new Image(),
325
+ imageLeft: 'Renovators Default Right.png',
326
+ imageRight: 'RenovatorsDefaultLeft.png',
327
+ imageShootLeft: 'RenovatorsgunLeft.png',
328
+ imageShootRight: 'RenovatorsGunRight.png',
329
+ imageHitLeft: 'RenovatorsExplodeLeft.png',
330
+ imageHitRight: 'RenovatorsExplodeRight.png',
331
+ lastShootTime: Date.now()
332
  };
333
  enemy.image.src = side === 0 ? enemy.imageLeft : enemy.imageRight;
334
  enemies.push(enemy);
335
+ }
336
 
337
+ function enemyShoot(enemy) {
 
338
  const now = Date.now();
339
+ if (now - enemy.lastShootTime >= 3000) {
340
+ enemy.image.src = enemy.speed > 0 ? enemy.imageShootRight : enemy.imageShootLeft;
341
+ enemyBullets.push({
342
+ x: enemy.x + enemy.width / 2,
343
+ y: enemy.y + enemy.height / 2.5,
344
+ width: 10,
345
+ height: 5,
346
+ speed: enemy.speed > 0 ? 5 : -5
347
+ });
348
+ enemy.lastShootTime = now;
349
+ enemyShootSound.play();
350
  }
351
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
 
353
+ // 총알 업데이트
354
+ function updateBullets() {
355
  bullets.forEach((bullet, index) => {
356
+ bullet.x += bullet.speed;
357
+ gameCtx.fillStyle = 'yellow';
358
+ gameCtx.fillRect(bullet.x, bullet.y, 10, 5);
359
  });
360
+ }
361
 
362
+ // 적 업데이트
363
+ function updateEnemies() {
364
  enemies.forEach((enemy, enemyIndex) => {
365
+ enemy.x += enemy.speed;
366
+ gameCtx.drawImage(enemy.image, enemy.x, enemy.y, enemy.width, enemy.height);
367
+ if (checkCollision(player, enemy)) {
368
+ handlePlayerEnemyCollision(enemy, enemyIndex);
369
+ }
370
+ enemyShoot(enemy);
 
 
 
 
 
 
 
 
 
 
371
  });
372
+ }
373
 
374
+ // 게임 업데이트 루프
375
+ function updateGame() {
376
+ gameCtx.clearRect(0, 0, gameCanvas.width, gameCanvas.height);
377
+ player.direction = mouseX > player.x ? 1 : -1;
378
+ player.image.src = player.direction === 1 ? player.imageRight : player.imageLeft;
379
+ gameCtx.drawImage(player.image, player.x, player.y, player.width, player.height);
380
+
381
+ updateBullets();
382
+ updateEnemies();
383
  enemyBullets.forEach((bullet, index) => {
384
+ bullet.x += bullet.speed;
385
+ gameCtx.fillStyle = 'orange';
386
+ gameCtx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
 
 
 
 
387
  });
388
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  if (!gameOver) {
390
+ requestAnimationFrame(updateGame);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
  }
392
+ }
393
 
394
+ // 생성 주기
395
+ setInterval(spawnEnemy, 2000);
396
+ bgMusic.play();
397
+ updateGame();
398
  }
399
+ </script>
400
+ </body>
401
+ </html>