cutechicken commited on
Commit
accbf92
β€’
1 Parent(s): 3799414

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +170 -122
game.js CHANGED
@@ -31,7 +31,11 @@ class TankPlayer {
31
  this.turnSpeed = 0.03;
32
  this.turretGroup = new THREE.Group();
33
  this.health = MAX_HEALTH;
34
- this.isLoaded = false; // λ‘œλ”© μƒνƒœ μΆ”κ°€
 
 
 
 
35
  }
36
 
37
  async initialize(scene, loader) {
@@ -62,15 +66,44 @@ class TankPlayer {
62
  });
63
 
64
  scene.add(this.body);
65
- this.isLoaded = true; // λ‘œλ”© μ™„λ£Œ ν‘œμ‹œ
66
 
67
  } catch (error) {
68
  console.error('Error loading tank models:', error);
69
- this.isLoaded = false; // λ‘œλ”© μ‹€νŒ¨ ν‘œμ‹œ
70
  }
71
  }
72
 
73
- // λ‚˜λ¨Έμ§€ TankPlayer λ©”μ„œλ“œλ“€μ€ 동일...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  update(mouseX, mouseY) {
75
  if (!this.body || !this.turretGroup) return;
76
 
@@ -83,6 +116,19 @@ class TankPlayer {
83
  while (normalizedDiff < -Math.PI) normalizedDiff += Math.PI * 2;
84
 
85
  this.turretGroup.rotation.y += normalizedDiff * 0.1;
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  }
87
 
88
  move(direction) {
@@ -262,28 +308,24 @@ class Game {
262
  this.renderer = new THREE.WebGLRenderer({ antialias: true });
263
  this.renderer.setSize(window.innerWidth, window.innerHeight);
264
  this.renderer.shadowMap.enabled = true;
265
- document.body.appendChild(this.renderer.domElement);
266
 
267
  // κ²Œμž„ μš”μ†Œ μ΄ˆκΈ°ν™”
268
  this.tank = new TankPlayer();
269
  this.enemies = [];
270
  this.particles = [];
271
- this.buildings = []; // 건물 λ°°μ—΄ μΆ”κ°€
272
  this.loader = new GLTFLoader();
273
  this.controls = null;
274
  this.gameTime = GAME_DURATION;
275
  this.score = 0;
276
  this.isGameOver = false;
277
- this.isLoading = true; // λ‘œλ”© μƒνƒœ μΆ”κ°€
278
  this.previousTankPosition = new THREE.Vector3();
 
279
 
280
- // 마우슀 μƒνƒœ
281
- this.mouse = {
282
- x: 0,
283
- y: 0
284
- };
285
-
286
- // ν‚€λ³΄λ“œ μƒνƒœ
287
  this.keys = {
288
  forward: false,
289
  backward: false,
@@ -293,8 +335,6 @@ class Game {
293
 
294
  // 이벀트 λ¦¬μŠ€λ„ˆ μ„€μ •
295
  this.setupEventListeners();
296
-
297
- // κ²Œμž„ μ΄ˆκΈ°ν™”
298
  this.initialize();
299
  }
300
 
@@ -311,14 +351,15 @@ class Game {
311
  directionalLight.shadow.mapSize.height = 2048;
312
  this.scene.add(directionalLight);
313
 
314
- // λ„μ‹œ λ°”λ‹₯ 생성 (μ•„μŠ€νŒ”νŠΈ 질감)
315
- const groundGeometry = new THREE.PlaneGeometry(MAP_SIZE, MAP_SIZE);
316
- const groundMaterial = new THREE.MeshStandardMaterial({
317
- color: 0x333333, // μ–΄λ‘μš΄ νšŒμƒ‰ (μ•„μŠ€νŒ”νŠΈ 색)
318
- roughness: 0.9,
319
- metalness: 0.1
320
- });
321
- const ground = new THREE.Mesh(groundGeometry, groundMaterial);
 
322
  ground.rotation.x = -Math.PI / 2;
323
  ground.receiveShadow = true;
324
  this.scene.add(ground);
@@ -332,11 +373,8 @@ class Game {
332
  throw new Error('Tank loading failed');
333
  }
334
 
335
- // 카메라 μœ„μΉ˜ μ„€μ •
336
- this.camera.position.set(0, 10, -10);
337
- this.camera.lookAt(0, 0, 0);
338
-
339
- // 포인터 락 컨트둀 μ„€μ •
340
  this.controls = new PointerLockControls(this.camera, document.body);
341
 
342
  // λ‘œλ”© μ™„λ£Œ
@@ -354,7 +392,89 @@ class Game {
354
  }
355
  }
356
 
357
- async createBuildings() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  const buildingTypes = [
359
  { width: 10, height: 30, depth: 10, color: 0x808080 },
360
  { width: 15, height: 40, depth: 15, color: 0x606060 },
@@ -365,7 +485,6 @@ class Game {
365
  const type = buildingTypes[Math.floor(Math.random() * buildingTypes.length)];
366
  const building = this.createBuilding(type);
367
 
368
- // 건물 μœ„μΉ˜ μ„€μ • (λ‹€λ₯Έ 건물과 κ²ΉμΉ˜μ§€ μ•Šκ²Œ)
369
  let position;
370
  let attempts = 0;
371
  do {
@@ -389,7 +508,6 @@ class Game {
389
  const geometry = new THREE.BoxGeometry(type.width, type.height, type.depth);
390
  const material = new THREE.MeshPhongMaterial({
391
  color: type.color,
392
- // μ°½λ¬Έ 효과λ₯Ό μœ„ν•œ μ„€μ •
393
  emissive: 0x222222,
394
  specular: 0x111111,
395
  shininess: 30
@@ -401,7 +519,7 @@ class Game {
401
  }
402
 
403
  checkBuildingCollision(position, type) {
404
- const margin = 5; // 건물 κ°„ μ΅œμ†Œ 거리
405
  const bbox = new THREE.Box3(
406
  new THREE.Vector3(
407
  position.x - (type.width / 2 + margin),
@@ -438,7 +556,6 @@ class Game {
438
  if (this.enemies.length < ENEMY_COUNT_MAX && !this.isGameOver) {
439
  const position = this.getValidEnemySpawnPosition();
440
  if (position) {
441
- // λžœλ€ν•˜κ²Œ 적 μœ ν˜• 선택 (70% tank, 30% heavy)
442
  const type = Math.random() < 0.7 ? 'tank' : 'heavy';
443
  const enemy = new Enemy(this.scene, position, type);
444
  enemy.initialize(this.loader);
@@ -464,11 +581,9 @@ class Game {
464
  (Math.random() - 0.5) * (MAP_SIZE - margin * 2)
465
  );
466
 
467
- // ν”Œλ ˆμ΄μ–΄μ™€μ˜ 거리 체크
468
  const distanceToPlayer = position.distanceTo(this.tank.getPosition());
469
  if (distanceToPlayer < 100) continue;
470
 
471
- // 건물과의 좩돌 체크
472
  let collisionFound = false;
473
  for (const building of this.buildings) {
474
  const buildingBox = new THREE.Box3().setFromObject(building);
@@ -498,62 +613,6 @@ class Game {
498
  }, 1000);
499
  }
500
 
501
- setupEventListeners() {
502
- // ν‚€λ³΄λ“œ 이벀트
503
- document.addEventListener('keydown', (event) => {
504
- if (this.isLoading) return;
505
-
506
- switch(event.code) {
507
- case 'KeyW': this.keys.forward = true; break;
508
- case 'KeyS': this.keys.backward = true; break;
509
- case 'KeyA': this.keys.left = true; break;
510
- case 'KeyD': this.keys.right = true; break;
511
- }
512
- });
513
-
514
- document.addEventListener('keyup', (event) => {
515
- if (this.isLoading) return;
516
-
517
- switch(event.code) {
518
- case 'KeyW': this.keys.forward = false; break;
519
- case 'KeyS': this.keys.backward = false; break;
520
- case 'KeyA': this.keys.left = false; break;
521
- case 'KeyD': this.keys.right = false; break;
522
- }
523
- });
524
-
525
- // 마우슀 이벀트
526
- document.addEventListener('mousemove', (event) => {
527
- if (this.isLoading) return;
528
-
529
- this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
530
- this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
531
- });
532
-
533
- // μ°½ 크기 λ³€κ²½ 이벀트
534
- window.addEventListener('resize', () => {
535
- this.camera.aspect = window.innerWidth / window.innerHeight;
536
- this.camera.updateProjectionMatrix();
537
- this.renderer.setSize(window.innerWidth, window.innerHeight);
538
- });
539
- }
540
-
541
- handleMovement() {
542
- if (this.isLoading || !this.tank.isLoaded) return;
543
-
544
- const direction = new THREE.Vector3();
545
-
546
- if (this.keys.forward) direction.z += 1;
547
- if (this.keys.backward) direction.z -= 1;
548
- if (this.keys.left) this.tank.rotate(-1);
549
- if (this.keys.right) this.tank.rotate(1);
550
-
551
- if (direction.length() > 0) {
552
- direction.normalize();
553
- this.tank.move(direction);
554
- }
555
- }
556
-
557
  updateParticles() {
558
  for (let i = this.particles.length - 1; i >= 0; i--) {
559
  const particle = this.particles[i];
@@ -575,7 +634,6 @@ class Game {
575
 
576
  const tankPosition = this.tank.getPosition();
577
 
578
- // 적과의 좩돌 체크
579
  this.enemies.forEach(enemy => {
580
  if (!enemy.mesh || !enemy.isLoaded) return;
581
 
@@ -588,7 +646,6 @@ class Game {
588
  this.scene.remove(bullet);
589
  enemy.bullets = enemy.bullets.filter(b => b !== bullet);
590
 
591
- // 피격 효과
592
  this.createExplosion(bullet.position);
593
  document.getElementById('health').style.width =
594
  `${(this.tank.health / MAX_HEALTH) * 100}%`;
@@ -596,24 +653,20 @@ class Game {
596
  });
597
  });
598
 
599
- // 건물과의 좩돌 체크
600
  const tankBoundingBox = new THREE.Box3().setFromObject(this.tank.body);
601
  for (const building of this.buildings) {
602
  const buildingBox = new THREE.Box3().setFromObject(building);
603
  if (tankBoundingBox.intersectsBox(buildingBox)) {
604
- // 좩돌 μ‹œ 탱크λ₯Ό 이전 μœ„μΉ˜λ‘œ 되돌림
605
  this.tank.body.position.copy(this.previousTankPosition);
606
  break;
607
  }
608
  }
609
 
610
- // ν˜„μž¬ μœ„μΉ˜ μ €μž₯
611
  this.previousTankPosition = this.tank.body.position.clone();
612
  }
613
 
614
  endGame() {
615
  this.isGameOver = true;
616
- // κ²Œμž„ μ˜€λ²„ UI ν‘œμ‹œ
617
  const gameOverDiv = document.createElement('div');
618
  gameOverDiv.style.position = 'absolute';
619
  gameOverDiv.style.top = '50%';
@@ -635,63 +688,52 @@ class Game {
635
  Play Again
636
  </button>
637
  `;
638
- document.body.appendChild(gameOverDiv); // 이 쀄이 λˆ„λ½λ¨
639
- }
640
- animate() {
 
641
  if (this.isGameOver) return;
642
 
643
  requestAnimationFrame(() => this.animate());
644
 
645
- // λ‘œλ”© 쀑이면 λ Œλ”λ§λ§Œ μˆ˜ν–‰
 
 
 
646
  if (this.isLoading) {
647
  this.renderer.render(this.scene, this.camera);
648
  return;
649
  }
650
 
651
- // 탱크 μ—…λ°μ΄νŠΈ
652
- this.tank.update(this.mouse.x, this.mouse.y);
653
  this.handleMovement();
654
-
655
- // 적 μ—…λ°μ΄νŠΈ
656
  const tankPosition = this.tank.getPosition();
657
- this.enemies.forEach((enemy, index) => {
658
  enemy.update(tankPosition);
659
 
660
- if (enemy.isLoaded) { // λ‘œλ”©λœ 적만 곡격 μˆ˜ν–‰
661
- const distance = enemy.mesh.position.distanceTo(tankPosition);
662
- if (distance < ENEMY_CONFIG.ATTACK_RANGE) {
663
- enemy.shoot(tankPosition);
664
- }
665
  }
666
  });
667
 
668
- // νŒŒν‹°ν΄ μ—…λ°μ΄νŠΈ
669
  this.updateParticles();
670
-
671
- // 좩돌 체크
672
  this.checkCollisions();
673
-
674
- // UI μ—…λ°μ΄νŠΈ
675
  this.updateUI();
676
-
677
- // λ Œλ”λ§
678
  this.renderer.render(this.scene, this.camera);
679
  }
680
 
681
  updateUI() {
682
- // 체λ ₯λ°” μ—…λ°μ΄νŠΈ
683
  const healthBar = document.getElementById('health');
684
  if (healthBar) {
685
  healthBar.style.width = `${(this.tank.health / MAX_HEALTH) * 100}%`;
686
  }
687
 
688
- // μ‹œκ°„ μ—…λ°μ΄νŠΈ
689
  const timeElement = document.getElementById('time');
690
  if (timeElement) {
691
  timeElement.textContent = `Time: ${this.gameTime}s`;
692
  }
693
 
694
- // 점수 μ—…λ°μ΄νŠΈ
695
  const scoreElement = document.getElementById('score');
696
  if (scoreElement) {
697
  scoreElement.textContent = `Score: ${this.score}`;
@@ -699,5 +741,11 @@ class Game {
699
  }
700
  }
701
 
 
 
 
 
 
 
702
  // κ²Œμž„ μΈμŠ€ν„΄μŠ€ 생성
703
  const game = new Game();
 
31
  this.turnSpeed = 0.03;
32
  this.turretGroup = new THREE.Group();
33
  this.health = MAX_HEALTH;
34
+ this.isLoaded = false;
35
+ this.ammo = 10;
36
+ this.lastShootTime = 0;
37
+ this.shootInterval = 1000;
38
+ this.bullets = [];
39
  }
40
 
41
  async initialize(scene, loader) {
 
66
  });
67
 
68
  scene.add(this.body);
69
+ this.isLoaded = true;
70
 
71
  } catch (error) {
72
  console.error('Error loading tank models:', error);
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
+ // μ΄μ•Œ 생성
82
+ const bulletGeometry = new THREE.SphereGeometry(0.2);
83
+ const bulletMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
84
+ const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
85
+
86
+ // μ΄μ•Œ μ‹œμž‘ μœ„μΉ˜ (포탑 끝)
87
+ const bulletOffset = new THREE.Vector3(0, 0.5, 2);
88
+ bulletOffset.applyQuaternion(this.turret.quaternion);
89
+ bullet.position.copy(this.body.position).add(bulletOffset);
90
+
91
+ // μ΄μ•Œ 속도 (카메라/포탑 λ°©ν–₯)
92
+ const direction = new THREE.Vector3(0, 0, 1);
93
+ direction.applyQuaternion(this.turret.quaternion);
94
+ bullet.velocity = direction.multiplyScalar(2);
95
+
96
+ scene.add(bullet);
97
+ this.bullets.push(bullet);
98
+ this.ammo--;
99
+ this.lastShootTime = currentTime;
100
+
101
+ // UI μ—…λ°μ΄νŠΈ
102
+ document.getElementById('ammo').textContent = `Ammo: ${this.ammo}/10`;
103
+
104
+ return bullet;
105
+ }
106
+
107
  update(mouseX, mouseY) {
108
  if (!this.body || !this.turretGroup) return;
109
 
 
116
  while (normalizedDiff < -Math.PI) normalizedDiff += Math.PI * 2;
117
 
118
  this.turretGroup.rotation.y += normalizedDiff * 0.1;
119
+
120
+ // μ΄μ•Œ μ—…λ°μ΄νŠΈ
121
+ for (let i = this.bullets.length - 1; i >= 0; i--) {
122
+ const bullet = this.bullets[i];
123
+ bullet.position.add(bullet.velocity);
124
+
125
+ // μ΄μ•Œμ΄ 맡 λ°–μœΌλ‘œ λ‚˜κ°€λ©΄ 제거
126
+ if (Math.abs(bullet.position.x) > MAP_SIZE/2 ||
127
+ Math.abs(bullet.position.z) > MAP_SIZE/2) {
128
+ scene.remove(bullet);
129
+ this.bullets.splice(i, 1);
130
+ }
131
+ }
132
  }
133
 
134
  move(direction) {
 
308
  this.renderer = new THREE.WebGLRenderer({ antialias: true });
309
  this.renderer.setSize(window.innerWidth, window.innerHeight);
310
  this.renderer.shadowMap.enabled = true;
311
+ document.getElementById('gameContainer').appendChild(this.renderer.domElement);
312
 
313
  // κ²Œμž„ μš”μ†Œ μ΄ˆκΈ°ν™”
314
  this.tank = new TankPlayer();
315
  this.enemies = [];
316
  this.particles = [];
317
+ this.buildings = [];
318
  this.loader = new GLTFLoader();
319
  this.controls = null;
320
  this.gameTime = GAME_DURATION;
321
  this.score = 0;
322
  this.isGameOver = false;
323
+ this.isLoading = true;
324
  this.previousTankPosition = new THREE.Vector3();
325
+ this.lastTime = performance.now();
326
 
327
+ // 마우슀/ν‚€λ³΄λ“œ μƒνƒœ
328
+ this.mouse = { x: 0, y: 0 };
 
 
 
 
 
329
  this.keys = {
330
  forward: false,
331
  backward: false,
 
335
 
336
  // 이벀트 λ¦¬μŠ€λ„ˆ μ„€μ •
337
  this.setupEventListeners();
 
 
338
  this.initialize();
339
  }
340
 
 
351
  directionalLight.shadow.mapSize.height = 2048;
352
  this.scene.add(directionalLight);
353
 
354
+ // μ§€ν˜• 생성
355
+ const ground = new THREE.Mesh(
356
+ new THREE.PlaneGeometry(MAP_SIZE, MAP_SIZE),
357
+ new THREE.MeshStandardMaterial({
358
+ color: 0x333333,
359
+ roughness: 0.9,
360
+ metalness: 0.1
361
+ })
362
+ );
363
  ground.rotation.x = -Math.PI / 2;
364
  ground.receiveShadow = true;
365
  this.scene.add(ground);
 
373
  throw new Error('Tank loading failed');
374
  }
375
 
376
+ // FPS 카메라 μ„€μ •
377
+ this.camera.position.set(0, 2, 0);
 
 
 
378
  this.controls = new PointerLockControls(this.camera, document.body);
379
 
380
  // λ‘œλ”© μ™„λ£Œ
 
392
  }
393
  }
394
 
395
+ setupEventListeners() {
396
+ // ν‚€λ³΄λ“œ 이벀트
397
+ document.addEventListener('keydown', (event) => {
398
+ if (this.isLoading) return;
399
+ switch(event.code) {
400
+ case 'KeyW': this.keys.forward = true; break;
401
+ case 'KeyS': this.keys.backward = true; break;
402
+ case 'KeyA': this.keys.left = true; break;
403
+ case 'KeyD': this.keys.right = true; break;
404
+ case 'Escape':
405
+ if (this.controls.isLocked) {
406
+ this.controls.unlock();
407
+ }
408
+ break;
409
+ }
410
+ });
411
+
412
+ document.addEventListener('keyup', (event) => {
413
+ if (this.isLoading) return;
414
+ switch(event.code) {
415
+ case 'KeyW': this.keys.forward = false; break;
416
+ case 'KeyS': this.keys.backward = false; break;
417
+ case 'KeyA': this.keys.left = false; break;
418
+ case 'KeyD': this.keys.right = false; break;
419
+ }
420
+ });
421
+
422
+ // λ°œμ‚¬ 및 포인터 락 이벀트
423
+ document.addEventListener('click', () => {
424
+ if (document.pointerLockElement !== document.body) {
425
+ this.controls.lock();
426
+ } else {
427
+ const bullet = this.tank.shoot(this.scene);
428
+ if (bullet) {
429
+ // μ΄μ•Œ λ°œμ‚¬ νš¨κ³ΌμŒμ΄λ‚˜ μ‹œκ°νš¨κ³Ό μΆ”κ°€ κ°€λŠ₯
430
+ }
431
+ }
432
+ });
433
+
434
+ // μ°½ 크기 λ³€κ²½ 이벀트
435
+ window.addEventListener('resize', () => {
436
+ this.camera.aspect = window.innerWidth / window.innerHeight;
437
+ this.camera.updateProjectionMatrix();
438
+ this.renderer.setSize(window.innerWidth, window.innerHeight);
439
+ });
440
+ }
441
+
442
+ handleMovement() {
443
+ if (!this.tank.isLoaded || !this.controls.isLocked) return;
444
+
445
+ const direction = new THREE.Vector3();
446
+ const cameraDirection = new THREE.Vector3();
447
+ this.camera.getWorldDirection(cameraDirection);
448
+
449
+ if (this.keys.forward) direction.z += 1;
450
+ if (this.keys.backward) direction.z -= 1;
451
+ if (this.keys.left) direction.x -= 1;
452
+ if (this.keys.right) direction.x += 1;
453
+
454
+ if (direction.length() > 0) {
455
+ direction.normalize();
456
+ // 카메라 λ°©ν–₯을 κΈ°μ€€μœΌλ‘œ 이동
457
+ direction.applyAxisAngle(new THREE.Vector3(0, 1, 0), this.camera.rotation.y);
458
+ this.tank.move(direction);
459
+
460
+ // 탱크 본체λ₯Ό 카메라 λ°©ν–₯으둜 νšŒμ „
461
+ if (Math.abs(direction.z) > 0 || Math.abs(direction.x) > 0) {
462
+ this.tank.body.rotation.y = Math.atan2(direction.x, direction.z);
463
+ }
464
+ }
465
+
466
+ // 카메라와 탱크 동기화
467
+ const tankPos = this.tank.getPosition();
468
+ this.camera.position.x = tankPos.x;
469
+ this.camera.position.z = tankPos.z;
470
+ this.camera.position.y = tankPos.y + 2;
471
+
472
+ // 포탑 νšŒμ „μ„ 카메라와 동기화
473
+ if (this.tank.turret) {
474
+ this.tank.turret.rotation.y = this.camera.rotation.y;
475
+ }
476
+ }
477
+ createBuildings() {
478
  const buildingTypes = [
479
  { width: 10, height: 30, depth: 10, color: 0x808080 },
480
  { width: 15, height: 40, depth: 15, color: 0x606060 },
 
485
  const type = buildingTypes[Math.floor(Math.random() * buildingTypes.length)];
486
  const building = this.createBuilding(type);
487
 
 
488
  let position;
489
  let attempts = 0;
490
  do {
 
508
  const geometry = new THREE.BoxGeometry(type.width, type.height, type.depth);
509
  const material = new THREE.MeshPhongMaterial({
510
  color: type.color,
 
511
  emissive: 0x222222,
512
  specular: 0x111111,
513
  shininess: 30
 
519
  }
520
 
521
  checkBuildingCollision(position, type) {
522
+ const margin = 5;
523
  const bbox = new THREE.Box3(
524
  new THREE.Vector3(
525
  position.x - (type.width / 2 + margin),
 
556
  if (this.enemies.length < ENEMY_COUNT_MAX && !this.isGameOver) {
557
  const position = this.getValidEnemySpawnPosition();
558
  if (position) {
 
559
  const type = Math.random() < 0.7 ? 'tank' : 'heavy';
560
  const enemy = new Enemy(this.scene, position, type);
561
  enemy.initialize(this.loader);
 
581
  (Math.random() - 0.5) * (MAP_SIZE - margin * 2)
582
  );
583
 
 
584
  const distanceToPlayer = position.distanceTo(this.tank.getPosition());
585
  if (distanceToPlayer < 100) continue;
586
 
 
587
  let collisionFound = false;
588
  for (const building of this.buildings) {
589
  const buildingBox = new THREE.Box3().setFromObject(building);
 
613
  }, 1000);
614
  }
615
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
616
  updateParticles() {
617
  for (let i = this.particles.length - 1; i >= 0; i--) {
618
  const particle = this.particles[i];
 
634
 
635
  const tankPosition = this.tank.getPosition();
636
 
 
637
  this.enemies.forEach(enemy => {
638
  if (!enemy.mesh || !enemy.isLoaded) return;
639
 
 
646
  this.scene.remove(bullet);
647
  enemy.bullets = enemy.bullets.filter(b => b !== bullet);
648
 
 
649
  this.createExplosion(bullet.position);
650
  document.getElementById('health').style.width =
651
  `${(this.tank.health / MAX_HEALTH) * 100}%`;
 
653
  });
654
  });
655
 
 
656
  const tankBoundingBox = new THREE.Box3().setFromObject(this.tank.body);
657
  for (const building of this.buildings) {
658
  const buildingBox = new THREE.Box3().setFromObject(building);
659
  if (tankBoundingBox.intersectsBox(buildingBox)) {
 
660
  this.tank.body.position.copy(this.previousTankPosition);
661
  break;
662
  }
663
  }
664
 
 
665
  this.previousTankPosition = this.tank.body.position.clone();
666
  }
667
 
668
  endGame() {
669
  this.isGameOver = true;
 
670
  const gameOverDiv = document.createElement('div');
671
  gameOverDiv.style.position = 'absolute';
672
  gameOverDiv.style.top = '50%';
 
688
  Play Again
689
  </button>
690
  `;
691
+ document.body.appendChild(gameOverDiv);
692
+ }
693
+
694
+ animate() {
695
  if (this.isGameOver) return;
696
 
697
  requestAnimationFrame(() => this.animate());
698
 
699
+ const currentTime = performance.now();
700
+ const deltaTime = (currentTime - this.lastTime) / 1000;
701
+ this.lastTime = currentTime;
702
+
703
  if (this.isLoading) {
704
  this.renderer.render(this.scene, this.camera);
705
  return;
706
  }
707
 
 
 
708
  this.handleMovement();
709
+ this.tank.update(this.mouse.x, this.mouse.y);
710
+
711
  const tankPosition = this.tank.getPosition();
712
+ this.enemies.forEach(enemy => {
713
  enemy.update(tankPosition);
714
 
715
+ if (enemy.isLoaded && enemy.mesh.position.distanceTo(tankPosition) < ENEMY_CONFIG.ATTACK_RANGE) {
716
+ enemy.shoot(tankPosition);
 
 
 
717
  }
718
  });
719
 
 
720
  this.updateParticles();
 
 
721
  this.checkCollisions();
 
 
722
  this.updateUI();
 
 
723
  this.renderer.render(this.scene, this.camera);
724
  }
725
 
726
  updateUI() {
 
727
  const healthBar = document.getElementById('health');
728
  if (healthBar) {
729
  healthBar.style.width = `${(this.tank.health / MAX_HEALTH) * 100}%`;
730
  }
731
 
 
732
  const timeElement = document.getElementById('time');
733
  if (timeElement) {
734
  timeElement.textContent = `Time: ${this.gameTime}s`;
735
  }
736
 
 
737
  const scoreElement = document.getElementById('score');
738
  if (scoreElement) {
739
  scoreElement.textContent = `Score: ${this.score}`;
 
741
  }
742
  }
743
 
744
+ // HTML의 startGame ν•¨μˆ˜μ™€ μ—°κ²°
745
+ window.startGame = function() {
746
+ document.getElementById('startScreen').style.display = 'none';
747
+ document.body.requestPointerLock();
748
+ };
749
+
750
  // κ²Œμž„ μΈμŠ€ν„΄μŠ€ 생성
751
  const game = new Game();