gunship999 commited on
Commit
08e0a03
โ€ข
1 Parent(s): 89db675

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +94 -322
game.js CHANGED
@@ -5,9 +5,11 @@ import { PointerLockControls } from 'three/addons/controls/PointerLockControls.j
5
  // ๊ฒŒ์ž„ ์ƒ์ˆ˜
6
  const GAME_DURATION = 180;
7
  const MAP_SIZE = 2000;
8
- const HELICOPTER_HEIGHT = 50;
 
9
  const ENEMY_SCALE = 3;
10
  const MAX_HEALTH = 1000;
 
11
  const ENEMY_MODELS = [
12
  './models/enemy1.glb',
13
  './models/enemy12.glb',
@@ -46,18 +48,13 @@ class SoundPool {
46
 
47
  play() {
48
  const sound = this.sounds[this.currentIndex];
49
-
50
- // ํ˜„์žฌ ์žฌ์ƒ ์ค‘์ธ ์‚ฌ์šด๋“œ ์ดˆ๊ธฐํ™”
51
  sound.pause();
52
  sound.currentTime = 0;
53
 
54
- // ์ƒˆ๋กœ์šด Promise๋กœ ์‚ฌ์šด๋“œ ์žฌ์ƒ
55
  const playPromise = sound.play();
56
-
57
  if (playPromise !== undefined) {
58
  playPromise.catch(error => {
59
  console.error("Sound play error:", error);
60
- // ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์ƒˆ๋กœ์šด Audio ๊ฐ์ฒด๋กœ ๊ต์ฒด
61
  this.sounds[this.currentIndex] = new Audio(sound.src);
62
  });
63
  }
@@ -69,7 +66,8 @@ class SoundPool {
69
  // ์‚ฌ์šด๋“œ ์ดˆ๊ธฐํ™”
70
  const sounds = {
71
  bgm: new Audio('Music.wav'),
72
- gunshot: new SoundPool('gun.wav', 20) // ํ’€ ํฌ๊ธฐ๋ฅผ 20์œผ๋กœ ์ฆ๊ฐ€
 
73
  };
74
  sounds.bgm.loop = true;
75
 
@@ -85,22 +83,18 @@ function init() {
85
  console.log('Game initialized');
86
  console.log('Available enemy models:', ENEMY_MODELS);
87
 
88
- // Scene ์ดˆ๊ธฐํ™”
89
  scene = new THREE.Scene();
90
  scene.background = new THREE.Color(0x87ceeb);
91
  scene.fog = new THREE.Fog(0x87ceeb, 0, 1500);
92
 
93
- // Camera ์„ค์ •
94
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 3000);
95
  camera.position.set(0, HELICOPTER_HEIGHT, 0);
96
 
97
- // Renderer ์„ค์ •
98
  renderer = new THREE.WebGLRenderer({ antialias: true });
99
  renderer.setSize(window.innerWidth, window.innerHeight);
100
  renderer.shadowMap.enabled = true;
101
  document.body.appendChild(renderer.domElement);
102
 
103
- // ์กฐ๋ช… ์„ค์ •
104
  const ambientLight = new THREE.AmbientLight(0xffffff, 1.0);
105
  scene.add(ambientLight);
106
 
@@ -109,29 +103,65 @@ function init() {
109
  dirLight.castShadow = true;
110
  scene.add(dirLight);
111
 
112
- // Controls ์„ค์ •
113
  controls = new PointerLockControls(camera, document.body);
114
 
115
- // ๋””๋ฒ„๊ทธ ํ—ฌํผ ์ถ”๊ฐ€
116
- const axesHelper = new THREE.AxesHelper(5);
117
- scene.add(axesHelper);
118
-
119
- const gridHelper = new THREE.GridHelper(1000, 100);
120
- scene.add(gridHelper);
121
-
122
- // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์„ค์ •
123
  document.addEventListener('click', onClick);
124
  document.addEventListener('keydown', onKeyDown);
125
  document.addEventListener('keyup', onKeyUp);
126
  window.addEventListener('resize', onWindowResize);
127
 
128
- // ์ง€ํ˜• ์ƒ์„ฑ
129
  createTerrain();
130
-
131
  console.log('Starting to load enemies...');
132
  loadEnemies();
133
  }
134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  function createTerrain() {
136
  const geometry = new THREE.PlaneGeometry(MAP_SIZE, MAP_SIZE, 200, 200);
137
  const material = new THREE.MeshStandardMaterial({
@@ -173,15 +203,13 @@ function loadEnemies() {
173
  const radius = 200;
174
  const position = new THREE.Vector3(
175
  Math.cos(angle) * radius,
176
- 10,
177
  Math.sin(angle) * radius
178
  );
179
 
180
- // ์ž„์‹œ ์  ์ƒ์„ฑ
181
  const tempEnemy = createTemporaryEnemy(position);
182
  scene.add(tempEnemy);
183
 
184
- // GLB ๋ชจ๋ธ ๋กœ๋“œ
185
  const modelPath = ENEMY_MODELS[i % ENEMY_MODELS.length];
186
  console.log('Loading enemy model:', modelPath);
187
 
@@ -193,7 +221,6 @@ function loadEnemies() {
193
  enemy.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
194
  enemy.position.copy(position);
195
 
196
- // ๋ชจ๋ธ ๋ฐฉํ–ฅ ์„ค์ •
197
  enemy.rotation.y = Math.PI;
198
 
199
  enemy.traverse((node) => {
@@ -205,15 +232,12 @@ function loadEnemies() {
205
  }
206
  });
207
 
208
- // ์ž„์‹œ ์ ์„ ์‹ค์ œ ๋ชจ๋ธ๋กœ ๊ต์ฒด
209
  scene.remove(tempEnemy);
210
  scene.add(enemy);
211
 
212
- // enemies ๋ฐฐ์—ด ์—…๋ฐ์ดํŠธ
213
  const index = enemies.findIndex(e => e.model === tempEnemy);
214
  if (index !== -1) {
215
  enemies[index].model = enemy;
216
- // ์  ๊ณต๊ฒฉ ํšจ๊ณผ ์ถ”๊ฐ€
217
  enemies[index].muzzleFlash = createMuzzleFlash();
218
  enemy.add(enemies[index].muzzleFlash);
219
  }
@@ -226,265 +250,16 @@ function loadEnemies() {
226
  }
227
  );
228
 
229
- // enemies ๋ฐฐ์—ด์— ์ถ”๊ฐ€
230
  enemies.push({
231
  model: tempEnemy,
232
  health: 100,
233
- speed: 0.3 + (currentStage * 0.1),
234
  lastAttackTime: 0
235
  });
236
  }
237
  }
238
 
239
- function createMuzzleFlash() {
240
- const geometry = new THREE.PlaneGeometry(2, 2);
241
- const material = new THREE.MeshBasicMaterial({
242
- color: 0xff7700,
243
- transparent: true,
244
- opacity: 0,
245
- blending: THREE.AdditiveBlending
246
- });
247
- const muzzleFlash = new THREE.Mesh(geometry, material);
248
- muzzleFlash.visible = false;
249
- return muzzleFlash;
250
- }
251
-
252
- function addObstacles() {
253
- const rockGeometry = new THREE.DodecahedronGeometry(10);
254
- const rockMaterial = new THREE.MeshStandardMaterial({
255
- color: 0x8B4513,
256
- roughness: 0.9
257
- });
258
-
259
- for (let i = 0; i < 100; i++) {
260
- const rock = new THREE.Mesh(rockGeometry, rockMaterial);
261
- rock.position.set(
262
- (Math.random() - 0.5) * MAP_SIZE * 0.9,
263
- Math.random() * 10,
264
- (Math.random() - 0.5) * MAP_SIZE * 0.9
265
- );
266
- rock.rotation.set(
267
- Math.random() * Math.PI,
268
- Math.random() * Math.PI,
269
- Math.random() * Math.PI
270
- );
271
- rock.castShadow = true;
272
- rock.receiveShadow = true;
273
- scene.add(rock);
274
- }
275
- }
276
-
277
- function onClick() {
278
- if (!controls.isLocked) {
279
- controls.lock();
280
- sounds.bgm.play();
281
- } else if (ammo > 0) {
282
- shoot();
283
- }
284
- }
285
-
286
- function onKeyDown(event) {
287
- switch(event.code) {
288
- case 'KeyW': moveState.forward = true; break;
289
- case 'KeyS': moveState.backward = true; break;
290
- case 'KeyA': moveState.left = true; break;
291
- case 'KeyD': moveState.right = true; break;
292
- case 'KeyR': reload(); break;
293
- }
294
- }
295
-
296
- function onKeyUp(event) {
297
- switch(event.code) {
298
- case 'KeyW': moveState.forward = false; break;
299
- case 'KeyS': moveState.backward = false; break;
300
- case 'KeyA': moveState.left = false; break;
301
- case 'KeyD': moveState.right = false; break;
302
- }
303
- }
304
-
305
- function onWindowResize() {
306
- camera.aspect = window.innerWidth / window.innerHeight;
307
- camera.updateProjectionMatrix();
308
- renderer.setSize(window.innerWidth, window.innerHeight);
309
- }
310
-
311
- function shoot() {
312
- if (ammo <= 0) return;
313
-
314
- ammo--;
315
- updateAmmoDisplay();
316
-
317
- const bullet = createBullet();
318
- bullets.push(bullet);
319
-
320
- sounds.gunshot.play();
321
- }
322
-
323
- function createBullet() {
324
- const bulletGeometry = new THREE.SphereGeometry(0.5);
325
- const bulletMaterial = new THREE.MeshBasicMaterial({
326
- color: 0xffff00,
327
- emissive: 0xffff00,
328
- emissiveIntensity: 1
329
- });
330
- const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
331
-
332
- bullet.position.copy(camera.position);
333
- const direction = new THREE.Vector3();
334
- camera.getWorldDirection(direction);
335
- bullet.velocity = direction.multiplyScalar(3);
336
-
337
- scene.add(bullet);
338
- return bullet;
339
- }
340
-
341
- function createEnemyBullet(enemy) {
342
- const bulletGeometry = new THREE.SphereGeometry(0.5);
343
- const bulletMaterial = new THREE.MeshBasicMaterial({
344
- color: 0xff0000,
345
- emissive: 0xff0000,
346
- emissiveIntensity: 1
347
- });
348
- const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
349
-
350
- // ์ด๊ตฌ ์œ„์น˜ ์กฐ์ •
351
- const muzzleOffset = new THREE.Vector3(0, 2, 0);
352
- bullet.position.copy(enemy.model.position).add(muzzleOffset);
353
-
354
- // ์ด์•Œ ๊ถค์  ํšจ๊ณผ
355
- const trail = new THREE.Points(
356
- new THREE.BufferGeometry().setFromPoints([
357
- new THREE.Vector3(0, 0, 0),
358
- new THREE.Vector3(0, 0, -5)
359
- ]),
360
- new THREE.PointsMaterial({
361
- color: 0xff0000,
362
- size: 0.5,
363
- blending: THREE.AdditiveBlending
364
- })
365
- );
366
- bullet.add(trail);
367
-
368
- const direction = new THREE.Vector3();
369
- direction.subVectors(camera.position, enemy.model.position).normalize();
370
- bullet.velocity = direction.multiplyScalar(ENEMY_CONFIG.BULLET_SPEED);
371
-
372
- scene.add(bullet);
373
- return bullet;
374
- }
375
-
376
- function reload() {
377
- ammo = 30;
378
- updateAmmoDisplay();
379
- }
380
-
381
- function updateAmmoDisplay() {
382
- document.getElementById('ammo').textContent = `Ammo: ${ammo}/30`;
383
- }
384
-
385
- function updateHealthBar() {
386
- const healthElement = document.getElementById('health');
387
- const healthPercentage = (playerHealth / MAX_HEALTH) * 100;
388
- healthElement.style.width = `${healthPercentage}%`;
389
- }
390
-
391
- function updateHelicopterHUD() {
392
- const altitude = Math.round(camera.position.y);
393
- document.querySelector('#altitude-indicator span').textContent = altitude;
394
-
395
- const speed = Math.round(
396
- Math.sqrt(
397
- moveState.forward * moveState.forward +
398
- moveState.right * moveState.right
399
- ) * 100
400
- );
401
- document.querySelector('#speed-indicator span').textContent = speed;
402
-
403
- const heading = Math.round(
404
- (camera.rotation.y * (180 / Math.PI) + 360) % 360
405
- );
406
- document.querySelector('#compass span').textContent = heading;
407
-
408
- updateRadar();
409
- }
410
-
411
- function updateRadar() {
412
- const radarTargets = document.querySelector('.radar-targets');
413
- radarTargets.innerHTML = '';
414
-
415
- enemies.forEach(enemy => {
416
- const relativePos = enemy.model.position.clone().sub(camera.position);
417
- const distance = relativePos.length();
418
-
419
- if (distance < 500) {
420
- const angle = Math.atan2(relativePos.x, relativePos.z);
421
- const normalizedDistance = distance / 500;
422
-
423
- const dot = document.createElement('div');
424
- dot.className = 'radar-dot';
425
- dot.style.left = `${50 + Math.sin(angle) * normalizedDistance * 45}%`;
426
- dot.style.top = `${50 + Math.cos(angle) * normalizedDistance * 45}%`;
427
- radarTargets.appendChild(dot);
428
- }
429
- });
430
- }
431
-
432
- function updateMovement() {
433
- if (controls.isLocked) {
434
- const speed = 2.0;
435
- if (moveState.forward) controls.moveForward(speed);
436
- if (moveState.backward) controls.moveForward(-speed);
437
- if (moveState.left) controls.moveRight(-speed);
438
- if (moveState.right) controls.moveRight(speed);
439
- }
440
- }
441
-
442
- function updateBullets() {
443
- for (let i = bullets.length - 1; i >= 0; i--) {
444
- bullets[i].position.add(bullets[i].velocity);
445
-
446
- enemies.forEach(enemy => {
447
- if (bullets[i].position.distanceTo(enemy.model.position) < 5) {
448
- scene.remove(bullets[i]);
449
- bullets.splice(i, 1);
450
- enemy.health -= 25;
451
-
452
- if (enemy.health <= 0) {
453
- scene.remove(enemy.model);
454
- enemies = enemies.filter(e => e !== enemy);
455
- }
456
- }
457
- });
458
-
459
- if (bullets[i] && bullets[i].position.distanceTo(camera.position) > 1000) {
460
- scene.remove(bullets[i]);
461
- bullets.splice(i, 1);
462
- }
463
- }
464
- }
465
-
466
- function updateEnemyBullets() {
467
- for (let i = enemyBullets.length - 1; i >= 0; i--) {
468
- enemyBullets[i].position.add(enemyBullets[i].velocity);
469
-
470
- if (enemyBullets[i].position.distanceTo(camera.position) < 3) {
471
- playerHealth -= 10;
472
- updateHealthBar();
473
- scene.remove(enemyBullets[i]);
474
- enemyBullets.splice(i, 1);
475
-
476
- if (playerHealth <= 0) {
477
- gameOver(false);
478
- }
479
- continue;
480
- }
481
-
482
- if (enemyBullets[i].position.distanceTo(camera.position) > 1000) {
483
- scene.remove(enemyBullets[i]);
484
- enemyBullets.splice(i, 1);
485
- }
486
- }
487
- }
488
 
489
  function updateEnemies() {
490
  const currentTime = Date.now();
@@ -492,16 +267,25 @@ function updateEnemies() {
492
  enemies.forEach(enemy => {
493
  const direction = new THREE.Vector3();
494
  direction.subVectors(camera.position, enemy.model.position);
 
495
  direction.normalize();
496
 
497
- enemy.model.position.add(direction.multiplyScalar(enemy.speed));
498
- enemy.model.lookAt(camera.position);
 
 
 
 
 
 
 
 
 
499
 
500
  const distanceToPlayer = enemy.model.position.distanceTo(camera.position);
501
  if (distanceToPlayer < ENEMY_CONFIG.ATTACK_RANGE &&
502
  currentTime - enemy.lastAttackTime > ENEMY_CONFIG.ATTACK_INTERVAL) {
503
 
504
- // ์ด๊ตฌ ํ™”์—ผ ํšจ๊ณผ ํ‘œ์‹œ
505
  if (enemy.muzzleFlash) {
506
  enemy.muzzleFlash.material.opacity = 1;
507
  enemy.muzzleFlash.visible = true;
@@ -514,50 +298,38 @@ function updateEnemies() {
514
  enemyBullets.push(createEnemyBullet(enemy));
515
  enemy.lastAttackTime = currentTime;
516
  }
517
-
518
- if (distanceToPlayer < 10) {
519
- gameOver(false);
520
- }
521
  });
522
  }
523
 
524
- function checkGameStatus() {
525
- if (enemies.length === 0 && currentStage < 5) {
526
- currentStage++;
527
- document.getElementById('stage').style.display = 'block';
528
- document.getElementById('stage').textContent = `Stage ${currentStage}`;
529
- setTimeout(() => {
530
- document.getElementById('stage').style.display = 'none';
531
- loadEnemies();
532
- }, 2000);
533
- }
534
- }
535
-
536
- function gameOver(won) {
537
- isGameOver = true;
538
- controls.unlock();
539
- sounds.bgm.pause();
540
- alert(won ? 'Mission Complete!' : 'Game Over!');
541
- location.reload();
542
- }
 
 
 
 
543
 
544
- let lastTime = performance.now();
545
- function gameLoop() {
546
- const time = performance.now();
547
- const delta = (time - lastTime) / 1000;
548
- lastTime = time;
549
-
550
- if (controls.isLocked && !isGameOver) {
551
- updateMovement();
552
- updateBullets();
553
- updateEnemies();
554
- updateEnemyBullets();
555
- updateHelicopterHUD();
556
- checkGameStatus();
557
  }
558
-
559
- renderer.render(scene, camera);
560
- requestAnimationFrame(gameLoop);
561
  }
562
 
563
  // ๊ฒŒ์ž„ ์‹œ์ž‘
 
5
  // ๊ฒŒ์ž„ ์ƒ์ˆ˜
6
  const GAME_DURATION = 180;
7
  const MAP_SIZE = 2000;
8
+ const HELICOPTER_HEIGHT = 100; // ํ—ฌ๋ฆฌ์ฝฅํ„ฐ ๊ณ ๋„ ์ƒํ–ฅ
9
+ const ENEMY_GROUND_HEIGHT = 5; // ์  ์ง€์ƒ ๋†’์ด
10
  const ENEMY_SCALE = 3;
11
  const MAX_HEALTH = 1000;
12
+ const ENEMY_MOVE_SPEED = 0.1; // ์  ์ด๋™ ์†๋„
13
  const ENEMY_MODELS = [
14
  './models/enemy1.glb',
15
  './models/enemy12.glb',
 
48
 
49
  play() {
50
  const sound = this.sounds[this.currentIndex];
 
 
51
  sound.pause();
52
  sound.currentTime = 0;
53
 
 
54
  const playPromise = sound.play();
 
55
  if (playPromise !== undefined) {
56
  playPromise.catch(error => {
57
  console.error("Sound play error:", error);
 
58
  this.sounds[this.currentIndex] = new Audio(sound.src);
59
  });
60
  }
 
66
  // ์‚ฌ์šด๋“œ ์ดˆ๊ธฐํ™”
67
  const sounds = {
68
  bgm: new Audio('Music.wav'),
69
+ gunshot: new SoundPool('gun.wav', 20),
70
+ explosion: new SoundPool('explosion.wav', 10) // ํญ๋ฐœ ์‚ฌ์šด๋“œ ์ถ”๊ฐ€
71
  };
72
  sounds.bgm.loop = true;
73
 
 
83
  console.log('Game initialized');
84
  console.log('Available enemy models:', ENEMY_MODELS);
85
 
 
86
  scene = new THREE.Scene();
87
  scene.background = new THREE.Color(0x87ceeb);
88
  scene.fog = new THREE.Fog(0x87ceeb, 0, 1500);
89
 
 
90
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 3000);
91
  camera.position.set(0, HELICOPTER_HEIGHT, 0);
92
 
 
93
  renderer = new THREE.WebGLRenderer({ antialias: true });
94
  renderer.setSize(window.innerWidth, window.innerHeight);
95
  renderer.shadowMap.enabled = true;
96
  document.body.appendChild(renderer.domElement);
97
 
 
98
  const ambientLight = new THREE.AmbientLight(0xffffff, 1.0);
99
  scene.add(ambientLight);
100
 
 
103
  dirLight.castShadow = true;
104
  scene.add(dirLight);
105
 
 
106
  controls = new PointerLockControls(camera, document.body);
107
 
 
 
 
 
 
 
 
 
108
  document.addEventListener('click', onClick);
109
  document.addEventListener('keydown', onKeyDown);
110
  document.addEventListener('keyup', onKeyUp);
111
  window.addEventListener('resize', onWindowResize);
112
 
 
113
  createTerrain();
 
114
  console.log('Starting to load enemies...');
115
  loadEnemies();
116
  }
117
 
118
+ // ํญ๋ฐœ ํšจ๊ณผ ์ƒ์„ฑ ํ•จ์ˆ˜
119
+ function createExplosion(position) {
120
+ const particleCount = 30;
121
+ const particles = [];
122
+
123
+ for (let i = 0; i < particleCount; i++) {
124
+ const particle = new THREE.Mesh(
125
+ new THREE.SphereGeometry(0.3),
126
+ new THREE.MeshBasicMaterial({
127
+ color: 0xff4400,
128
+ transparent: true,
129
+ opacity: 1
130
+ })
131
+ );
132
+
133
+ particle.position.copy(position);
134
+ particle.velocity = new THREE.Vector3(
135
+ (Math.random() - 0.5) * 2,
136
+ Math.random() * 2,
137
+ (Math.random() - 0.5) * 2
138
+ );
139
+
140
+ particles.push(particle);
141
+ scene.add(particle);
142
+ }
143
+
144
+ // ํญ๋ฐœ ์• ๋‹ˆ๋ฉ”์ด์…˜
145
+ const animateExplosion = () => {
146
+ particles.forEach((particle, i) => {
147
+ particle.position.add(particle.velocity);
148
+ particle.material.opacity -= 0.02;
149
+
150
+ if (particle.material.opacity <= 0) {
151
+ scene.remove(particle);
152
+ particles.splice(i, 1);
153
+ }
154
+ });
155
+
156
+ if (particles.length > 0) {
157
+ requestAnimationFrame(animateExplosion);
158
+ }
159
+ };
160
+
161
+ animateExplosion();
162
+ sounds.explosion.play();
163
+ }
164
+
165
  function createTerrain() {
166
  const geometry = new THREE.PlaneGeometry(MAP_SIZE, MAP_SIZE, 200, 200);
167
  const material = new THREE.MeshStandardMaterial({
 
203
  const radius = 200;
204
  const position = new THREE.Vector3(
205
  Math.cos(angle) * radius,
206
+ ENEMY_GROUND_HEIGHT,
207
  Math.sin(angle) * radius
208
  );
209
 
 
210
  const tempEnemy = createTemporaryEnemy(position);
211
  scene.add(tempEnemy);
212
 
 
213
  const modelPath = ENEMY_MODELS[i % ENEMY_MODELS.length];
214
  console.log('Loading enemy model:', modelPath);
215
 
 
221
  enemy.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
222
  enemy.position.copy(position);
223
 
 
224
  enemy.rotation.y = Math.PI;
225
 
226
  enemy.traverse((node) => {
 
232
  }
233
  });
234
 
 
235
  scene.remove(tempEnemy);
236
  scene.add(enemy);
237
 
 
238
  const index = enemies.findIndex(e => e.model === tempEnemy);
239
  if (index !== -1) {
240
  enemies[index].model = enemy;
 
241
  enemies[index].muzzleFlash = createMuzzleFlash();
242
  enemy.add(enemies[index].muzzleFlash);
243
  }
 
250
  }
251
  );
252
 
 
253
  enemies.push({
254
  model: tempEnemy,
255
  health: 100,
256
+ speed: ENEMY_MOVE_SPEED,
257
  lastAttackTime: 0
258
  });
259
  }
260
  }
261
 
262
+ // ๋‚˜๋จธ์ง€ ํ•จ์ˆ˜๋“ค์€ ์ด์ „๊ณผ ๋™์ผํ•˜๊ฒŒ ์œ ์ง€๋˜๋ฉฐ, updateEnemies์™€ updateBullets ํ•จ์ˆ˜๋งŒ ์ˆ˜์ •๋ฉ๋‹ˆ๋‹ค.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
 
264
  function updateEnemies() {
265
  const currentTime = Date.now();
 
267
  enemies.forEach(enemy => {
268
  const direction = new THREE.Vector3();
269
  direction.subVectors(camera.position, enemy.model.position);
270
+ direction.y = 0; // y์ถ• ์ด๋™ ์ œํ•œ
271
  direction.normalize();
272
 
273
+ const newPosition = enemy.model.position.clone()
274
+ .add(direction.multiplyScalar(enemy.speed));
275
+ newPosition.y = ENEMY_GROUND_HEIGHT;
276
+ enemy.model.position.copy(newPosition);
277
+
278
+ // ์ ์ด ํ”Œ๋ ˆ์ด์–ด๋ฅผ ๋ฐ”๋ผ๋ณด๋„๋ก ์„ค์ •
279
+ enemy.model.lookAt(new THREE.Vector3(
280
+ camera.position.x,
281
+ enemy.model.position.y,
282
+ camera.position.z
283
+ ));
284
 
285
  const distanceToPlayer = enemy.model.position.distanceTo(camera.position);
286
  if (distanceToPlayer < ENEMY_CONFIG.ATTACK_RANGE &&
287
  currentTime - enemy.lastAttackTime > ENEMY_CONFIG.ATTACK_INTERVAL) {
288
 
 
289
  if (enemy.muzzleFlash) {
290
  enemy.muzzleFlash.material.opacity = 1;
291
  enemy.muzzleFlash.visible = true;
 
298
  enemyBullets.push(createEnemyBullet(enemy));
299
  enemy.lastAttackTime = currentTime;
300
  }
 
 
 
 
301
  });
302
  }
303
 
304
+ function updateBullets() {
305
+ for (let i = bullets.length - 1; i >= 0; i--) {
306
+ bullets[i].position.add(bullets[i].velocity);
307
+
308
+ enemies.forEach(enemy => {
309
+ if (bullets[i].position.distanceTo(enemy.model.position) < 5) {
310
+ scene.remove(bullets[i]);
311
+ bullets.splice(i, 1);
312
+ enemy.health -= 25;
313
+
314
+ // ํ”ผ๊ฒฉ ํšจ๊ณผ
315
+ createExplosion(enemy.model.position.clone());
316
+
317
+ if (enemy.health <= 0) {
318
+ // ํŒŒ๊ดด ํšจ๊ณผ
319
+ createExplosion(enemy.model.position.clone());
320
+ createExplosion(enemy.model.position.clone().add(new THREE.Vector3(2, 0, 2)));
321
+ createExplosion(enemy.model.position.clone().add(new THREE.Vector3(-2, 0, -2)));
322
+ scene.remove(enemy.model);
323
+ enemies = enemies.filter(e => e !== enemy);
324
+ }
325
+ }
326
+ });
327
 
328
+ if (bullets[i] && bullets[i].position.distanceTo(camera.position) > 1000) {
329
+ scene.remove(bullets[i]);
330
+ bullets.splice(i, 1);
331
+ }
 
 
 
 
 
 
 
 
 
332
  }
 
 
 
333
  }
334
 
335
  // ๊ฒŒ์ž„ ์‹œ์ž‘