cutechicken commited on
Commit
14f5fcc
·
verified ·
1 Parent(s): 5efbc2d

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +200 -103
game.js CHANGED
@@ -807,106 +807,127 @@ class Fighter {
807
  }
808
 
809
  shoot(scene) {
810
- if (this.currentWeapon === 'MG') {
811
- // 기존 MG 발사 로직
812
- // 탄약이 없으면 발사하지 않음
813
- if (this.ammo <= 0) return;
814
-
815
- this.ammo--;
816
-
817
- // 직선 모양의 탄환 (100% 더 크게)
818
- const bulletGeometry = new THREE.CylinderGeometry(1.0, 1.0, 16, 8); // 반지름 0.75→1.0, 길이 12→16
819
- const bulletMaterial = new THREE.MeshBasicMaterial({
820
- color: 0xffff00,
821
- });
822
- const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
823
-
824
- // 기수 끝에서 발사 (쿼터니언 사용)
825
- const muzzleOffset = new THREE.Vector3(0, 0, 10);
826
-
827
- // 전투기와 동일한 쿼터니언 생성
828
- const quaternion = new THREE.Quaternion();
829
- const pitchQuat = new THREE.Quaternion();
830
- const yawQuat = new THREE.Quaternion();
831
- const rollQuat = new THREE.Quaternion();
832
-
833
- pitchQuat.setFromAxisAngle(new THREE.Vector3(1, 0, 0), this.rotation.x);
834
- yawQuat.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.rotation.y);
835
- rollQuat.setFromAxisAngle(new THREE.Vector3(0, 0, 1), this.rotation.z);
836
-
837
- quaternion.multiply(rollQuat);
838
- quaternion.multiply(pitchQuat);
839
- quaternion.multiply(yawQuat);
840
-
841
- muzzleOffset.applyQuaternion(quaternion);
842
- bullet.position.copy(this.position).add(muzzleOffset);
843
-
844
- // 탄환을 발사 방향으로 회전
845
- bullet.quaternion.copy(quaternion);
846
- // 실린더가 Z축 방향을 향하도록 추가 회전
847
- const cylinderRotation = new THREE.Quaternion();
848
- cylinderRotation.setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 2);
849
- bullet.quaternion.multiply(cylinderRotation);
850
-
851
- // 탄환 초기 위치 저장
852
- bullet.startPosition = bullet.position.clone();
853
-
854
- const bulletSpeed = 1500; // 1000에서 1500으로 증가 (50% 빠르게)
855
- const direction = new THREE.Vector3(0, 0, 1);
856
- direction.applyQuaternion(quaternion);
857
- bullet.velocity = direction.multiplyScalar(bulletSpeed);
858
-
859
- scene.add(bullet);
860
- this.bullets.push(bullet);
861
-
862
- // m134.ogg 소리 재생 - 중첩 제한 해제, 랜덤 피치
863
- try {
864
- const audio = new Audio('sounds/m134.ogg');
865
- audio.volume = 0.15; // 0.3에서 0.15로 감소 (50% 줄임)
866
-
867
- // -2 ~ +2 사이의 랜덤 피치 (playbackRate로 시뮬레이션)
868
- const randomPitch = 0.8 + Math.random() * 0.4; // 0.8 ~ 1.2 범위
869
- audio.playbackRate = randomPitch;
870
-
871
- audio.play().catch(e => console.log('Gunfire sound failed to play'));
872
-
873
- // 재생 완료 시 메모리 정리를 위해 참조 제거
874
- audio.addEventListener('ended', () => {
875
- audio.remove();
876
- });
877
- } catch (e) {
878
- console.log('Audio error:', e);
879
- }
880
- } else if (this.currentWeapon === 'AIM9') {
881
- // AIM-9 미사일 발사
882
- if (this.aim9Missiles <= 0) return;
883
- if (this.lockProgress < GAME_CONSTANTS.AIM9_LOCK_REQUIRED) return;
884
- if (!this.lockTarget) return;
885
-
886
- this.aim9Missiles--;
887
-
888
- // 미사일 생성
889
- const missile = new AIM9Missile(scene, this.position.clone(), this.lockTarget, this.rotation.clone());
890
- this.firedMissiles.push(missile);
891
-
892
- // 락온 초기화
893
- this.lockTarget = null;
894
- this.lockProgress = 0;
895
- if (this.lockAudios.locking && !this.lockAudios.locking.paused) {
896
- this.lockAudios.locking.pause();
897
- this.lockAudios.locking.currentTime = 0;
898
- }
899
-
900
- // 발사음
901
- try {
902
- const missileSound = new Audio('sounds/missile.ogg');
903
- missileSound.volume = 0.7;
904
- missileSound.play().catch(e => {});
905
- } catch (e) {
906
- console.log('Missile sound failed:', e);
907
- }
908
- }
909
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
910
 
911
  updateBullets(scene, deltaTime, gameInstance) {
912
  for (let i = this.bullets.length - 1; i >= 0; i--) {
@@ -996,13 +1017,17 @@ class AIM9Missile {
996
  this.position = position.clone();
997
  this.target = target;
998
  this.rotation = rotation.clone();
999
- this.speed = GAME_CONSTANTS.AIM9_SPEED;
1000
  this.mesh = null;
1001
  this.isLoaded = false;
1002
  this.lifeTime = 20; // 20초 후 자폭
1003
  this.turnRate = 3.0; // 초당 회전 속도 (라디안)
1004
  this.startPosition = position.clone();
1005
 
 
 
 
 
1006
  // 미사일 발사 방향 설정
1007
  const quaternion = new THREE.Quaternion();
1008
  const pitchQuat = new THREE.Quaternion();
@@ -1045,7 +1070,7 @@ class AIM9Missile {
1045
  const loader = new GLTFLoader();
1046
  const result = await loader.loadAsync('models/aim-9.glb');
1047
  this.mesh = result.scene;
1048
- this.mesh.scale.set(0.5, 0.5, 0.5);
1049
  this.isLoaded = true;
1050
  } catch (error) {
1051
  console.log('AIM-9 model not found, using fallback');
@@ -1099,7 +1124,7 @@ class AIM9Missile {
1099
  group.add(flame);
1100
 
1101
  this.mesh = group;
1102
- this.mesh.scale.set(1.5, 1.5, 1.5);
1103
  this.isLoaded = true;
1104
  }
1105
 
@@ -1145,6 +1170,31 @@ class AIM9Missile {
1145
  const lookAtTarget = this.position.clone().add(this.velocity);
1146
  this.mesh.lookAt(lookAtTarget);
1147
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1148
  // 사운드 볼륨 조정 (플레이어와의 거리에 따라)
1149
  if (this.swingAudio && playerPosition) {
1150
  const distanceToPlayer = this.position.distanceTo(playerPosition);
@@ -1164,6 +1214,45 @@ class AIM9Missile {
1164
  return 'flying';
1165
  }
1166
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1167
  onHit() {
1168
  // 명중 효과
1169
  if (window.gameInstance) {
@@ -1192,6 +1281,14 @@ class AIM9Missile {
1192
  this.scene.remove(this.mesh);
1193
  }
1194
 
 
 
 
 
 
 
 
 
1195
  if (this.swingAudio) {
1196
  this.swingAudio.pause();
1197
  this.swingAudio = null;
 
807
  }
808
 
809
  shoot(scene) {
810
+ if (this.currentWeapon === 'MG') {
811
+ // 기존 MG 발사 로직
812
+ // 탄약이 없으면 발사하지 않음
813
+ if (this.ammo <= 0) return;
814
+
815
+ this.ammo--;
816
+
817
+ // 직선 모양의 탄환 (100% 더 크게)
818
+ const bulletGeometry = new THREE.CylinderGeometry(1.0, 1.0, 16, 8); // 반지름 0.75→1.0, 길이 12→16
819
+ const bulletMaterial = new THREE.MeshBasicMaterial({
820
+ color: 0xffff00,
821
+ });
822
+ const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
823
+
824
+ // 기수 끝에�� 발사 (쿼터니언 사용)
825
+ const muzzleOffset = new THREE.Vector3(0, 0, 10);
826
+
827
+ // 전투기와 동일한 쿼터니언 생성
828
+ const quaternion = new THREE.Quaternion();
829
+ const pitchQuat = new THREE.Quaternion();
830
+ const yawQuat = new THREE.Quaternion();
831
+ const rollQuat = new THREE.Quaternion();
832
+
833
+ pitchQuat.setFromAxisAngle(new THREE.Vector3(1, 0, 0), this.rotation.x);
834
+ yawQuat.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.rotation.y);
835
+ rollQuat.setFromAxisAngle(new THREE.Vector3(0, 0, 1), this.rotation.z);
836
+
837
+ quaternion.multiply(rollQuat);
838
+ quaternion.multiply(pitchQuat);
839
+ quaternion.multiply(yawQuat);
840
+
841
+ muzzleOffset.applyQuaternion(quaternion);
842
+ bullet.position.copy(this.position).add(muzzleOffset);
843
+
844
+ // 탄환을 발사 방향으로 회전
845
+ bullet.quaternion.copy(quaternion);
846
+ // 실린더가 Z축 방향을 향하도록 추가 회전
847
+ const cylinderRotation = new THREE.Quaternion();
848
+ cylinderRotation.setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 2);
849
+ bullet.quaternion.multiply(cylinderRotation);
850
+
851
+ // 탄환 초기 위치 저장
852
+ bullet.startPosition = bullet.position.clone();
853
+
854
+ const bulletSpeed = 1500; // 1000에서 1500으로 증가 (50% 빠르게)
855
+ const direction = new THREE.Vector3(0, 0, 1);
856
+ direction.applyQuaternion(quaternion);
857
+ bullet.velocity = direction.multiplyScalar(bulletSpeed);
858
+
859
+ scene.add(bullet);
860
+ this.bullets.push(bullet);
861
+
862
+ // m134.ogg 소리 재생 - 중첩 제한 해제, 랜덤 피치
863
+ try {
864
+ const audio = new Audio('sounds/m134.ogg');
865
+ audio.volume = 0.15; // 0.3에서 0.15로 감소 (50% 줄임)
866
+
867
+ // -2 ~ +2 사이의 랜덤 피치 (playbackRate로 시뮬레이션)
868
+ const randomPitch = 0.8 + Math.random() * 0.4; // 0.8 ~ 1.2 범위
869
+ audio.playbackRate = randomPitch;
870
+
871
+ audio.play().catch(e => console.log('Gunfire sound failed to play'));
872
+
873
+ // 재생 완료 시 메모리 정리를 위해 참조 제거
874
+ audio.addEventListener('ended', () => {
875
+ audio.remove();
876
+ });
877
+ } catch (e) {
878
+ console.log('Audio error:', e);
879
+ }
880
+ } else if (this.currentWeapon === 'AIM9') {
881
+ // AIM-9 미사일 발사
882
+ if (this.aim9Missiles <= 0) return;
883
+ if (this.lockProgress < GAME_CONSTANTS.AIM9_LOCK_REQUIRED) return;
884
+ if (!this.lockTarget) return;
885
+
886
+ this.aim9Missiles--;
887
+
888
+ // 날개 발사 위치 결정 - 좌우 날개 중 무작위 선택
889
+ const isLeftWing = Math.random() < 0.5;
890
+ const wingOffset = new THREE.Vector3(isLeftWing ? -8 : 8, -1, 2); // 날개 위치
891
+
892
+ // 전투기의 회전을 적용
893
+ const quaternion = new THREE.Quaternion();
894
+ const pitchQuat = new THREE.Quaternion();
895
+ const yawQuat = new THREE.Quaternion();
896
+ const rollQuat = new THREE.Quaternion();
897
+
898
+ pitchQuat.setFromAxisAngle(new THREE.Vector3(1, 0, 0), this.rotation.x);
899
+ yawQuat.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.rotation.y);
900
+ rollQuat.setFromAxisAngle(new THREE.Vector3(0, 0, 1), this.rotation.z);
901
+
902
+ quaternion.multiply(rollQuat);
903
+ quaternion.multiply(pitchQuat);
904
+ quaternion.multiply(yawQuat);
905
+
906
+ wingOffset.applyQuaternion(quaternion);
907
+ const missileStartPos = this.position.clone().add(wingOffset);
908
+
909
+ // 미사일 생성
910
+ const missile = new AIM9Missile(scene, missileStartPos, this.lockTarget, this.rotation.clone());
911
+ this.firedMissiles.push(missile);
912
+
913
+ // 락온 초기화
914
+ this.lockTarget = null;
915
+ this.lockProgress = 0;
916
+ if (this.lockAudios.locking && !this.lockAudios.locking.paused) {
917
+ this.lockAudios.locking.pause();
918
+ this.lockAudios.locking.currentTime = 0;
919
+ }
920
+
921
+ // 발사음
922
+ try {
923
+ const missileSound = new Audio('sounds/missile.ogg');
924
+ missileSound.volume = 0.7;
925
+ missileSound.play().catch(e => {});
926
+ } catch (e) {
927
+ console.log('Missile sound failed:', e);
928
+ }
929
+ }
930
+ }
931
 
932
  updateBullets(scene, deltaTime, gameInstance) {
933
  for (let i = this.bullets.length - 1; i >= 0; i--) {
 
1017
  this.position = position.clone();
1018
  this.target = target;
1019
  this.rotation = rotation.clone();
1020
+ this.speed = 1028.8; // 2000kt를 m/s로 변환
1021
  this.mesh = null;
1022
  this.isLoaded = false;
1023
  this.lifeTime = 20; // 20초 후 자폭
1024
  this.turnRate = 3.0; // 초당 회전 속도 (라디안)
1025
  this.startPosition = position.clone();
1026
 
1027
+ // 추력 연기 파티클
1028
+ this.smokeTrail = [];
1029
+ this.smokeEmitTime = 0;
1030
+
1031
  // 미사일 발사 방향 설정
1032
  const quaternion = new THREE.Quaternion();
1033
  const pitchQuat = new THREE.Quaternion();
 
1070
  const loader = new GLTFLoader();
1071
  const result = await loader.loadAsync('models/aim-9.glb');
1072
  this.mesh = result.scene;
1073
+ this.mesh.scale.set(0.75, 0.75, 0.75); // 50% 더 크게 (0.5 -> 0.75)
1074
  this.isLoaded = true;
1075
  } catch (error) {
1076
  console.log('AIM-9 model not found, using fallback');
 
1124
  group.add(flame);
1125
 
1126
  this.mesh = group;
1127
+ this.mesh.scale.set(2.25, 2.25, 2.25); // 50% 더 크게 (1.5 -> 2.25)
1128
  this.isLoaded = true;
1129
  }
1130
 
 
1170
  const lookAtTarget = this.position.clone().add(this.velocity);
1171
  this.mesh.lookAt(lookAtTarget);
1172
 
1173
+ // 추력 연기 생성
1174
+ this.smokeEmitTime += deltaTime;
1175
+ if (this.smokeEmitTime >= 0.02) { // 0.02초마다 연기 생성
1176
+ this.smokeEmitTime = 0;
1177
+ this.createSmokeParticle();
1178
+ }
1179
+
1180
+ // 연기 파티클 업데이트
1181
+ for (let i = this.smokeTrail.length - 1; i >= 0; i--) {
1182
+ const smoke = this.smokeTrail[i];
1183
+ smoke.life -= deltaTime;
1184
+
1185
+ if (smoke.life <= 0) {
1186
+ this.scene.remove(smoke.mesh);
1187
+ this.smokeTrail.splice(i, 1);
1188
+ } else {
1189
+ // 연기 확산 및 페이드
1190
+ smoke.mesh.scale.multiplyScalar(1.02);
1191
+ smoke.mesh.material.opacity = smoke.life / 3.0;
1192
+
1193
+ // 약간의 상승 효과
1194
+ smoke.mesh.position.y += deltaTime * 2;
1195
+ }
1196
+ }
1197
+
1198
  // 사운드 볼륨 조정 (플레이어와의 거리에 따라)
1199
  if (this.swingAudio && playerPosition) {
1200
  const distanceToPlayer = this.position.distanceTo(playerPosition);
 
1214
  return 'flying';
1215
  }
1216
 
1217
+ createSmokeParticle() {
1218
+ // 미사일 뒤쪽 위치 계산
1219
+ const backward = this.velocity.clone().normalize().multiplyScalar(-3);
1220
+ const smokePos = this.position.clone().add(backward);
1221
+
1222
+ // 하얀 연기 파티클 생성
1223
+ const smokeGeometry = new THREE.SphereGeometry(
1224
+ 1 + Math.random() * 1.5, // 크기 변화
1225
+ 6,
1226
+ 6
1227
+ );
1228
+ const smokeMaterial = new THREE.MeshBasicMaterial({
1229
+ color: 0xffffff, // 하얀색
1230
+ transparent: true,
1231
+ opacity: 0.8
1232
+ });
1233
+
1234
+ const smoke = new THREE.Mesh(smokeGeometry, smokeMaterial);
1235
+ smoke.position.copy(smokePos);
1236
+
1237
+ // 약간의 랜덤 오프셋
1238
+ smoke.position.x += (Math.random() - 0.5) * 1;
1239
+ smoke.position.y += (Math.random() - 0.5) * 1;
1240
+ smoke.position.z += (Math.random() - 0.5) * 1;
1241
+
1242
+ this.scene.add(smoke);
1243
+
1244
+ this.smokeTrail.push({
1245
+ mesh: smoke,
1246
+ life: 3.0 // 3초 동안 지속
1247
+ });
1248
+
1249
+ // 연기 파티클 제한 (성능을 위해)
1250
+ if (this.smokeTrail.length > 150) {
1251
+ const oldSmoke = this.smokeTrail.shift();
1252
+ this.scene.remove(oldSmoke.mesh);
1253
+ }
1254
+ }
1255
+
1256
  onHit() {
1257
  // 명중 효과
1258
  if (window.gameInstance) {
 
1281
  this.scene.remove(this.mesh);
1282
  }
1283
 
1284
+ // 연기 파티클 정리
1285
+ this.smokeTrail.forEach(smoke => {
1286
+ if (smoke.mesh) {
1287
+ this.scene.remove(smoke.mesh);
1288
+ }
1289
+ });
1290
+ this.smokeTrail = [];
1291
+
1292
  if (this.swingAudio) {
1293
  this.swingAudio.pause();
1294
  this.swingAudio = null;