cutechicken commited on
Commit
7827e8b
β€’
1 Parent(s): 89d8144

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +125 -79
game.js CHANGED
@@ -73,23 +73,20 @@ class TankPlayer {
73
  this.isLoaded = false;
74
  }
75
  }
76
- shoot(scene) {
 
77
  const currentTime = Date.now();
78
  if (currentTime - this.lastShootTime < this.shootInterval || this.ammo <= 0) return null;
79
 
80
- // μ΄μ•Œ 생성
81
  const bulletGeometry = new THREE.SphereGeometry(0.2);
82
  const bulletMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
83
  const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
84
 
85
- // μ΄μ•Œ μ‹œμž‘ μœ„μΉ˜ (포탑 끝)
86
  const bulletOffset = new THREE.Vector3(0, 0.5, 2);
87
- // ν¬νƒ‘μ˜ νšŒμ „μ„ 적용
88
  bulletOffset.applyQuaternion(this.turretGroup.quaternion);
89
  bulletOffset.applyQuaternion(this.body.quaternion);
90
  bullet.position.copy(this.body.position).add(bulletOffset);
91
 
92
- // μ΄μ•Œ 속도 (포탑 λ°©ν–₯)
93
  const direction = new THREE.Vector3(0, 0, 1);
94
  direction.applyQuaternion(this.turretGroup.quaternion);
95
  direction.applyQuaternion(this.body.quaternion);
@@ -108,12 +105,10 @@ class TankPlayer {
108
  update(mouseX, mouseY) {
109
  if (!this.body || !this.turretGroup) return;
110
 
111
- // μ΄μ•Œ μ—…λ°μ΄νŠΈ
112
  for (let i = this.bullets.length - 1; i >= 0; i--) {
113
  const bullet = this.bullets[i];
114
  bullet.position.add(bullet.velocity);
115
 
116
- // μ΄μ•Œμ΄ 맡 λ°–μœΌλ‘œ λ‚˜κ°€λ©΄ 제거
117
  if (Math.abs(bullet.position.x) > MAP_SIZE/2 ||
118
  Math.abs(bullet.position.z) > MAP_SIZE/2) {
119
  scene.remove(bullet);
@@ -146,6 +141,7 @@ class TankPlayer {
146
  return this.health <= 0;
147
  }
148
  }
 
149
  // Enemy 클래슀
150
  class Enemy {
151
  constructor(scene, position, type = 'tank') {
@@ -193,7 +189,6 @@ class Enemy {
193
  this.mesh.lookAt(playerPosition);
194
  this.mesh.position.add(direction.multiplyScalar(this.moveSpeed));
195
 
196
- // μ΄μ•Œ μ—…λ°μ΄νŠΈ
197
  for (let i = this.bullets.length - 1; i >= 0; i--) {
198
  const bullet = this.bullets[i];
199
  bullet.position.add(bullet.velocity);
@@ -241,7 +236,8 @@ class Enemy {
241
  this.health -= damage;
242
  return this.health <= 0;
243
  }
244
- destroy() {
 
245
  if (this.mesh) {
246
  this.scene.remove(this.mesh);
247
  this.bullets.forEach(bullet => this.scene.remove(bullet));
@@ -306,7 +302,8 @@ class Game {
306
  this.isLoading = true;
307
  this.previousTankPosition = new THREE.Vector3();
308
  this.lastTime = performance.now();
309
- this.animationFrameId = null; // μ• λ‹ˆλ©”μ΄μ…˜ ν”„λ ˆμž„ ID μΆ”κ°€
 
310
 
311
  this.mouse = { x: 0, y: 0 };
312
  this.keys = {
@@ -340,7 +337,7 @@ class Game {
340
  metalness: 0.1
341
  })
342
  );
343
- ground.rotation.x = -Math.PI / 2;
344
  ground.receiveShadow = true;
345
  this.scene.add(ground);
346
 
@@ -378,7 +375,7 @@ class Game {
378
 
379
  setupEventListeners() {
380
  document.addEventListener('keydown', (event) => {
381
- if (this.isLoading) return;
382
  switch(event.code) {
383
  case 'KeyW': this.keys.forward = true; break;
384
  case 'KeyS': this.keys.backward = true; break;
@@ -388,7 +385,7 @@ class Game {
388
  });
389
 
390
  document.addEventListener('keyup', (event) => {
391
- if (this.isLoading) return;
392
  switch(event.code) {
393
  case 'KeyW': this.keys.forward = false; break;
394
  case 'KeyS': this.keys.backward = false; break;
@@ -398,7 +395,7 @@ class Game {
398
  });
399
 
400
  document.addEventListener('mousemove', (event) => {
401
- if (this.isLoading || !document.pointerLockElement) return;
402
  this.mouse.x += event.movementX * 0.002;
403
  this.mouse.y += event.movementY * 0.002;
404
  });
@@ -406,10 +403,10 @@ class Game {
406
  document.addEventListener('click', () => {
407
  if (!document.pointerLockElement) {
408
  document.body.requestPointerLock();
409
- } else {
410
  const bullet = this.tank.shoot(this.scene);
411
  if (bullet) {
412
- // μ΄μ•Œ λ°œμ‚¬ 효과 μΆ”κ°€ κ°€λŠ₯
413
  }
414
  }
415
  });
@@ -427,8 +424,9 @@ class Game {
427
  this.renderer.setSize(window.innerWidth, window.innerHeight);
428
  });
429
  }
430
- handleMovement() {
431
- if (!this.tank.isLoaded) return;
 
432
 
433
  const direction = new THREE.Vector3();
434
 
@@ -504,7 +502,7 @@ class Game {
504
  this.scene.add(building);
505
  }
506
  }
507
- return Promise.resolve(); // 비동기 처리λ₯Ό μœ„ν•΄ Promise λ°˜ν™˜
508
  }
509
 
510
  createBuilding(type) {
@@ -515,7 +513,7 @@ class Game {
515
  specular: 0x111111,
516
  shininess: 30
517
  });
518
- const building = new THREE.Mesh(geometry, material);
519
  building.castShadow = true;
520
  building.receiveShadow = true;
521
  return building;
@@ -554,6 +552,27 @@ class Game {
554
  }
555
  }
556
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
557
  spawnEnemies() {
558
  const spawnEnemy = () => {
559
  if (this.enemies.length < ENEMY_COUNT_MAX && !this.isGameOver) {
@@ -606,20 +625,6 @@ class Game {
606
  return null;
607
  }
608
 
609
- startGameTimer() {
610
- const timer = setInterval(() => {
611
- if (this.isLoading || this.isGameOver) return;
612
-
613
- this.gameTime--;
614
- document.getElementById('time').textContent = `Time: ${this.gameTime}s`;
615
-
616
- if (this.gameTime <= 0) {
617
- clearInterval(timer);
618
- this.endGame();
619
- }
620
- }, 1000);
621
- }
622
-
623
  updateParticles() {
624
  for (let i = this.particles.length - 1; i >= 0; i--) {
625
  const particle = this.particles[i];
@@ -629,7 +634,8 @@ class Game {
629
  }
630
  }
631
  }
632
- createExplosion(position) {
 
633
  for (let i = 0; i < PARTICLE_COUNT; i++) {
634
  this.particles.push(new Particle(this.scene, position));
635
  }
@@ -660,6 +666,26 @@ class Game {
660
  });
661
  });
662
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
663
  // ν”Œλ ˆμ΄μ–΄ 탱크와 건물 좩돌 체크
664
  const tankBoundingBox = new THREE.Box3().setFromObject(this.tank.body);
665
  for (const building of this.buildings) {
@@ -671,42 +697,73 @@ class Game {
671
  }
672
 
673
  // 이전 μœ„μΉ˜ μ €μž₯
674
- this.previousTankPosition = this.tank.body.position.clone();
675
  }
676
 
677
  endGame() {
 
 
678
  this.isGameOver = true;
 
 
 
 
 
679
  if (this.animationFrameId) {
680
  cancelAnimationFrame(this.animationFrameId);
681
  }
682
 
 
 
683
  const gameOverDiv = document.createElement('div');
684
  gameOverDiv.style.position = 'absolute';
685
  gameOverDiv.style.top = '50%';
686
  gameOverDiv.style.left = '50%';
687
  gameOverDiv.style.transform = 'translate(-50%, -50%)';
688
- gameOverDiv.style.color = 'white';
689
  gameOverDiv.style.fontSize = '48px';
690
- gameOverDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
691
  gameOverDiv.style.padding = '20px';
692
  gameOverDiv.style.borderRadius = '10px';
 
693
  gameOverDiv.innerHTML = `
694
  Game Over<br>
695
  Score: ${this.score}<br>
696
  Time Survived: ${GAME_DURATION - this.gameTime}s<br>
697
  <button onclick="location.reload()"
698
  style="font-size: 24px; padding: 10px; margin-top: 20px;
699
- cursor: pointer; background: #4CAF50; border: none;
700
- color: white; border-radius: 5px;">
701
  Play Again
702
  </button>
703
  `;
704
  document.body.appendChild(gameOverDiv);
705
  }
706
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
707
  animate() {
708
  if (this.isGameOver) {
709
- cancelAnimationFrame(this.animationFrameId);
 
 
710
  return;
711
  }
712
 
@@ -716,51 +773,40 @@ class Game {
716
  const deltaTime = (currentTime - this.lastTime) / 1000;
717
  this.lastTime = currentTime;
718
 
719
- if (this.isLoading) {
720
- this.renderer.render(this.scene, this.camera);
721
- return;
722
- }
723
- this.handleMovement();
724
- this.tank.update(this.mouse.x, this.mouse.y);
725
-
726
- const tankPosition = this.tank.getPosition();
727
- this.enemies.forEach(enemy => {
728
- enemy.update(tankPosition);
729
 
730
- if (enemy.isLoaded && enemy.mesh.position.distanceTo(tankPosition) < ENEMY_CONFIG.ATTACK_RANGE) {
731
- enemy.shoot(tankPosition);
732
- }
733
- });
734
-
735
- this.updateParticles();
736
- this.checkCollisions();
737
- this.updateUI();
738
- this.renderer.render(this.scene, this.camera);
739
- }
740
-
741
- updateUI() {
742
- const healthBar = document.getElementById('health');
743
- if (healthBar) {
744
- healthBar.style.width = `${(this.tank.health / MAX_HEALTH) * 100}%`;
745
- }
746
-
747
- const timeElement = document.getElementById('time');
748
- if (timeElement) {
749
- timeElement.textContent = `Time: ${this.gameTime}s`;
750
- }
751
 
752
- const scoreElement = document.getElementById('score');
753
- if (scoreElement) {
754
- scoreElement.textContent = `Score: ${this.score}`;
755
  }
 
 
756
  }
757
- }
758
 
759
- // HTML의 startGame ν•¨μˆ˜μ™€ μ—°κ²°
760
  window.startGame = function() {
761
  document.getElementById('startScreen').style.display = 'none';
762
  document.body.requestPointerLock();
 
 
 
 
 
763
  };
764
 
765
- // κ²Œμž„ μΈμŠ€ν„΄μŠ€ 생성
766
- const game = new Game();
 
 
 
73
  this.isLoaded = false;
74
  }
75
  }
76
+
77
+ shoot(scene) {
78
  const currentTime = Date.now();
79
  if (currentTime - this.lastShootTime < this.shootInterval || this.ammo <= 0) return null;
80
 
 
81
  const bulletGeometry = new THREE.SphereGeometry(0.2);
82
  const bulletMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
83
  const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
84
 
 
85
  const bulletOffset = new THREE.Vector3(0, 0.5, 2);
 
86
  bulletOffset.applyQuaternion(this.turretGroup.quaternion);
87
  bulletOffset.applyQuaternion(this.body.quaternion);
88
  bullet.position.copy(this.body.position).add(bulletOffset);
89
 
 
90
  const direction = new THREE.Vector3(0, 0, 1);
91
  direction.applyQuaternion(this.turretGroup.quaternion);
92
  direction.applyQuaternion(this.body.quaternion);
 
105
  update(mouseX, mouseY) {
106
  if (!this.body || !this.turretGroup) return;
107
 
 
108
  for (let i = this.bullets.length - 1; i >= 0; i--) {
109
  const bullet = this.bullets[i];
110
  bullet.position.add(bullet.velocity);
111
 
 
112
  if (Math.abs(bullet.position.x) > MAP_SIZE/2 ||
113
  Math.abs(bullet.position.z) > MAP_SIZE/2) {
114
  scene.remove(bullet);
 
141
  return this.health <= 0;
142
  }
143
  }
144
+
145
  // Enemy 클래슀
146
  class Enemy {
147
  constructor(scene, position, type = 'tank') {
 
189
  this.mesh.lookAt(playerPosition);
190
  this.mesh.position.add(direction.multiplyScalar(this.moveSpeed));
191
 
 
192
  for (let i = this.bullets.length - 1; i >= 0; i--) {
193
  const bullet = this.bullets[i];
194
  bullet.position.add(bullet.velocity);
 
236
  this.health -= damage;
237
  return this.health <= 0;
238
  }
239
+
240
+ destroy() {
241
  if (this.mesh) {
242
  this.scene.remove(this.mesh);
243
  this.bullets.forEach(bullet => this.scene.remove(bullet));
 
302
  this.isLoading = true;
303
  this.previousTankPosition = new THREE.Vector3();
304
  this.lastTime = performance.now();
305
+ this.gameTimer = null;
306
+ this.animationFrameId = null;
307
 
308
  this.mouse = { x: 0, y: 0 };
309
  this.keys = {
 
337
  metalness: 0.1
338
  })
339
  );
340
+ ground.rotation.x = -Math.PI / 2;
341
  ground.receiveShadow = true;
342
  this.scene.add(ground);
343
 
 
375
 
376
  setupEventListeners() {
377
  document.addEventListener('keydown', (event) => {
378
+ if (this.isLoading || this.isGameOver) return;
379
  switch(event.code) {
380
  case 'KeyW': this.keys.forward = true; break;
381
  case 'KeyS': this.keys.backward = true; break;
 
385
  });
386
 
387
  document.addEventListener('keyup', (event) => {
388
+ if (this.isLoading || this.isGameOver) return;
389
  switch(event.code) {
390
  case 'KeyW': this.keys.forward = false; break;
391
  case 'KeyS': this.keys.backward = false; break;
 
395
  });
396
 
397
  document.addEventListener('mousemove', (event) => {
398
+ if (this.isLoading || this.isGameOver || !document.pointerLockElement) return;
399
  this.mouse.x += event.movementX * 0.002;
400
  this.mouse.y += event.movementY * 0.002;
401
  });
 
403
  document.addEventListener('click', () => {
404
  if (!document.pointerLockElement) {
405
  document.body.requestPointerLock();
406
+ } else if (!this.isGameOver) {
407
  const bullet = this.tank.shoot(this.scene);
408
  if (bullet) {
409
+ // Shooting effects could be added here
410
  }
411
  }
412
  });
 
424
  this.renderer.setSize(window.innerWidth, window.innerHeight);
425
  });
426
  }
427
+
428
+ handleMovement() {
429
+ if (!this.tank.isLoaded || this.isGameOver) return;
430
 
431
  const direction = new THREE.Vector3();
432
 
 
502
  this.scene.add(building);
503
  }
504
  }
505
+ return Promise.resolve();
506
  }
507
 
508
  createBuilding(type) {
 
513
  specular: 0x111111,
514
  shininess: 30
515
  });
516
+ const building = new THREE.Mesh(geometry, material);
517
  building.castShadow = true;
518
  building.receiveShadow = true;
519
  return building;
 
552
  }
553
  }
554
 
555
+ startGameTimer() {
556
+ if (this.gameTimer) {
557
+ clearInterval(this.gameTimer);
558
+ }
559
+
560
+ this.gameTimer = setInterval(() => {
561
+ if (this.isLoading || this.isGameOver) {
562
+ clearInterval(this.gameTimer);
563
+ return;
564
+ }
565
+
566
+ this.gameTime--;
567
+ document.getElementById('time').textContent = `Time: ${this.gameTime}s`;
568
+
569
+ if (this.gameTime <= 0) {
570
+ clearInterval(this.gameTimer);
571
+ this.endGame();
572
+ }
573
+ }, 1000);
574
+ }
575
+
576
  spawnEnemies() {
577
  const spawnEnemy = () => {
578
  if (this.enemies.length < ENEMY_COUNT_MAX && !this.isGameOver) {
 
625
  return null;
626
  }
627
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
628
  updateParticles() {
629
  for (let i = this.particles.length - 1; i >= 0; i--) {
630
  const particle = this.particles[i];
 
634
  }
635
  }
636
  }
637
+
638
+ createExplosion(position) {
639
  for (let i = 0; i < PARTICLE_COUNT; i++) {
640
  this.particles.push(new Particle(this.scene, position));
641
  }
 
666
  });
667
  });
668
 
669
+ // ν”Œλ ˆμ΄μ–΄ μ΄μ•Œκ³Ό 적 좩돌 체크
670
+ this.tank.bullets.forEach((bullet, bulletIndex) => {
671
+ this.enemies.forEach((enemy, enemyIndex) => {
672
+ if (!enemy.mesh || !enemy.isLoaded) return;
673
+
674
+ const distance = bullet.position.distanceTo(enemy.mesh.position);
675
+ if (distance < 2) {
676
+ if (enemy.takeDamage(50)) {
677
+ enemy.destroy();
678
+ this.enemies.splice(enemyIndex, 1);
679
+ this.score += 100;
680
+ document.getElementById('score').textContent = `Score: ${this.score}`;
681
+ }
682
+ this.scene.remove(bullet);
683
+ this.tank.bullets.splice(bulletIndex, 1);
684
+ this.createExplosion(bullet.position);
685
+ }
686
+ });
687
+ });
688
+
689
  // ν”Œλ ˆμ΄μ–΄ 탱크와 건물 좩돌 체크
690
  const tankBoundingBox = new THREE.Box3().setFromObject(this.tank.body);
691
  for (const building of this.buildings) {
 
697
  }
698
 
699
  // 이전 μœ„μΉ˜ μ €μž₯
700
+ this.previousTankPosition.copy(this.tank.body.position);
701
  }
702
 
703
  endGame() {
704
+ if (this.isGameOver) return;
705
+
706
  this.isGameOver = true;
707
+
708
+ if (this.gameTimer) {
709
+ clearInterval(this.gameTimer);
710
+ }
711
+
712
  if (this.animationFrameId) {
713
  cancelAnimationFrame(this.animationFrameId);
714
  }
715
 
716
+ document.exitPointerLock();
717
+
718
  const gameOverDiv = document.createElement('div');
719
  gameOverDiv.style.position = 'absolute';
720
  gameOverDiv.style.top = '50%';
721
  gameOverDiv.style.left = '50%';
722
  gameOverDiv.style.transform = 'translate(-50%, -50%)';
723
+ gameOverDiv.style.color = '#0f0';
724
  gameOverDiv.style.fontSize = '48px';
725
+ gameOverDiv.style.backgroundColor = 'rgba(0, 20, 0, 0.7)';
726
  gameOverDiv.style.padding = '20px';
727
  gameOverDiv.style.borderRadius = '10px';
728
+ gameOverDiv.style.textAlign = 'center';
729
  gameOverDiv.innerHTML = `
730
  Game Over<br>
731
  Score: ${this.score}<br>
732
  Time Survived: ${GAME_DURATION - this.gameTime}s<br>
733
  <button onclick="location.reload()"
734
  style="font-size: 24px; padding: 10px; margin-top: 20px;
735
+ cursor: pointer; background: #0f0; border: none;
736
+ color: black; border-radius: 5px;">
737
  Play Again
738
  </button>
739
  `;
740
  document.body.appendChild(gameOverDiv);
741
  }
742
 
743
+ updateUI() {
744
+ if (!this.isGameOver) {
745
+ const healthBar = document.getElementById('health');
746
+ if (healthBar) {
747
+ healthBar.style.width = `${(this.tank.health / MAX_HEALTH) * 100}%`;
748
+ }
749
+
750
+ const timeElement = document.getElementById('time');
751
+ if (timeElement) {
752
+ timeElement.textContent = `Time: ${this.gameTime}s`;
753
+ }
754
+
755
+ const scoreElement = document.getElementById('score');
756
+ if (scoreElement) {
757
+ scoreElement.textContent = `Score: ${this.score}`;
758
+ }
759
+ }
760
+ }
761
+
762
  animate() {
763
  if (this.isGameOver) {
764
+ if (this.animationFrameId) {
765
+ cancelAnimationFrame(this.animationFrameId);
766
+ }
767
  return;
768
  }
769
 
 
773
  const deltaTime = (currentTime - this.lastTime) / 1000;
774
  this.lastTime = currentTime;
775
 
776
+ if (!this.isLoading) {
777
+ this.handleMovement();
778
+ this.tank.update(this.mouse.x, this.mouse.y);
 
 
 
 
 
 
 
779
 
780
+ const tankPosition = this.tank.getPosition();
781
+ this.enemies.forEach(enemy => {
782
+ enemy.update(tankPosition);
783
+
784
+ if (enemy.isLoaded && enemy.mesh.position.distanceTo(tankPosition) < ENEMY_CONFIG.ATTACK_RANGE) {
785
+ enemy.shoot(tankPosition);
786
+ }
787
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
788
 
789
+ this.updateParticles();
790
+ this.checkCollisions();
791
+ this.updateUI();
792
  }
793
+
794
+ this.renderer.render(this.scene, this.camera);
795
  }
796
+ }
797
 
798
+ // Start game
799
  window.startGame = function() {
800
  document.getElementById('startScreen').style.display = 'none';
801
  document.body.requestPointerLock();
802
+
803
+ // Create new game instance or restart existing one
804
+ if (!window.gameInstance) {
805
+ window.gameInstance = new Game();
806
+ }
807
  };
808
 
809
+ // Initialize game
810
+ document.addEventListener('DOMContentLoaded', () => {
811
+ const game = new Game();
812
+ });