cutechicken commited on
Commit
358f719
ยท
verified ยท
1 Parent(s): 9bac41b

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +396 -361
game.js CHANGED
@@ -1779,82 +1779,78 @@ class Game {
1779
  }
1780
 
1781
  setupEventListeners() {
1782
- document.addEventListener('keydown', (event) => {
1783
- if (this.isGameOver) return;
1784
-
1785
- // gameStarted ๋Œ€์‹  this.isStarted ์‚ฌ์šฉ
1786
- if (!this.isStarted) return;
1787
-
1788
- switch(event.code) {
1789
- case 'KeyW':
1790
- this.keys.w = true;
1791
- console.log('W key pressed - Accelerating');
1792
- break;
1793
- case 'KeyA':
1794
- this.keys.a = true;
1795
- console.log('A key pressed - Turning left');
1796
- break;
1797
- case 'KeyS':
1798
- this.keys.s = true;
1799
- console.log('S key pressed - Decelerating');
1800
- break;
1801
- case 'KeyD':
1802
- this.keys.d = true;
1803
- console.log('D key pressed - Turning right');
1804
- break;
1805
- case 'KeyF':
1806
- this.keys.f = true;
1807
- break;
1808
- }
1809
- });
1810
 
1811
- document.addEventListener('keyup', (event) => {
1812
- if (this.isGameOver) return;
1813
-
1814
- // gameStarted ๋Œ€์‹  this.isStarted ์‚ฌ์šฉ
1815
- if (!this.isStarted) return;
1816
-
1817
- switch(event.code) {
1818
- case 'KeyW': this.keys.w = false; break;
1819
- case 'KeyA': this.keys.a = false; break;
1820
- case 'KeyS': this.keys.s = false; break;
1821
- case 'KeyD': this.keys.d = false; break;
1822
- case 'KeyF': this.keys.f = false; break;
1823
- }
1824
- });
1825
 
1826
- document.addEventListener('mousemove', (event) => {
1827
- // ์—ฌ๊ธฐ๋„ gameStarted๋ฅผ this.isStarted๋กœ ๋ณ€๊ฒฝ
1828
- if (!document.pointerLockElement || this.isGameOver || !this.isStarted) return;
1829
-
1830
- const deltaX = event.movementX || 0;
1831
- const deltaY = event.movementY || 0;
1832
-
1833
- this.fighter.updateMouseInput(deltaX, deltaY);
1834
- });
1835
 
1836
- document.addEventListener('mousedown', (event) => {
1837
- // ์—ฌ๊ธฐ๋„ gameStarted๋ฅผ this.isStarted๋กœ ๋ณ€๊ฒฝ
1838
- if (!document.pointerLockElement || this.isGameOver || !this.isStarted) return;
1839
-
1840
- if (event.button === 0) {
1841
- this.fighter.isMouseDown = true;
1842
- this.lastShootTime = 0;
1843
- }
1844
- });
1845
 
1846
- document.addEventListener('mouseup', (event) => {
1847
- if (event.button === 0) {
1848
- this.fighter.isMouseDown = false;
1849
- }
1850
- });
1851
 
1852
- window.addEventListener('resize', () => {
1853
- this.camera.aspect = window.innerWidth / window.innerHeight;
1854
- this.camera.updateProjectionMatrix();
1855
- this.renderer.setSize(window.innerWidth, window.innerHeight);
1856
- });
1857
- }
1858
 
1859
  startGame() {
1860
  if (!this.isLoaded) {
@@ -2519,72 +2515,88 @@ class Game {
2519
  animateImpact();
2520
 
2521
  // ์ž‘์€ ์ถฉ๋Œ์Œ
2522
- try {
2523
- const impactSound = new Audio('sounds/hit.ogg');
2524
- impactSound.volume = 0.2;
2525
- impactSound.play().catch(e => {
2526
- console.log('Impact sound not found or failed to play');
2527
- });
2528
- } catch (e) {
2529
- console.log('Impact sound error:', e);
 
2530
  }
2531
- }
2532
  checkCollisions() {
2533
- // ํ”Œ๋ ˆ์ด์–ด ํƒ„ํ™˜ vs ์ ๊ธฐ ์ถฉ๋Œ
2534
- for (let i = this.fighter.bullets.length - 1; i >= 0; i--) {
2535
- const bullet = this.fighter.bullets[i];
2536
-
2537
- for (let j = this.enemies.length - 1; j >= 0; j--) {
2538
- const enemy = this.enemies[j];
2539
- if (!enemy.mesh || !enemy.isLoaded) continue;
2540
-
2541
- const distance = bullet.position.distanceTo(enemy.position);
2542
- if (distance < 90) {
2543
- // ํžˆํŠธ ํ‘œ์‹œ ์ถ”๊ฐ€
2544
- this.showHitMarker(enemy.position);
2545
- // ํ”ผ๊ฒฉ ์ดํŽ™ํŠธ ์ถ”๊ฐ€
2546
- this.createHitEffect(enemy.position);
2547
-
2548
- // ํƒ„ํ™˜ ์ œ๊ฑฐ๋Š” ์ดํŽ™ํŠธ ์ƒ์„ฑ ํ›„์—
2549
- this.scene.remove(bullet);
2550
- this.fighter.bullets.splice(i, 1);
2551
 
2552
- if (enemy.takeDamage(GAME_CONSTANTS.BULLET_DAMAGE)) {
2553
- // ์ ๊ธฐ ํŒŒ๊ดด ์‹œ ํญ๋ฐœ ํšจ๊ณผ ์ถ”๊ฐ€ - ์œ„์น˜ ํ™•์ธ
2554
- this.createExplosionEffect(enemy.position);
 
 
 
 
 
2555
 
2556
- enemy.destroy();
2557
- this.enemies.splice(j, 1);
2558
- this.score += 100;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2559
  }
2560
- break;
2561
  }
2562
  }
2563
- }
2564
-
2565
- // ์  ํƒ„ํ™˜ vs ํ”Œ๋ ˆ์ด์–ด ์ถฉ๋Œ
2566
- this.enemies.forEach(enemy => {
2567
- for (let index = enemy.bullets.length - 1; index >= 0; index--) {
2568
- const bullet = enemy.bullets[index];
2569
- const distance = bullet.position.distanceTo(this.fighter.position);
2570
- if (distance < 100) {
2571
- // ํ”Œ๋ ˆ์ด์–ด ํ”ผ๊ฒฉ ์ดํŽ™ํŠธ
2572
- this.createHitEffect(this.fighter.position);
2573
-
2574
- // ํƒ„ํ™˜ ์ œ๊ฑฐ
2575
- this.scene.remove(bullet);
2576
- enemy.bullets.splice(index, 1);
2577
-
2578
- if (this.fighter.takeDamage(GAME_CONSTANTS.BULLET_DAMAGE)) {
2579
- // ํ”Œ๋ ˆ์ด์–ด ํŒŒ๊ดด ์‹œ ํญ๋ฐœ ํšจ๊ณผ ์ถ”๊ฐ€
2580
- this.createExplosionEffect(this.fighter.position);
2581
 
2582
- this.endGame(false);
 
 
 
 
 
 
 
 
 
2583
  }
2584
  }
2585
- }
2586
- });
2587
- }
2588
 
2589
  createHitEffect(position) {
2590
  // ํ”ผ๊ฒฉ ํŒŒํ‹ฐํด ํšจ๊ณผ ์ƒ์„ฑ
@@ -2685,169 +2697,192 @@ class Game {
2685
  }
2686
 
2687
  createExplosionEffect(position) {
2688
- // ํญ๋ฐœ์Œ์„ ๊ฐ€์žฅ ๋จผ์ € ์žฌ์ƒ (์‹œ๊ฐํšจ๊ณผ๋ณด๋‹ค ์šฐ์„ )
2689
- try {
2690
- const explosionSound = new Audio('sounds/bang.ogg');
2691
- explosionSound.volume = 1.0; // ์ตœ๋Œ€ ์Œ๋Ÿ‰
2692
 
2693
- // ์ฆ‰์‹œ ์žฌ์ƒ ์‹œ๋„
2694
- const playPromise = explosionSound.play();
2695
- if (playPromise !== undefined) {
2696
- playPromise.catch(e => console.log('Explosion sound failed:', e));
2697
  }
2698
- } catch (e) {
2699
- console.log('Explosion sound error:', e);
2700
- }
2701
-
2702
- // ๋ฉ”์ธ ํญ๋ฐœ ํ”Œ๋ž˜์‹œ
2703
- const explosionGeometry = new THREE.SphereGeometry(50, 16, 16);
2704
- const explosionMaterial = new THREE.MeshBasicMaterial({
2705
- color: 0xffaa00,
2706
- emissive: 0xffaa00,
2707
- emissiveIntensity: 3.0,
2708
- transparent: true,
2709
- opacity: 1.0
2710
- });
2711
-
2712
- const explosion = new THREE.Mesh(explosionGeometry, explosionMaterial);
2713
- explosion.position.copy(position);
2714
- this.scene.add(explosion);
2715
-
2716
- // ํญ๋ฐœ ํŒŒํŽธ๋“ค
2717
- const debrisCount = 30;
2718
- const debris = [];
2719
-
2720
- for (let i = 0; i < debrisCount; i++) {
2721
- const debrisGeometry = new THREE.BoxGeometry(
2722
- 2 + Math.random() * 4,
2723
- 2 + Math.random() * 4,
2724
- 2 + Math.random() * 4
2725
- );
2726
- const debrisMaterial = new THREE.MeshBasicMaterial({
2727
- color: Math.random() > 0.5 ? 0xff6600 : 0x333333,
 
 
 
2728
  transparent: true,
2729
  opacity: 1.0
2730
  });
2731
 
2732
- const debrisPiece = new THREE.Mesh(debrisGeometry, debrisMaterial);
2733
- debrisPiece.position.copy(position);
 
 
2734
 
2735
- // ๋žœ๋ค ์†๋„์™€ ํšŒ์ „
2736
- debrisPiece.velocity = new THREE.Vector3(
2737
- (Math.random() - 0.5) * 200,
2738
- (Math.random() - 0.5) * 200,
2739
- (Math.random() - 0.5) * 200
2740
- );
2741
- debrisPiece.rotationSpeed = new THREE.Vector3(
2742
- Math.random() * 10,
2743
- Math.random() * 10,
2744
- Math.random() * 10
2745
- );
2746
- debrisPiece.life = 2.0;
2747
 
2748
- this.scene.add(debrisPiece);
2749
- debris.push(debrisPiece);
2750
- }
2751
-
2752
- // ์—ฐ๊ธฐ ํšจ๊ณผ
2753
- const smokeCount = 10;
2754
- const smoke = [];
2755
-
2756
- for (let i = 0; i < smokeCount; i++) {
2757
- const smokeGeometry = new THREE.SphereGeometry(10 + Math.random() * 20, 8, 8);
2758
- const smokeMaterial = new THREE.MeshBasicMaterial({
2759
- color: 0x222222,
2760
- transparent: true,
2761
- opacity: 0.8
2762
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2763
 
2764
- const smokePuff = new THREE.Mesh(smokeGeometry, smokeMaterial);
2765
- smokePuff.position.copy(position);
2766
- smokePuff.position.add(new THREE.Vector3(
2767
- (Math.random() - 0.5) * 20,
2768
- (Math.random() - 0.5) * 20,
2769
- (Math.random() - 0.5) * 20
2770
- ));
2771
-
2772
- smokePuff.velocity = new THREE.Vector3(
2773
- (Math.random() - 0.5) * 50,
2774
- Math.random() * 50 + 20,
2775
- (Math.random() - 0.5) * 50
2776
- );
2777
- smokePuff.life = 3.0;
2778
 
2779
- this.scene.add(smokePuff);
2780
- smoke.push(smokePuff);
2781
- }
2782
-
2783
- // ์• ๋‹ˆ๋ฉ”์ด์…˜
2784
- const animateExplosion = () => {
2785
- let allDead = true;
2786
-
2787
- // ๋ฉ”์ธ ํญ๋ฐœ ์• ๋‹ˆ๋ฉ”์ด์…˜
2788
- if (explosion.material.opacity > 0) {
2789
- explosion.material.opacity -= 0.02;
2790
- explosion.scale.multiplyScalar(1.08);
2791
- allDead = false;
2792
- } else if (explosion.parent) {
2793
- this.scene.remove(explosion);
2794
- }
2795
-
2796
- // ํŒŒํŽธ ์• ๋‹ˆ๋ฉ”์ด์…˜
2797
- debris.forEach(piece => {
2798
- if (piece.life > 0) {
2799
- allDead = false;
2800
- piece.life -= 0.02;
2801
-
2802
- // ์œ„์น˜ ์—…๋ฐ์ดํŠธ
2803
- piece.position.add(piece.velocity.clone().multiplyScalar(0.02));
2804
-
2805
- // ์ค‘๋ ฅ
2806
- piece.velocity.y -= 3;
2807
-
2808
- // ํšŒ์ „
2809
- piece.rotation.x += piece.rotationSpeed.x * 0.02;
2810
- piece.rotation.y += piece.rotationSpeed.y * 0.02;
2811
- piece.rotation.z += piece.rotationSpeed.z * 0.02;
2812
-
2813
- // ํŽ˜์ด๋“œ ์•„์›ƒ
2814
- piece.material.opacity = piece.life / 2;
2815
- } else if (piece.parent) {
2816
- this.scene.remove(piece);
2817
- }
2818
- });
2819
 
2820
- // ์—ฐ๊ธฐ ์• ๋‹ˆ๋ฉ”์ด์…˜
2821
- smoke.forEach(puff => {
2822
- if (puff.life > 0) {
 
 
 
 
 
2823
  allDead = false;
2824
- puff.life -= 0.02;
2825
-
2826
- // ์œ„์น˜ ์—…๋ฐ์ดํŠธ
2827
- puff.position.add(puff.velocity.clone().multiplyScalar(0.02));
2828
-
2829
- // ์ƒ์Šน ๊ฐ์†
2830
- puff.velocity.y *= 0.98;
2831
- puff.velocity.x *= 0.98;
2832
- puff.velocity.z *= 0.98;
2833
-
2834
- // ํ™•์‚ฐ
2835
- puff.scale.multiplyScalar(1.02);
2836
-
2837
- // ํŽ˜์ด๋“œ ์•„์›ƒ
2838
- puff.material.opacity = (puff.life / 3) * 0.8;
2839
- } else if (puff.parent) {
2840
- this.scene.remove(puff);
2841
  }
2842
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2843
 
2844
- if (!allDead) {
2845
- requestAnimationFrame(animateExplosion);
2846
- }
2847
- };
2848
-
2849
- animateExplosion();
2850
- }
2851
 
2852
  showHitMarker(position) {
2853
  // ํžˆํŠธ ๋งˆ์ปค div ์ƒ์„ฑ
@@ -2900,101 +2935,101 @@ class Game {
2900
  }
2901
 
2902
  animate() {
2903
- if (this.isGameOver) return;
2904
-
2905
- this.animationFrameId = requestAnimationFrame(() => this.animate());
2906
-
2907
- const currentTime = performance.now();
2908
- const deltaTime = Math.min((currentTime - this.lastTime) / 1000, 0.1);
2909
- this.lastTime = currentTime;
2910
-
2911
- if (this.isLoaded && this.fighter.isLoaded && this.isStarted) {
2912
- // ํ‚ค ์ƒํƒœ ๋””๋ฒ„๊น…
2913
- if (this.keys.w || this.keys.s || this.keys.a || this.keys.d) {
2914
- console.log('animate() - Keys state:', this.keys);
2915
- }
2916
-
2917
- // Fํ‚ค ์ƒํƒœ๋ฅผ Fighter์— ์ „๋‹ฌ
2918
- this.fighter.escapeKeyPressed = this.keys.f;
2919
 
2920
- // ์ปจํŠธ๋กค ์—…๋ฐ์ดํŠธ - ๋ฐ˜๋“œ์‹œ ๋ฌผ๋ฆฌ ์—…๋ฐ์ดํŠธ ์ „์— ํ˜ธ์ถœ
2921
- this.fighter.updateControls(this.keys, deltaTime);
2922
 
2923
- // ๋ฌผ๋ฆฌ ์—…๋ฐ์ดํŠธ
2924
- this.fighter.updatePhysics(deltaTime);
 
2925
 
2926
- // ํƒ„ํ™˜ ์—…๋ฐ์ดํŠธ
2927
- this.fighter.updateBullets(this.scene, deltaTime, this);
2928
-
2929
- // ๋งˆ์šฐ์Šค ๋ˆ„๋ฆ„ ์ƒํƒœ์ผ ๏ฟฝ๏ฟฝ ์—ฐ์† ๋ฐœ์‚ฌ
2930
- if (this.fighter.isMouseDown) {
2931
- const currentShootTime = Date.now();
2932
- if (!this.lastShootTime || currentShootTime - this.lastShootTime >= 100) {
2933
- this.fighter.shoot(this.scene);
2934
- this.lastShootTime = currentShootTime;
2935
  }
2936
- }
2937
-
2938
- // ์ ๊ธฐ ์—…๋ฐ์ดํŠธ
2939
- this.enemies.forEach(enemy => {
2940
- enemy.nearbyEnemies = this.enemies;
2941
- });
2942
-
2943
- this.enemies.forEach(enemy => {
2944
- enemy.update(this.fighter.position, deltaTime);
2945
- });
2946
-
2947
- // ์ถฉ๋Œ ์ฒดํฌ
2948
- this.checkCollisions();
2949
-
2950
- // ๊ฒŒ์ž„ ์ข…๋ฃŒ ์กฐ๊ฑด ์ฒดํฌ
2951
- if (this.fighter.health <= 0) {
2952
- if (this.fighter.position.y <= 0) {
2953
- this.endGame(false, "GROUND COLLISION");
2954
- } else {
2955
- this.endGame(false);
2956
  }
2957
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2958
  }
2959
 
2960
- // UI ์—…๋ฐ์ดํŠธ
2961
- this.updateUI();
2962
- this.updateRadar();
2963
-
2964
- // ์ ์ด ๋ชจ๋‘ ์ œ๊ฑฐ๋˜์—ˆ๋Š”์ง€ ์ฒดํฌ
2965
- if (this.enemies.length === 0) {
2966
- this.endGame(true);
2967
  }
2968
- } else if (this.isLoaded && this.fighter.isLoaded) {
2969
- // ๊ฒŒ์ž„์ด ์‹œ์ž‘๋˜์ง€ ์•Š์•˜์„ ๋•Œ๋„ ๋ฌผ๋ฆฌ๋Š” ์—…๋ฐ์ดํŠธ (์นด๋ฉ”๋ผ ์›€์ง์ž„์„ ์œ„ํ•ด)
2970
- this.fighter.updatePhysics(deltaTime);
2971
- }
2972
-
2973
- // ๊ตฌ๋ฆ„ ์• ๋‹ˆ๋ฉ”์ด์…˜
2974
- if (this.clouds) {
2975
- this.clouds.forEach(cloud => {
2976
- cloud.userData.time += deltaTime;
2977
- cloud.position.x += cloud.userData.driftSpeed;
2978
- cloud.position.y = cloud.userData.initialY +
2979
- Math.sin(cloud.userData.time * cloud.userData.floatSpeed) * 20;
2980
-
2981
- const mapLimit = GAME_CONSTANTS.MAP_SIZE / 2;
2982
- if (cloud.position.x > mapLimit) cloud.position.x = -mapLimit;
2983
- if (cloud.position.x < -mapLimit) cloud.position.x = mapLimit;
2984
- });
2985
- }
2986
-
2987
- // ์นด๋ฉ”๋ผ ์—…๋ฐ์ดํŠธ
2988
- if (this.fighter.isLoaded) {
2989
- const targetCameraPos = this.fighter.getCameraPosition();
2990
- const targetCameraTarget = this.fighter.getCameraTarget();
2991
 
2992
- this.camera.position.lerp(targetCameraPos, this.fighter.cameraLag);
2993
- this.camera.lookAt(targetCameraTarget);
2994
  }
2995
-
2996
- this.renderer.render(this.scene, this.camera);
2997
- }
2998
 
2999
  endGame(victory = false, reason = "") {
3000
  this.isGameOver = true;
 
1779
  }
1780
 
1781
  setupEventListeners() {
1782
+ document.addEventListener('keydown', (event) => {
1783
+ if (this.isGameOver) return;
1784
+
1785
+ if (!this.isStarted) return;
1786
+
1787
+ switch(event.code) {
1788
+ case 'KeyW':
1789
+ this.keys.w = true;
1790
+ console.log('W key pressed - Accelerating');
1791
+ break;
1792
+ case 'KeyA':
1793
+ this.keys.a = true;
1794
+ console.log('A key pressed - Turning left');
1795
+ break;
1796
+ case 'KeyS':
1797
+ this.keys.s = true;
1798
+ console.log('S key pressed - Decelerating');
1799
+ break;
1800
+ case 'KeyD':
1801
+ this.keys.d = true;
1802
+ console.log('D key pressed - Turning right');
1803
+ break;
1804
+ case 'KeyF':
1805
+ this.keys.f = true;
1806
+ break;
1807
+ }
1808
+ });
 
1809
 
1810
+ document.addEventListener('keyup', (event) => {
1811
+ if (this.isGameOver) return;
1812
+
1813
+ if (!this.isStarted) return;
1814
+
1815
+ switch(event.code) {
1816
+ case 'KeyW': this.keys.w = false; break;
1817
+ case 'KeyA': this.keys.a = false; break;
1818
+ case 'KeyS': this.keys.s = false; break;
1819
+ case 'KeyD': this.keys.d = false; break;
1820
+ case 'KeyF': this.keys.f = false; break;
1821
+ }
1822
+ });
 
1823
 
1824
+ document.addEventListener('mousemove', (event) => {
1825
+ if (!document.pointerLockElement || this.isGameOver || !this.isStarted) return;
1826
+
1827
+ const deltaX = event.movementX || 0;
1828
+ const deltaY = event.movementY || 0;
1829
+
1830
+ this.fighter.updateMouseInput(deltaX, deltaY);
1831
+ });
 
1832
 
1833
+ document.addEventListener('mousedown', (event) => {
1834
+ if (!document.pointerLockElement || this.isGameOver || !this.isStarted) return;
1835
+
1836
+ if (event.button === 0) {
1837
+ this.fighter.isMouseDown = true;
1838
+ this.lastShootTime = 0;
1839
+ }
1840
+ });
 
1841
 
1842
+ document.addEventListener('mouseup', (event) => {
1843
+ if (event.button === 0) {
1844
+ this.fighter.isMouseDown = false;
1845
+ }
1846
+ });
1847
 
1848
+ window.addEventListener('resize', () => {
1849
+ this.camera.aspect = window.innerWidth / window.innerHeight;
1850
+ this.camera.updateProjectionMatrix();
1851
+ this.renderer.setSize(window.innerWidth, window.innerHeight);
1852
+ });
1853
+ }
1854
 
1855
  startGame() {
1856
  if (!this.isLoaded) {
 
2515
  animateImpact();
2516
 
2517
  // ์ž‘์€ ์ถฉ๋Œ์Œ
2518
+ try {
2519
+ const impactSound = new Audio('sounds/hit.ogg');
2520
+ impactSound.volume = 0.2;
2521
+ impactSound.play().catch(e => {
2522
+ console.log('Impact sound not found or failed to play');
2523
+ });
2524
+ } catch (e) {
2525
+ console.log('Impact sound error:', e);
2526
+ }
2527
  }
2528
+
2529
  checkCollisions() {
2530
+ // ํ”Œ๋ ˆ์ด์–ด ํƒ„ํ™˜ vs ์ ๊ธฐ ์ถฉ๋Œ
2531
+ for (let i = this.fighter.bullets.length - 1; i >= 0; i--) {
2532
+ const bullet = this.fighter.bullets[i];
2533
+
2534
+ for (let j = this.enemies.length - 1; j >= 0; j--) {
2535
+ const enemy = this.enemies[j];
2536
+ if (!enemy.mesh || !enemy.isLoaded) continue;
 
 
 
 
 
 
 
 
 
 
 
2537
 
2538
+ const distance = bullet.position.distanceTo(enemy.position);
2539
+ if (distance < 90) {
2540
+ console.log(`Hit detected! Distance: ${distance}, Enemy health: ${enemy.health}`);
2541
+
2542
+ // ํžˆํŠธ ํ‘œ์‹œ ์ถ”๊ฐ€
2543
+ this.showHitMarker(enemy.position);
2544
+ // ํ”ผ๊ฒฉ ์ดํŽ™ํŠธ ์ถ”๊ฐ€
2545
+ this.createHitEffect(enemy.position);
2546
 
2547
+ // ํƒ„ํ™˜ ์ œ๊ฑฐ
2548
+ this.scene.remove(bullet);
2549
+ this.fighter.bullets.splice(i, 1);
2550
+
2551
+ // ์ ๊ธฐ์— ๋ฐ๋ฏธ์ง€ ์ž…ํžˆ๊ธฐ
2552
+ const isDestroyed = enemy.takeDamage(GAME_CONSTANTS.BULLET_DAMAGE);
2553
+ console.log(`Enemy damaged. New health: ${enemy.health}, Destroyed: ${isDestroyed}`);
2554
+
2555
+ if (isDestroyed) {
2556
+ console.log('Enemy destroyed! Creating explosion effect...');
2557
+
2558
+ // ํญ๋ฐœ ํšจ๊ณผ ์ƒ์„ฑ - ์œ„์น˜๋ฅผ ๋ณต์‚ฌํ•ด์„œ ์ „๋‹ฌ
2559
+ const explosionPosition = enemy.position.clone();
2560
+ console.log('Explosion position:', explosionPosition);
2561
+
2562
+ // ํญ๋ฐœ ํšจ๊ณผ ์ฆ‰์‹œ ์ƒ์„ฑ
2563
+ this.createExplosionEffect(explosionPosition);
2564
+
2565
+ // ์ ๊ธฐ ์ œ๊ฑฐ
2566
+ enemy.destroy();
2567
+ this.enemies.splice(j, 1);
2568
+ this.score += 100;
2569
+
2570
+ console.log(`Enemy removed. Remaining enemies: ${this.enemies.length}`);
2571
+ }
2572
+ break;
2573
  }
 
2574
  }
2575
  }
2576
+
2577
+ // ์  ํƒ„ํ™˜ vs ํ”Œ๋ ˆ์ด์–ด ์ถฉ๋Œ
2578
+ this.enemies.forEach(enemy => {
2579
+ for (let index = enemy.bullets.length - 1; index >= 0; index--) {
2580
+ const bullet = enemy.bullets[index];
2581
+ const distance = bullet.position.distanceTo(this.fighter.position);
2582
+ if (distance < 100) {
2583
+ // ํ”Œ๋ ˆ์ด์–ด ํ”ผ๊ฒฉ ์ดํŽ™ํŠธ
2584
+ this.createHitEffect(this.fighter.position);
 
 
 
 
 
 
 
 
 
2585
 
2586
+ // ํƒ„ํ™˜ ์ œ๊ฑฐ
2587
+ this.scene.remove(bullet);
2588
+ enemy.bullets.splice(index, 1);
2589
+
2590
+ if (this.fighter.takeDamage(GAME_CONSTANTS.BULLET_DAMAGE)) {
2591
+ // ํ”Œ๋ ˆ์ด์–ด ํŒŒ๊ดด ์‹œ ํญ๋ฐœ ํšจ๊ณผ ์ถ”๊ฐ€
2592
+ this.createExplosionEffect(this.fighter.position);
2593
+
2594
+ this.endGame(false);
2595
+ }
2596
  }
2597
  }
2598
+ });
2599
+ }
 
2600
 
2601
  createHitEffect(position) {
2602
  // ํ”ผ๊ฒฉ ํŒŒํ‹ฐํด ํšจ๊ณผ ์ƒ์„ฑ
 
2697
  }
2698
 
2699
  createExplosionEffect(position) {
2700
+ console.log('createExplosionEffect called with position:', position);
 
 
 
2701
 
2702
+ // ์œ„์น˜๊ฐ€ ์œ ํšจํ•œ์ง€ ํ™•์ธ
2703
+ if (!position || typeof position.x === 'undefined') {
2704
+ console.error('Invalid position for explosion effect');
2705
+ return;
2706
  }
2707
+
2708
+ // ํญ๋ฐœ์Œ์„ ๊ฐ€์žฅ ๋จผ์ € ์žฌ์ƒ (์‹œ๊ฐํšจ๊ณผ๋ณด๋‹ค ์šฐ์„ )
2709
+ try {
2710
+ const explosionSound = new Audio('sounds/bang.ogg');
2711
+ explosionSound.volume = 1.0; // ์ตœ๋Œ€ ์Œ๋Ÿ‰
2712
+
2713
+ // ํ”Œ๋ ˆ์ด์–ด์™€์˜ ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ฅธ ๋ณผ๋ฅจ ์กฐ์ •
2714
+ if (this.fighter && this.fighter.position) {
2715
+ const distanceToPlayer = position.distanceTo(this.fighter.position);
2716
+ const maxAudibleDistance = 5000;
2717
+ if (distanceToPlayer < maxAudibleDistance) {
2718
+ explosionSound.volume = Math.max(0.3, 1.0 - (distanceToPlayer / maxAudibleDistance));
2719
+ console.log(`Explosion sound volume: ${explosionSound.volume} (distance: ${distanceToPlayer})`);
2720
+ }
2721
+ }
2722
+
2723
+ // ์ฆ‰์‹œ ์žฌ์ƒ ์‹œ๋„
2724
+ const playPromise = explosionSound.play();
2725
+ if (playPromise !== undefined) {
2726
+ playPromise
2727
+ .then(() => console.log('Explosion sound played successfully'))
2728
+ .catch(e => console.error('Explosion sound failed:', e));
2729
+ }
2730
+ } catch (e) {
2731
+ console.error('Explosion sound error:', e);
2732
+ }
2733
+
2734
+ // ๋ฉ”์ธ ํญ๋ฐœ ํ”Œ๋ž˜์‹œ
2735
+ const explosionGeometry = new THREE.SphereGeometry(50, 16, 16);
2736
+ const explosionMaterial = new THREE.MeshBasicMaterial({
2737
+ color: 0xffaa00,
2738
+ emissive: 0xffaa00,
2739
+ emissiveIntensity: 3.0,
2740
  transparent: true,
2741
  opacity: 1.0
2742
  });
2743
 
2744
+ const explosion = new THREE.Mesh(explosionGeometry, explosionMaterial);
2745
+ explosion.position.copy(position);
2746
+ this.scene.add(explosion);
2747
+ console.log('Explosion mesh added to scene');
2748
 
2749
+ // ํญ๋ฐœ ํŒŒํŽธ๋“ค
2750
+ const debrisCount = 30;
2751
+ const debris = [];
 
 
 
 
 
 
 
 
 
2752
 
2753
+ for (let i = 0; i < debrisCount; i++) {
2754
+ const debrisGeometry = new THREE.BoxGeometry(
2755
+ 2 + Math.random() * 4,
2756
+ 2 + Math.random() * 4,
2757
+ 2 + Math.random() * 4
2758
+ );
2759
+ const debrisMaterial = new THREE.MeshBasicMaterial({
2760
+ color: Math.random() > 0.5 ? 0xff6600 : 0x333333,
2761
+ transparent: true,
2762
+ opacity: 1.0
2763
+ });
2764
+
2765
+ const debrisPiece = new THREE.Mesh(debrisGeometry, debrisMaterial);
2766
+ debrisPiece.position.copy(position);
2767
+
2768
+ // ๋žœ๋ค ์†๋„์™€ ํšŒ์ „
2769
+ debrisPiece.velocity = new THREE.Vector3(
2770
+ (Math.random() - 0.5) * 200,
2771
+ (Math.random() - 0.5) * 200,
2772
+ (Math.random() - 0.5) * 200
2773
+ );
2774
+ debrisPiece.rotationSpeed = new THREE.Vector3(
2775
+ Math.random() * 10,
2776
+ Math.random() * 10,
2777
+ Math.random() * 10
2778
+ );
2779
+ debrisPiece.life = 2.0;
2780
+
2781
+ this.scene.add(debrisPiece);
2782
+ debris.push(debrisPiece);
2783
+ }
2784
 
2785
+ // ์—ฐ๊ธฐ ํšจ๊ณผ
2786
+ const smokeCount = 10;
2787
+ const smoke = [];
 
 
 
 
 
 
 
 
 
 
 
2788
 
2789
+ for (let i = 0; i < smokeCount; i++) {
2790
+ const smokeGeometry = new THREE.SphereGeometry(10 + Math.random() * 20, 8, 8);
2791
+ const smokeMaterial = new THREE.MeshBasicMaterial({
2792
+ color: 0x222222,
2793
+ transparent: true,
2794
+ opacity: 0.8
2795
+ });
2796
+
2797
+ const smokePuff = new THREE.Mesh(smokeGeometry, smokeMaterial);
2798
+ smokePuff.position.copy(position);
2799
+ smokePuff.position.add(new THREE.Vector3(
2800
+ (Math.random() - 0.5) * 20,
2801
+ (Math.random() - 0.5) * 20,
2802
+ (Math.random() - 0.5) * 20
2803
+ ));
2804
+
2805
+ smokePuff.velocity = new THREE.Vector3(
2806
+ (Math.random() - 0.5) * 50,
2807
+ Math.random() * 50 + 20,
2808
+ (Math.random() - 0.5) * 50
2809
+ );
2810
+ smokePuff.life = 3.0;
2811
+
2812
+ this.scene.add(smokePuff);
2813
+ smoke.push(smokePuff);
2814
+ }
2815
+
2816
+ console.log(`Explosion effect created with ${debrisCount} debris and ${smokeCount} smoke particles`);
 
 
 
 
 
 
 
 
 
 
 
 
2817
 
2818
+ // ์• ๋‹ˆ๋ฉ”์ด์…˜
2819
+ const animateExplosion = () => {
2820
+ let allDead = true;
2821
+
2822
+ // ๋ฉ”์ธ ํญ๋ฐœ ์• ๋‹ˆ๋ฉ”์ด์…˜
2823
+ if (explosion.material.opacity > 0) {
2824
+ explosion.material.opacity -= 0.02;
2825
+ explosion.scale.multiplyScalar(1.08);
2826
  allDead = false;
2827
+ } else if (explosion.parent) {
2828
+ this.scene.remove(explosion);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2829
  }
2830
+
2831
+ // ํŒŒํŽธ ์• ๋‹ˆ๋ฉ”์ด์…˜
2832
+ debris.forEach(piece => {
2833
+ if (piece.life > 0) {
2834
+ allDead = false;
2835
+ piece.life -= 0.02;
2836
+
2837
+ // ์œ„์น˜ ์—…๋ฐ์ดํŠธ
2838
+ piece.position.add(piece.velocity.clone().multiplyScalar(0.02));
2839
+
2840
+ // ์ค‘๋ ฅ
2841
+ piece.velocity.y -= 3;
2842
+
2843
+ // ํšŒ์ „
2844
+ piece.rotation.x += piece.rotationSpeed.x * 0.02;
2845
+ piece.rotation.y += piece.rotationSpeed.y * 0.02;
2846
+ piece.rotation.z += piece.rotationSpeed.z * 0.02;
2847
+
2848
+ // ํŽ˜์ด๋“œ ์•„์›ƒ
2849
+ piece.material.opacity = piece.life / 2;
2850
+ } else if (piece.parent) {
2851
+ this.scene.remove(piece);
2852
+ }
2853
+ });
2854
+
2855
+ // ์—ฐ๊ธฐ ์• ๋‹ˆ๋ฉ”์ด์…˜
2856
+ smoke.forEach(puff => {
2857
+ if (puff.life > 0) {
2858
+ allDead = false;
2859
+ puff.life -= 0.02;
2860
+
2861
+ // ์œ„์น˜ ์—…๋ฐ์ดํŠธ
2862
+ puff.position.add(puff.velocity.clone().multiplyScalar(0.02));
2863
+
2864
+ // ์ƒ์Šน ๊ฐ์†
2865
+ puff.velocity.y *= 0.98;
2866
+ puff.velocity.x *= 0.98;
2867
+ puff.velocity.z *= 0.98;
2868
+
2869
+ // ํ™•์‚ฐ
2870
+ puff.scale.multiplyScalar(1.02);
2871
+
2872
+ // ํŽ˜์ด๋“œ ์•„์›ƒ
2873
+ puff.material.opacity = (puff.life / 3) * 0.8;
2874
+ } else if (puff.parent) {
2875
+ this.scene.remove(puff);
2876
+ }
2877
+ });
2878
+
2879
+ if (!allDead) {
2880
+ requestAnimationFrame(animateExplosion);
2881
+ }
2882
+ };
2883
 
2884
+ animateExplosion();
2885
+ }
 
 
 
 
 
2886
 
2887
  showHitMarker(position) {
2888
  // ํžˆํŠธ ๋งˆ์ปค div ์ƒ์„ฑ
 
2935
  }
2936
 
2937
  animate() {
2938
+ if (this.isGameOver) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2939
 
2940
+ this.animationFrameId = requestAnimationFrame(() => this.animate());
 
2941
 
2942
+ const currentTime = performance.now();
2943
+ const deltaTime = Math.min((currentTime - this.lastTime) / 1000, 0.1);
2944
+ this.lastTime = currentTime;
2945
 
2946
+ if (this.isLoaded && this.fighter.isLoaded && this.isStarted) {
2947
+ // ํ‚ค ์ƒํƒœ ๋””๋ฒ„๊น…
2948
+ if (this.keys.w || this.keys.s || this.keys.a || this.keys.d) {
2949
+ console.log('animate() - Keys state:', this.keys);
 
 
 
 
 
2950
  }
2951
+
2952
+ // Fํ‚ค ์ƒํƒœ๋ฅผ Fighter์— ์ „๋‹ฌ
2953
+ this.fighter.escapeKeyPressed = this.keys.f;
2954
+
2955
+ // ์ปจํŠธ๋กค ์—…๋ฐ์ดํŠธ - ๋ฐ˜๋“œ์‹œ ๋ฌผ๋ฆฌ ์—…๋ฐ์ดํŠธ ์ „์— ํ˜ธ์ถœ
2956
+ this.fighter.updateControls(this.keys, deltaTime);
2957
+
2958
+ // ๋ฌผ๋ฆฌ ์—…๋ฐ์ดํŠธ
2959
+ this.fighter.updatePhysics(deltaTime);
2960
+
2961
+ // ํƒ„ํ™˜ ์—…๋ฐ์ดํŠธ
2962
+ this.fighter.updateBullets(this.scene, deltaTime, this);
2963
+
2964
+ // ๋งˆ์šฐ์Šค ๋ˆ„๋ฆ„ ์ƒํƒœ์ผ ๋•Œ ์—ฐ์† ๋ฐœ์‚ฌ
2965
+ if (this.fighter.isMouseDown) {
2966
+ const currentShootTime = Date.now();
2967
+ if (!this.lastShootTime || currentShootTime - this.lastShootTime >= 100) {
2968
+ this.fighter.shoot(this.scene);
2969
+ this.lastShootTime = currentShootTime;
2970
+ }
2971
  }
2972
+
2973
+ // ์ ๊ธฐ ์—…๋ฐ์ดํŠธ
2974
+ this.enemies.forEach(enemy => {
2975
+ enemy.nearbyEnemies = this.enemies;
2976
+ });
2977
+
2978
+ this.enemies.forEach(enemy => {
2979
+ enemy.update(this.fighter.position, deltaTime);
2980
+ });
2981
+
2982
+ // ์ถฉ๋Œ ์ฒดํฌ
2983
+ this.checkCollisions();
2984
+
2985
+ // ๊ฒŒ์ž„ ์ข…๋ฃŒ ์กฐ๊ฑด ์ฒดํฌ
2986
+ if (this.fighter.health <= 0) {
2987
+ if (this.fighter.position.y <= 0) {
2988
+ this.endGame(false, "GROUND COLLISION");
2989
+ } else {
2990
+ this.endGame(false);
2991
+ }
2992
+ return;
2993
+ }
2994
+
2995
+ // UI ์—…๋ฐ์ดํŠธ
2996
+ this.updateUI();
2997
+ this.updateRadar();
2998
+
2999
+ // ์ ์ด ๋ชจ๋‘ ์ œ๊ฑฐ๋˜์—ˆ๋Š”์ง€ ์ฒดํฌ
3000
+ if (this.enemies.length === 0) {
3001
+ this.endGame(true);
3002
+ }
3003
+ } else if (this.isLoaded && this.fighter.isLoaded) {
3004
+ // ๊ฒŒ์ž„์ด ์‹œ์ž‘๋˜์ง€ ์•Š์•˜์„ ๋•Œ๋„ ๋ฌผ๋ฆฌ๋Š” ์—…๋ฐ์ดํŠธ (์นด๋ฉ”๋ผ ์›€์ง์ž„์„ ์œ„ํ•ด)
3005
+ this.fighter.updatePhysics(deltaTime);
3006
+ }
3007
+
3008
+ // ๊ตฌ๋ฆ„ ์• ๋‹ˆ๋ฉ”์ด์…˜
3009
+ if (this.clouds) {
3010
+ this.clouds.forEach(cloud => {
3011
+ cloud.userData.time += deltaTime;
3012
+ cloud.position.x += cloud.userData.driftSpeed;
3013
+ cloud.position.y = cloud.userData.initialY +
3014
+ Math.sin(cloud.userData.time * cloud.userData.floatSpeed) * 20;
3015
+
3016
+ const mapLimit = GAME_CONSTANTS.MAP_SIZE / 2;
3017
+ if (cloud.position.x > mapLimit) cloud.position.x = -mapLimit;
3018
+ if (cloud.position.x < -mapLimit) cloud.position.x = mapLimit;
3019
+ });
3020
  }
3021
 
3022
+ // ์นด๋ฉ”๋ผ ์—…๋ฐ์ดํŠธ
3023
+ if (this.fighter.isLoaded) {
3024
+ const targetCameraPos = this.fighter.getCameraPosition();
3025
+ const targetCameraTarget = this.fighter.getCameraTarget();
3026
+
3027
+ this.camera.position.lerp(targetCameraPos, this.fighter.cameraLag);
3028
+ this.camera.lookAt(targetCameraTarget);
3029
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3030
 
3031
+ this.renderer.render(this.scene, this.camera);
 
3032
  }
 
 
 
3033
 
3034
  endGame(victory = false, reason = "") {
3035
  this.isGameOver = true;