cutechicken commited on
Commit
dd3a438
ยท
verified ยท
1 Parent(s): 4b16f5f

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +290 -294
game.js CHANGED
@@ -25,293 +25,287 @@ const GAME_CONSTANTS = {
25
 
26
  // ์ „ํˆฌ๊ธฐ ํด๋ž˜์Šค
27
  class Fighter {
28
- constructor() {
29
- this.mesh = null;
30
- this.isLoaded = false;
31
-
32
- // ๋ฌผ๋ฆฌ ์†์„ฑ
33
- this.position = new THREE.Vector3(0, 2000, 0);
34
- this.velocity = new THREE.Vector3(0, 0, 350); // ์ดˆ๊ธฐ ์†๋„ 350kt
35
- this.acceleration = new THREE.Vector3(0, 0, 0);
36
- this.rotation = new THREE.Euler(0, 0, 0);
37
-
38
- // ๋น„ํ–‰ ์ œ์–ด
39
- this.throttle = 0.7; // ์ดˆ๊ธฐ ์Šค๋กœํ‹€ 70%
40
- this.speed = 350; // ์ดˆ๊ธฐ ์†๋„ 350kt
41
- this.altitude = 2000;
42
- this.gForce = 1.0;
43
- this.health = GAME_CONSTANTS.MAX_HEALTH; // ์ฒด๋ ฅ 1000
44
-
45
- // ์กฐ์ข… ์ž…๋ ฅ ์‹œ์Šคํ…œ
46
- this.pitchInput = 0;
47
- this.rollInput = 0;
48
- this.yawInput = 0;
49
-
50
- // ๋งˆ์šฐ์Šค ๋ˆ„์  ์ž…๋ ฅ
51
- this.mousePitch = 0;
52
- this.mouseRoll = 0;
53
-
54
- // ๋ถ€๋“œ๋Ÿฌ์šด ํšŒ์ „์„ ์œ„ํ•œ ๋ชฉํ‘œ๊ฐ’
55
- this.targetPitch = 0;
56
- this.targetRoll = 0;
57
- this.targetYaw = 0;
58
-
59
- // ๋ฌด๊ธฐ
60
- this.missiles = GAME_CONSTANTS.MISSILE_COUNT;
61
- this.ammo = GAME_CONSTANTS.AMMO_COUNT;
62
- this.bullets = [];
63
- this.lastShootTime = 0;
64
- this.isMouseDown = false; // ๋งˆ์šฐ์Šค ๋ˆ„๋ฆ„ ์ƒํƒœ ์ถ”์ 
65
- this.gunfireAudios = []; // ๊ธฐ๊ด€์ด ์†Œ๋ฆฌ ๋ฐฐ์—ด (์ตœ๋Œ€ 5๊ฐœ)
66
-
67
- // ์Šคํ†จ ํƒˆ์ถœ์„ ์œ„ํ•œ Fํ‚ค ์ƒํƒœ
68
- this.escapeKeyPressed = false;
69
- this.stallEscapeProgress = 0; // ์Šคํ†จ ํƒˆ์ถœ ์ง„ํ–‰๋„ (0~2์ดˆ)
70
-
71
- // ์นด๋ฉ”๋ผ ์„ค์ •
72
- this.cameraDistance = 250;
73
- this.cameraHeight = 30;
74
- this.cameraLag = 0.06;
75
-
76
- // ๊ฒฝ๊ณ  ์‹œ์Šคํ…œ
77
- this.altitudeWarning = false;
78
- this.stallWarning = false;
79
- this.warningBlinkTimer = 0;
80
- this.warningBlinkState = false;
81
-
82
- // Over-G ์‹œ์Šคํ…œ
83
- this.overG = false;
84
- this.maxGForce = 9.0;
85
- this.overGTimer = 0; // Over-G ์ง€์† ์‹œ๊ฐ„
86
-
87
- // ๊ฒฝ๊ณ ์Œ ์‹œ์Šคํ…œ
88
- this.warningAudios = {
89
- altitude: null,
90
- pullup: null,
91
- overg: null,
92
- stall: null,
93
- normal: null // ์—”์ง„ ์†Œ๋ฆฌ
94
- };
95
- this.initializeWarningAudios();
96
- }
97
-
98
- // ํ—ค๋”ฉ์„ 0~360๋„๋กœ ์ •๊ทœํ™”ํ•˜๋Š” ํ—ฌํผ ํ•จ์ˆ˜
99
- normalizeHeading(radians) {
100
- let degrees = radians * (180 / Math.PI);
101
- while (degrees < 0) degrees += 360;
102
- while (degrees >= 360) degrees -= 360;
103
- return degrees;
104
- }
105
-
106
- // ๋ผ๋””์•ˆ์„ -ฯ€ ~ ฯ€ ๋ฒ”์œ„๋กœ ์ •๊ทœํ™”
107
- normalizeAngle(angle) {
108
- while (angle > Math.PI) angle -= Math.PI * 2;
109
- while (angle < -Math.PI) angle += Math.PI * 2;
110
- return angle;
111
- }
112
-
113
- initializeWarningAudios() {
114
- try {
115
- this.warningAudios.altitude = new Audio('sounds/altitude.ogg');
116
- this.warningAudios.altitude.volume = 0.75;
117
-
118
- this.warningAudios.pullup = new Audio('sounds/pullup.ogg');
119
- this.warningAudios.pullup.volume = 0.9;
120
-
121
- this.warningAudios.overg = new Audio('sounds/overg.ogg');
122
- this.warningAudios.overg.volume = 0.75;
123
-
124
- this.warningAudios.stall = new Audio('sounds/alert.ogg');
125
- this.warningAudios.stall.volume = 0.75;
126
-
127
- // ์—”์ง„ ์†Œ๋ฆฌ ์„ค์ •
128
- this.warningAudios.normal = new Audio('sounds/normal.ogg');
129
- this.warningAudios.normal.volume = 0.5;
130
- this.warningAudios.normal.loop = true; // ์—”์ง„ ์†Œ๋ฆฌ๋Š” ๊ณ„์† ๋ฐ˜๋ณต
131
-
132
- // ๊ฒฝ๊ณ ์Œ์—๋งŒ ended ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ถ”๊ฐ€ (์—”์ง„ ์†Œ๋ฆฌ ์ œ์™ธ)
133
- Object.keys(this.warningAudios).forEach(key => {
134
- if (key !== 'normal' && this.warningAudios[key]) {
135
- this.warningAudios[key].addEventListener('ended', () => {
136
- this.updateWarningAudios();
137
- });
138
- }
139
- });
140
- } catch (e) {
141
- console.log('Warning audio initialization failed:', e);
142
- }
143
- }
144
-
145
- startEngineSound() {
146
- // ์—”์ง„ ์†Œ๋ฆฌ ์‹œ์ž‘
147
- if (this.warningAudios.normal) {
148
- this.warningAudios.normal.play().catch(e => {
149
- console.log('Engine sound failed to start:', e);
150
- });
151
- }
152
- }
153
-
154
- updateWarningAudios() {
155
- let currentWarning = null;
156
-
157
- if (this.altitude < 250) {
158
- currentWarning = 'pullup';
159
- }
160
- else if (this.altitude < 500) {
161
- currentWarning = 'altitude';
162
- }
163
- else if (this.overG) {
164
- currentWarning = 'overg';
165
- }
166
- else if (this.stallWarning) {
167
- currentWarning = 'stall';
168
- }
169
-
170
- // ๊ฒฝ๊ณ ์Œ๋งŒ ๊ด€๋ฆฌ (์—”์ง„ ์†Œ๋ฆฌ๋Š” ์ œ์™ธ)
171
- Object.keys(this.warningAudios).forEach(key => {
172
- if (key !== 'normal' && key !== currentWarning && this.warningAudios[key] && !this.warningAudios[key].paused) {
173
- this.warningAudios[key].pause();
174
- this.warningAudios[key].currentTime = 0;
175
- }
176
- });
177
-
178
- if (currentWarning && this.warningAudios[currentWarning]) {
179
- if (this.warningAudios[currentWarning].paused) {
180
- this.warningAudios[currentWarning].play().catch(e => {});
181
- }
182
- }
183
- }
184
-
185
- stopAllWarningAudios() {
186
- // ๋ชจ๋“  ์˜ค๋””์˜ค ์ •์ง€ (์—”์ง„ ์†Œ๋ฆฌ ํฌํ•จ)
187
- Object.values(this.warningAudios).forEach(audio => {
188
- if (audio && !audio.paused) {
189
- audio.pause();
190
- audio.currentTime = 0;
191
- }
192
- });
193
- }
194
 
195
- async initialize(scene, loader) {
196
- try {
197
- const result = await loader.loadAsync('models/f-15.glb');
198
- this.mesh = result.scene;
199
- this.mesh.position.copy(this.position);
200
- this.mesh.scale.set(2, 2, 2);
201
- this.mesh.rotation.y = Math.PI / 4;
202
-
203
- this.mesh.traverse((child) => {
204
- if (child.isMesh) {
205
- child.castShadow = true;
206
- child.receiveShadow = true;
207
- }
208
- });
209
-
210
- scene.add(this.mesh);
211
- this.isLoaded = true;
212
- console.log('F-15 ์ „ํˆฌ๊ธฐ ๋กœ๋”ฉ ์™„๋ฃŒ');
213
- } catch (error) {
214
- console.error('F-15 ๋ชจ๋ธ ๋กœ๋”ฉ ์‹คํŒจ:', error);
215
- this.createFallbackModel(scene);
216
- }
217
- }
218
 
219
- createFallbackModel(scene) {
220
- const group = new THREE.Group();
221
-
222
- const fuselageGeometry = new THREE.CylinderGeometry(0.8, 1.5, 12, 8);
223
- const fuselageMaterial = new THREE.MeshLambertMaterial({ color: 0x606060 });
224
- const fuselage = new THREE.Mesh(fuselageGeometry, fuselageMaterial);
225
- fuselage.rotation.x = Math.PI / 2;
226
- group.add(fuselage);
227
-
228
- const wingGeometry = new THREE.BoxGeometry(16, 0.3, 4);
229
- const wingMaterial = new THREE.MeshLambertMaterial({ color: 0x404040 });
230
- const wings = new THREE.Mesh(wingGeometry, wingMaterial);
231
- wings.position.z = -1;
232
- group.add(wings);
233
-
234
- const tailGeometry = new THREE.BoxGeometry(0.3, 4, 3);
235
- const tailMaterial = new THREE.MeshLambertMaterial({ color: 0x404040 });
236
- const tail = new THREE.Mesh(tailGeometry, tailMaterial);
237
- tail.position.z = -5;
238
- tail.position.y = 1.5;
239
- group.add(tail);
240
-
241
- const horizontalTailGeometry = new THREE.BoxGeometry(6, 0.2, 2);
242
- const horizontalTail = new THREE.Mesh(horizontalTailGeometry, tailMaterial);
243
- horizontalTail.position.z = -5;
244
- horizontalTail.position.y = 0.5;
245
- group.add(horizontalTail);
246
-
247
- this.mesh = group;
248
- this.mesh.position.copy(this.position);
249
- this.mesh.scale.set(2, 2, 2);
250
- scene.add(this.mesh);
251
- this.isLoaded = true;
252
-
253
- console.log('Fallback ์ „ํˆฌ๊ธฐ ๋ชจ๋ธ ์ƒ์„ฑ ์™„๋ฃŒ');
254
- }
255
 
256
- updateMouseInput(deltaX, deltaY) {
257
- // Over-G ์ƒํƒœ์—์„œ ์Šคํ†จ์ด ํ•ด์ œ๋˜์ง€ ์•Š์•˜์œผ๋ฉด ํ”ผ์น˜ ์กฐ์ž‘ ๋ถˆ๊ฐ€
258
- if (this.overG && this.overGTimer > 1.0 && this.stallWarning) {
259
- // ์š”(Yaw)๋งŒ ์ œํ•œ์ ์œผ๋กœ ํ—ˆ์šฉ
260
- const sensitivity = GAME_CONSTANTS.MOUSE_SENSITIVITY * 0.3; // ๊ฐ๋„ ๋Œ€ํญ ๊ฐ์†Œ
261
- this.targetYaw += deltaX * sensitivity * 0.3;
262
-
263
- // ํ”ผ์น˜๋Š” ์กฐ์ž‘ ๋ถˆ๊ฐ€
264
- // ๋กค๋„ ์ œํ•œ์ ์œผ๋กœ๋งŒ ํ—ˆ์šฉ
265
- const yawRate = deltaX * sensitivity * 0.3;
266
- this.targetRoll = -yawRate * 5; // ๋งค์šฐ ์ œํ•œ๋œ ๋กค
267
-
268
- return; // ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ ์ค‘๋‹จ
269
- }
270
-
271
- const sensitivity = GAME_CONSTANTS.MOUSE_SENSITIVITY * 1.0;
272
-
273
- // ๋งˆ์šฐ์Šค Y์ถ•: ํ”ผ์น˜(๊ธฐ์ˆ˜ ์ƒํ•˜) - ํ•ญ์ƒ ๋™์ผํ•˜๊ฒŒ ์ž‘๋™
274
- this.targetPitch -= deltaY * sensitivity;
275
-
276
- // ๋งˆ์šฐ์Šค X์ถ•: ์š”(์ขŒ์šฐ ํšŒ์ „) - ํ•ญ์ƒ ๋™์ผํ•˜๊ฒŒ ์ž‘๋™
277
- this.targetYaw += deltaX * sensitivity * 0.8;
278
-
279
- // ์š” ํšŒ์ „์— ๋”ฐ๋ฅธ ์ž๋™ ๋กค (๋ฑ…ํฌ ํ„ด)
280
- // ์ขŒ์šฐ๋กœ ํšŒ์ „ํ•  ๋•Œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋‚ ๊ฐœ๊ฐ€ ๊ธฐ์šธ์–ด์ง
281
- const yawRate = deltaX * sensitivity * 0.8;
282
- this.targetRoll = -yawRate * 15; // ์š” ํšŒ์ „๋Ÿ‰์— ๋น„๋ก€ํ•˜์—ฌ ๋กค ๋ฐœ์ƒ
283
-
284
- // ๊ฐ๋„ ์ œํ•œ
285
- const maxPitchAngle = Math.PI / 3; // 60๋„
286
- const maxRollAngle = Math.PI * 0.5; // 90๋„๋กœ ์ œํ•œ (์ž๋™ ๋กค)
287
-
288
- this.targetPitch = Math.max(-maxPitchAngle, Math.min(maxPitchAngle, this.targetPitch));
289
-
290
- // ์ž๋™ ๋กค์€ ์ œํ•œ๋œ ๋ฒ”์œ„ ๋‚ด์—์„œ๋งŒ ์ž‘๋™
291
- if (Math.abs(this.targetRoll) < maxRollAngle) {
292
- // ๋กค ๊ฐ๋„ ์ œํ•œ ์ ์šฉ
293
- this.targetRoll = Math.max(-maxRollAngle, Math.min(maxRollAngle, this.targetRoll));
294
- }
295
- }
296
 
297
- updateControls(keys, deltaTime) {
298
- // W/S: ์Šค๋กœํ‹€๋งŒ ์ œ์–ด (๊ฐ€์†/๊ฐ์†)
299
- if (keys.w) {
300
- this.throttle = Math.min(1.0, this.throttle + deltaTime * 0.5);
301
- }
302
- if (keys.s) {
303
- this.throttle = Math.max(0.1, this.throttle - deltaTime * 0.5);
304
- }
305
-
306
- // A/D: ๋ณด์กฐ ์š” ์ œ์–ด (๋Ÿฌ๋”) - ๋ฐ˜์‘์„ฑ ๊ฐœ์„ 
307
- if (keys.a) {
308
- this.targetYaw -= deltaTime * 1.2;
309
- }
310
- if (keys.d) {
311
- this.targetYaw += deltaTime * 1.2;
312
- }
313
- }
314
- updatePhysics(deltaTime) {
 
315
  if (!this.mesh) return;
316
 
317
  // ๋ถ€๋“œ๋Ÿฌ์šด ํšŒ์ „ ๋ณด๊ฐ„
@@ -529,7 +523,8 @@ class Fighter {
529
  const gravityAcceleration = GAME_CONSTANTS.GRAVITY * deltaTime * 3.0; // 3๋ฐฐ ์ค‘๋ ฅ
530
  this.velocity.y -= gravityAcceleration;
531
  }
532
- // ์†๋„ ๋ฒกํ„ฐ ๊ณ„์‚ฐ - ์ฟผํ„ฐ๋‹ˆ์–ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ง๋ฒŒ ๋ฝ ์™„์ „ ํšŒํ”ผ
 
533
  const noseDirection = new THREE.Vector3(0, 0, 1);
534
 
535
  // ์ฟผํ„ฐ๋‹ˆ์–ธ ๊ธฐ๋ฐ˜ ํšŒ์ „ ์‹œ์Šคํ…œ
@@ -550,10 +545,11 @@ class Fighter {
550
  yawQuat.setFromAxisAngle(yAxis, this.rotation.y);
551
  rollQuat.setFromAxisAngle(zAxis, this.rotation.z);
552
 
553
- // ์ฟผํ„ฐ๋‹ˆ์–ธ ํ•ฉ์„ฑ (ํ•ญ๊ณต๊ธฐ ํ‘œ์ค€ ์ˆœ์„œ: Yaw -> Pitch -> Roll)
554
- quaternion.multiply(yawQuat);
555
- quaternion.multiply(pitchQuat);
556
  quaternion.multiply(rollQuat);
 
 
557
 
558
  // ๋ฐฉํ–ฅ ๋ฒกํ„ฐ์— ํšŒ์ „ ์ ์šฉ
559
  noseDirection.applyQuaternion(quaternion);
@@ -626,10 +622,10 @@ class Fighter {
626
  meshYawQuat.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.rotation.y + 3 * Math.PI / 2);
627
  meshRollQuat.setFromAxisAngle(new THREE.Vector3(0, 0, 1), this.rotation.z);
628
 
629
- // ์ฟผํ„ฐ๋‹ˆ์–ธ ํ•ฉ์„ฑ
630
- meshQuaternion.multiply(meshYawQuat);
631
- meshQuaternion.multiply(meshPitchQuat);
632
  meshQuaternion.multiply(meshRollQuat);
 
 
633
 
634
  this.mesh.quaternion.copy(meshQuaternion);
635
 
@@ -680,9 +676,9 @@ class Fighter {
680
  yawQuat.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.rotation.y);
681
  rollQuat.setFromAxisAngle(new THREE.Vector3(0, 0, 1), this.rotation.z);
682
 
683
- quaternion.multiply(yawQuat);
684
- quaternion.multiply(pitchQuat);
685
  quaternion.multiply(rollQuat);
 
 
686
 
687
  muzzleOffset.applyQuaternion(quaternion);
688
  bullet.position.copy(this.position).add(muzzleOffset);
@@ -772,9 +768,9 @@ class Fighter {
772
  yawQuat.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.rotation.y);
773
  rollQuat.setFromAxisAngle(new THREE.Vector3(0, 0, 1), this.rotation.z);
774
 
775
- quaternion.multiply(yawQuat);
776
- quaternion.multiply(pitchQuat);
777
  quaternion.multiply(rollQuat);
 
 
778
 
779
  backward.applyQuaternion(quaternion);
780
  up.applyQuaternion(quaternion);
 
25
 
26
  // ์ „ํˆฌ๊ธฐ ํด๋ž˜์Šค
27
  class Fighter {
28
+ constructor() {
29
+ this.mesh = null;
30
+ this.isLoaded = false;
31
+
32
+ // ๋ฌผ๋ฆฌ ์†์„ฑ
33
+ this.position = new THREE.Vector3(0, 2000, 0);
34
+ this.velocity = new THREE.Vector3(0, 0, 350); // ์ดˆ๊ธฐ ์†๋„ 350kt
35
+ this.acceleration = new THREE.Vector3(0, 0, 0);
36
+ this.rotation = new THREE.Euler(0, 0, 0, 'YXZ'); // ํ•ญ๊ณต๊ธฐ ํ‘œ์ค€ ์˜ค์ผ๋Ÿฌ ์ˆœ์„œ
37
+
38
+ // ๋น„ํ–‰ ์ œ์–ด
39
+ this.throttle = 0.7; // ์ดˆ๊ธฐ ์Šค๋กœํ‹€ 70%
40
+ this.speed = 350; // ์ดˆ๊ธฐ ์†๋„ 350kt
41
+ this.altitude = 2000;
42
+ this.gForce = 1.0;
43
+ this.health = GAME_CONSTANTS.MAX_HEALTH; // ์ฒด๋ ฅ 1000
44
+
45
+ // ์กฐ์ข… ์ž…๋ ฅ ์‹œ์Šคํ…œ
46
+ this.pitchInput = 0;
47
+ this.rollInput = 0;
48
+ this.yawInput = 0;
49
+
50
+ // ๋งˆ์šฐ์Šค ๋ˆ„์  ์ž…๋ ฅ
51
+ this.mousePitch = 0;
52
+ this.mouseRoll = 0;
53
+
54
+ // ๋ถ€๋“œ๋Ÿฌ์šด ํšŒ์ „์„ ์œ„ํ•œ ๋ชฉํ‘œ๊ฐ’
55
+ this.targetPitch = 0;
56
+ this.targetRoll = 0;
57
+ this.targetYaw = 0;
58
+
59
+ // ๋ฌด๊ธฐ
60
+ this.missiles = GAME_CONSTANTS.MISSILE_COUNT;
61
+ this.ammo = GAME_CONSTANTS.AMMO_COUNT;
62
+ this.bullets = [];
63
+ this.lastShootTime = 0;
64
+ this.isMouseDown = false; // ๋งˆ์šฐ์Šค ๋ˆ„๋ฆ„ ์ƒํƒœ ์ถ”์ 
65
+ this.gunfireAudios = []; // ๊ธฐ๊ด€์ด ์†Œ๋ฆฌ ๋ฐฐ์—ด (์ตœ๋Œ€ 5๊ฐœ)
66
+
67
+ // ์Šคํ†จ ํƒˆ์ถœ์„ ์œ„ํ•œ Fํ‚ค ์ƒํƒœ
68
+ this.escapeKeyPressed = false;
69
+ this.stallEscapeProgress = 0; // ์Šคํ†จ ํƒˆ์ถœ ์ง„ํ–‰๋„ (0~2์ดˆ)
70
+
71
+ // ์นด๋ฉ”๋ผ ์„ค์ •
72
+ this.cameraDistance = 250;
73
+ this.cameraHeight = 30;
74
+ this.cameraLag = 0.06;
75
+
76
+ // ๊ฒฝ๊ณ  ์‹œ์Šคํ…œ
77
+ this.altitudeWarning = false;
78
+ this.stallWarning = false;
79
+ this.warningBlinkTimer = 0;
80
+ this.warningBlinkState = false;
81
+
82
+ // Over-G ์‹œ์Šคํ…œ
83
+ this.overG = false;
84
+ this.maxGForce = 9.0;
85
+ this.overGTimer = 0; // Over-G ์ง€์† ์‹œ๊ฐ„
86
+
87
+ // ๊ฒฝ๊ณ ์Œ ์‹œ์Šคํ…œ
88
+ this.warningAudios = {
89
+ altitude: null,
90
+ pullup: null,
91
+ overg: null,
92
+ stall: null,
93
+ normal: null // ์—”์ง„ ์†Œ๋ฆฌ
94
+ };
95
+ this.initializeWarningAudios();
96
+ }
97
+
98
+ // ํ—ค๋”ฉ์„ 0~360๋„๋กœ ์ •๊ทœํ™”ํ•˜๋Š” ํ—ฌํผ ํ•จ์ˆ˜
99
+ normalizeHeading(radians) {
100
+ let degrees = radians * (180 / Math.PI);
101
+ while (degrees < 0) degrees += 360;
102
+ while (degrees >= 360) degrees -= 360;
103
+ return degrees;
104
+ }
105
+
106
+ // ๋ผ๋””์•ˆ์„ -ฯ€ ~ ฯ€ ๋ฒ”์œ„๋กœ ์ •๊ทœํ™”
107
+ normalizeAngle(angle) {
108
+ while (angle > Math.PI) angle -= Math.PI * 2;
109
+ while (angle < -Math.PI) angle += Math.PI * 2;
110
+ return angle;
111
+ }
112
+
113
+ initializeWarningAudios() {
114
+ try {
115
+ this.warningAudios.altitude = new Audio('sounds/altitude.ogg');
116
+ this.warningAudios.altitude.volume = 0.75;
117
+
118
+ this.warningAudios.pullup = new Audio('sounds/pullup.ogg');
119
+ this.warningAudios.pullup.volume = 0.9;
120
+
121
+ this.warningAudios.overg = new Audio('sounds/overg.ogg');
122
+ this.warningAudios.overg.volume = 0.75;
123
+
124
+ this.warningAudios.stall = new Audio('sounds/alert.ogg');
125
+ this.warningAudios.stall.volume = 0.75;
126
+
127
+ // ์—”์ง„ ์†Œ๋ฆฌ ์„ค์ •
128
+ this.warningAudios.normal = new Audio('sounds/normal.ogg');
129
+ this.warningAudios.normal.volume = 0.5;
130
+ this.warningAudios.normal.loop = true; // ์—”์ง„ ์†Œ๋ฆฌ๋Š” ๊ณ„์† ๋ฐ˜๋ณต
131
+
132
+ // ๊ฒฝ๊ณ ์Œ์—๋งŒ ended ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ถ”๊ฐ€ (์—”์ง„ ์†Œ๋ฆฌ ์ œ์™ธ)
133
+ Object.keys(this.warningAudios).forEach(key => {
134
+ if (key !== 'normal' && this.warningAudios[key]) {
135
+ this.warningAudios[key].addEventListener('ended', () => {
136
+ this.updateWarningAudios();
137
+ });
138
+ }
139
+ });
140
+ } catch (e) {
141
+ console.log('Warning audio initialization failed:', e);
142
+ }
143
+ }
144
+
145
+ startEngineSound() {
146
+ // ์—”์ง„ ์†Œ๋ฆฌ ์‹œ์ž‘
147
+ if (this.warningAudios.normal) {
148
+ this.warningAudios.normal.play().catch(e => {
149
+ console.log('Engine sound failed to start:', e);
150
+ });
151
+ }
152
+ }
153
+
154
+ updateWarningAudios() {
155
+ let currentWarning = null;
156
+
157
+ if (this.altitude < 250) {
158
+ currentWarning = 'pullup';
159
+ }
160
+ else if (this.altitude < 500) {
161
+ currentWarning = 'altitude';
162
+ }
163
+ else if (this.overG) {
164
+ currentWarning = 'overg';
165
+ }
166
+ else if (this.stallWarning) {
167
+ currentWarning = 'stall';
168
+ }
169
+
170
+ // ๊ฒฝ๊ณ ์Œ๋งŒ ๊ด€๋ฆฌ (์—”์ง„ ์†Œ๋ฆฌ๋Š” ์ œ์™ธ)
171
+ Object.keys(this.warningAudios).forEach(key => {
172
+ if (key !== 'normal' && key !== currentWarning && this.warningAudios[key] && !this.warningAudios[key].paused) {
173
+ this.warningAudios[key].pause();
174
+ this.warningAudios[key].currentTime = 0;
175
+ }
176
+ });
177
+
178
+ if (currentWarning && this.warningAudios[currentWarning]) {
179
+ if (this.warningAudios[currentWarning].paused) {
180
+ this.warningAudios[currentWarning].play().catch(e => {});
181
+ }
182
+ }
183
+ }
184
+
185
+ stopAllWarningAudios() {
186
+ // ๋ชจ๋“  ์˜ค๋””์˜ค ์ •์ง€ (์—”์ง„ ์†Œ๋ฆฌ ํฌํ•จ)
187
+ Object.values(this.warningAudios).forEach(audio => {
188
+ if (audio && !audio.paused) {
189
+ audio.pause();
190
+ audio.currentTime = 0;
191
+ }
192
+ });
193
+ }
194
 
195
+ async initialize(scene, loader) {
196
+ try {
197
+ const result = await loader.loadAsync('models/f-15.glb');
198
+ this.mesh = result.scene;
199
+ this.mesh.position.copy(this.position);
200
+ this.mesh.scale.set(2, 2, 2);
201
+ this.mesh.rotation.y = Math.PI / 4;
202
+
203
+ this.mesh.traverse((child) => {
204
+ if (child.isMesh) {
205
+ child.castShadow = true;
206
+ child.receiveShadow = true;
207
+ }
208
+ });
209
+
210
+ scene.add(this.mesh);
211
+ this.isLoaded = true;
212
+ console.log('F-15 ์ „ํˆฌ๊ธฐ ๋กœ๋”ฉ ์™„๋ฃŒ');
213
+ } catch (error) {
214
+ console.error('F-15 ๋ชจ๋ธ ๋กœ๋”ฉ ์‹คํŒจ:', error);
215
+ this.createFallbackModel(scene);
216
+ }
217
+ }
218
 
219
+ createFallbackModel(scene) {
220
+ const group = new THREE.Group();
221
+
222
+ const fuselageGeometry = new THREE.CylinderGeometry(0.8, 1.5, 12, 8);
223
+ const fuselageMaterial = new THREE.MeshLambertMaterial({ color: 0x606060 });
224
+ const fuselage = new THREE.Mesh(fuselageGeometry, fuselageMaterial);
225
+ fuselage.rotation.x = Math.PI / 2;
226
+ group.add(fuselage);
227
+
228
+ const wingGeometry = new THREE.BoxGeometry(16, 0.3, 4);
229
+ const wingMaterial = new THREE.MeshLambertMaterial({ color: 0x404040 });
230
+ const wings = new THREE.Mesh(wingGeometry, wingMaterial);
231
+ wings.position.z = -1;
232
+ group.add(wings);
233
+
234
+ const tailGeometry = new THREE.BoxGeometry(0.3, 4, 3);
235
+ const tailMaterial = new THREE.MeshLambertMaterial({ color: 0x404040 });
236
+ const tail = new THREE.Mesh(tailGeometry, tailMaterial);
237
+ tail.position.z = -5;
238
+ tail.position.y = 1.5;
239
+ group.add(tail);
240
+
241
+ const horizontalTailGeometry = new THREE.BoxGeometry(6, 0.2, 2);
242
+ const horizontalTail = new THREE.Mesh(horizontalTailGeometry, tailMaterial);
243
+ horizontalTail.position.z = -5;
244
+ horizontalTail.position.y = 0.5;
245
+ group.add(horizontalTail);
246
+
247
+ this.mesh = group;
248
+ this.mesh.position.copy(this.position);
249
+ this.mesh.scale.set(2, 2, 2);
250
+ scene.add(this.mesh);
251
+ this.isLoaded = true;
252
+
253
+ console.log('Fallback ์ „ํˆฌ๊ธฐ ๋ชจ๋ธ ์ƒ์„ฑ ์™„๋ฃŒ');
254
+ }
255
 
256
+ updateMouseInput(deltaX, deltaY) {
257
+ // Over-G ์ƒํƒœ์—์„œ ์Šคํ†จ์ด ํ•ด์ œ๋˜์ง€ ์•Š์•˜์œผ๋ฉด ํ”ผ์น˜ ์กฐ์ž‘ ๋ถˆ๊ฐ€
258
+ if (this.overG && this.overGTimer > 1.0 && this.stallWarning) {
259
+ // ์š”(Yaw)๋งŒ ์ œํ•œ์ ์œผ๋กœ ํ—ˆ์šฉ
260
+ const sensitivity = GAME_CONSTANTS.MOUSE_SENSITIVITY * 0.3;
261
+ this.targetYaw += deltaX * sensitivity * 0.3;
262
+ return;
263
+ }
264
+
265
+ const sensitivity = GAME_CONSTANTS.MOUSE_SENSITIVITY * 1.0;
266
+
267
+ // ๋งˆ์šฐ์Šค ์ž…๋ ฅ์„ ๊ฐ์†๋„๋กœ ๋ณ€ํ™˜
268
+ const pitchRate = -deltaY * sensitivity;
269
+ const yawRate = deltaX * sensitivity * 0.8;
270
+
271
+ // ํ˜„์žฌ ์ƒํƒœ์—์„œ์˜ ํ”ผ์น˜ ์ œํ•œ
272
+ this.targetPitch += pitchRate;
273
+ this.targetYaw += yawRate;
274
+
275
+ // ์š” ํšŒ์ „์— ๋”ฐ๋ฅธ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ฑ…ํฌ (๋กค)
276
+ // ์‹ค์ œ ํ•ญ๊ณต๊ธฐ์ฒ˜๋Ÿผ ์„ ํšŒ ์‹œ ์ž๋™์œผ๋กœ ๊ธฐ์šธ์–ด์ง
277
+ const targetBankAngle = -yawRate * 15; // ์š” ์†๋„์— ๋น„๋ก€ํ•œ ๋ฑ…ํฌ ๊ฐ๋„
278
+
279
+ // ๋กค์„ ๋ชฉํ‘œ ๋ฑ…ํฌ ๊ฐ๋„๋กœ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ „ํ™˜
280
+ this.targetRoll = THREE.MathUtils.lerp(this.targetRoll, targetBankAngle, 0.1);
281
+
282
+ // ๊ฐ๋„ ์ œํ•œ
283
+ const maxPitchAngle = Math.PI / 3; // 60๋„
284
+ const maxRollAngle = Math.PI * 0.5; // 90๋„
285
+
286
+ this.targetPitch = Math.max(-maxPitchAngle, Math.min(maxPitchAngle, this.targetPitch));
287
+ this.targetRoll = Math.max(-maxRollAngle, Math.min(maxRollAngle, this.targetRoll));
288
+ }
 
 
 
 
 
 
 
289
 
290
+ updateControls(keys, deltaTime) {
291
+ // W/S: ์Šค๋กœํ‹€๋งŒ ์ œ์–ด (๊ฐ€์†/๊ฐ์†)
292
+ if (keys.w) {
293
+ this.throttle = Math.min(1.0, this.throttle + deltaTime * 0.5);
294
+ }
295
+ if (keys.s) {
296
+ this.throttle = Math.max(0.1, this.throttle - deltaTime * 0.5);
297
+ }
298
+
299
+ // A/D: ๋ณด์กฐ ์š” ์ œ์–ด (๋Ÿฌ๋”) - ๋ฐ˜์‘์„ฑ ๊ฐœ์„ 
300
+ if (keys.a) {
301
+ this.targetYaw -= deltaTime * 1.2;
302
+ }
303
+ if (keys.d) {
304
+ this.targetYaw += deltaTime * 1.2;
305
+ }
306
+ }
307
+
308
+ updatePhysics(deltaTime) {
309
  if (!this.mesh) return;
310
 
311
  // ๋ถ€๋“œ๋Ÿฌ์šด ํšŒ์ „ ๋ณด๊ฐ„
 
523
  const gravityAcceleration = GAME_CONSTANTS.GRAVITY * deltaTime * 3.0; // 3๋ฐฐ ์ค‘๋ ฅ
524
  this.velocity.y -= gravityAcceleration;
525
  }
526
+
527
+ // ์†๋„ ๋ฒกํ„ฐ ๊ณ„์‚ฐ - ์ฟผํ„ฐ๋‹ˆ์–ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ง๋ฒŒ ๋ฝ ์™„์ „ ํšŒํ”ผ
528
  const noseDirection = new THREE.Vector3(0, 0, 1);
529
 
530
  // ์ฟผํ„ฐ๋‹ˆ์–ธ ๊ธฐ๋ฐ˜ ํšŒ์ „ ์‹œ์Šคํ…œ
 
545
  yawQuat.setFromAxisAngle(yAxis, this.rotation.y);
546
  rollQuat.setFromAxisAngle(zAxis, this.rotation.z);
547
 
548
+ // ์ฟผํ„ฐ๋‹ˆ์–ธ ํ•ฉ์„ฑ (์˜ฌ๋ฐ”๋ฅธ ํ•ญ๊ณต๊ธฐ ์ˆœ์„œ: Roll -> Pitch -> Yaw)
549
+ // ํ•ญ๊ณต๊ธฐ๋Š” ๋จผ์ € ๋กค, ๊ทธ ๋‹ค์Œ ํ”ผ์น˜, ๋งˆ์ง€๋ง‰์œผ๋กœ ์š” ์ˆœ์„œ๋กœ ํšŒ์ „
 
550
  quaternion.multiply(rollQuat);
551
+ quaternion.multiply(pitchQuat);
552
+ quaternion.multiply(yawQuat);
553
 
554
  // ๋ฐฉํ–ฅ ๋ฒกํ„ฐ์— ํšŒ์ „ ์ ์šฉ
555
  noseDirection.applyQuaternion(quaternion);
 
622
  meshYawQuat.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.rotation.y + 3 * Math.PI / 2);
623
  meshRollQuat.setFromAxisAngle(new THREE.Vector3(0, 0, 1), this.rotation.z);
624
 
625
+ // ์ฟผํ„ฐ๋‹ˆ์–ธ ํ•ฉ์„ฑ (์˜ฌ๋ฐ”๋ฅธ ์ˆœ์„œ)
 
 
626
  meshQuaternion.multiply(meshRollQuat);
627
+ meshQuaternion.multiply(meshPitchQuat);
628
+ meshQuaternion.multiply(meshYawQuat);
629
 
630
  this.mesh.quaternion.copy(meshQuaternion);
631
 
 
676
  yawQuat.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.rotation.y);
677
  rollQuat.setFromAxisAngle(new THREE.Vector3(0, 0, 1), this.rotation.z);
678
 
 
 
679
  quaternion.multiply(rollQuat);
680
+ quaternion.multiply(pitchQuat);
681
+ quaternion.multiply(yawQuat);
682
 
683
  muzzleOffset.applyQuaternion(quaternion);
684
  bullet.position.copy(this.position).add(muzzleOffset);
 
768
  yawQuat.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.rotation.y);
769
  rollQuat.setFromAxisAngle(new THREE.Vector3(0, 0, 1), this.rotation.z);
770
 
 
 
771
  quaternion.multiply(rollQuat);
772
+ quaternion.multiply(pitchQuat);
773
+ quaternion.multiply(yawQuat);
774
 
775
  backward.applyQuaternion(quaternion);
776
  up.applyQuaternion(quaternion);