gini1 commited on
Commit
bafb03c
โ€ข
1 Parent(s): cdd12f9

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +203 -286
index.html CHANGED
@@ -1,7 +1,7 @@
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
- <title>Helicopter Combat Game</title>
5
  <meta charset="utf-8">
6
  <style>
7
  body {
@@ -100,6 +100,15 @@
100
  z-index: 100;
101
  border-radius: 5px;
102
  }
 
 
 
 
 
 
 
 
 
103
  </style>
104
  </head>
105
  <body>
@@ -117,6 +126,7 @@
117
  <div id="healthBar"><div id="health"></div></div>
118
  <div id="ammo">Ammo: 30/30</div>
119
  <div id="stage">Stage 1</div>
 
120
 
121
  <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
122
 
@@ -138,9 +148,10 @@
138
  const GAME_DURATION = 180; // 3๋ถ„
139
  const MAX_STAGES = 5;
140
  const ENEMY_MODELS = ['1.GLB', '2.GLB', '3.GLB', '4.GLB'];
141
- const HELICOPTER_HEIGHT = 30; // ํ—ฌ๊ธฐ ๋†’์ด
142
- const ENEMY_SCALE = 2.5; // ์  ํฌ๊ธฐ
143
- const MAX_HEALTH = 1000; // ์ฆ๊ฐ€๋œ ์ฒด๋ ฅ
 
144
 
145
  // ๊ฒŒ์ž„ ๋ณ€์ˆ˜
146
  let scene, camera, renderer, controls;
@@ -165,10 +176,10 @@
165
  // Scene ์ดˆ๊ธฐํ™”
166
  function initScene() {
167
  scene = new THREE.Scene();
168
- scene.background = new THREE.Color(0x87ceeb);
169
- scene.fog = new THREE.Fog(0x87ceeb, 0, 500);
170
 
171
- camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
172
  camera.position.set(0, HELICOPTER_HEIGHT, 0);
173
 
174
  renderer = new THREE.WebGLRenderer({ antialias: true });
@@ -178,25 +189,45 @@
178
  document.body.appendChild(renderer.domElement);
179
 
180
  // ์กฐ๋ช…
181
- const ambientLight = new THREE.AmbientLight(0xffffff, 1.2);
182
  scene.add(ambientLight);
183
 
184
- const dirLight = new THREE.DirectionalLight(0xffffff, 1.5);
185
- dirLight.position.set(50, 50, 0);
186
  dirLight.castShadow = true;
187
  dirLight.shadow.mapSize.width = 4096;
188
  dirLight.shadow.mapSize.height = 4096;
189
- dirLight.shadow.camera.far = 300;
 
 
 
 
190
  scene.add(dirLight);
191
 
192
- // ๋ฐ˜์‚ฌ๊ด‘
193
- const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.8);
194
- scene.add(hemiLight);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  }
196
 
197
  // ์ด์•Œ ์ƒ์„ฑ
198
  function createBullet(isEnemy = false) {
199
- const bulletGeometry = new THREE.SphereGeometry(0.2, 8, 8);
200
  const bulletMaterial = new THREE.MeshBasicMaterial({
201
  color: isEnemy ? 0xff0000 : 0xffff00,
202
  emissive: isEnemy ? 0xff0000 : 0xffff00,
@@ -208,7 +239,7 @@
208
  bullet.position.copy(camera.position);
209
  const direction = new THREE.Vector3();
210
  camera.getWorldDirection(direction);
211
- bullet.velocity = direction.multiplyScalar(2);
212
  sounds.gunshot.currentTime = 0;
213
  sounds.gunshot.play();
214
  }
@@ -217,197 +248,149 @@
217
  return bullet;
218
  }
219
 
220
- // ๋ฐœ์‚ฌ
221
- function shoot() {
222
- if (ammo <= 0) return;
223
-
224
- ammo--;
225
- updateAmmoDisplay();
226
-
227
- const bullet = createBullet();
228
- bullets.push(bullet);
229
- }
230
 
231
- // ์  ๋ฐœ์‚ฌ
232
- function enemyShoot(enemy) {
233
- if (Math.random() > getFireRate()) return;
234
-
235
- const bullet = createBullet(true);
236
- bullet.position.copy(enemy.model.position);
237
-
238
- const direction = new THREE.Vector3();
239
- direction.subVectors(camera.position, enemy.model.position);
240
- direction.normalize();
241
- bullet.velocity = direction.multiplyScalar(1.5);
242
 
243
- enemyBullets.push(bullet);
244
- }
245
-
246
- // ์Šคํ…Œ์ด์ง€๋ณ„ ๋ฐœ์‚ฌ ํ™•๋ฅ 
247
- function getFireRate() {
248
- const baseRate = 0.01;
249
- return baseRate * (1 + (currentStage - 1) * 0.5);
250
- }
251
-
252
- // ์ด์•Œ ์—…๋ฐ์ดํŠธ
253
- function updateBullets() {
254
- // ํ”Œ๋ ˆ์ด์–ด ์ด์•Œ
255
- for (let i = bullets.length - 1; i >= 0; i--) {
256
- bullets[i].position.add(bullets[i].velocity);
257
-
258
- // ์ ๊ณผ์˜ ์ถฉ๋Œ ์ฒดํฌ
259
- enemies.forEach(enemy => {
260
- if (bullets[i].position.distanceTo(enemy.model.position) < 4) {
261
- scene.remove(bullets[i]);
262
- bullets.splice(i, 1);
263
- enemy.health -= 25;
264
-
265
- if (enemy.health <= 0) {
266
- scene.remove(enemy.model);
267
- enemies = enemies.filter(e => e !== enemy);
268
- checkStageCompletion();
269
- }
270
- return;
271
- }
272
- });
273
-
274
- // ์ˆ˜๋ช… ์ œํ•œ
275
- if (bullets[i] && bullets[i].position.distanceTo(camera.position) > 200) {
276
- scene.remove(bullets[i]);
277
- bullets.splice(i, 1);
278
- }
279
- }
280
-
281
- // ์  ์ด์•Œ
282
- for (let i = enemyBullets.length - 1; i >= 0; i--) {
283
- enemyBullets[i].position.add(enemyBullets[i].velocity);
284
-
285
- // ํ”Œ๋ ˆ์ด์–ด์™€์˜ ์ถฉ๋Œ ์ฒดํฌ
286
- if (enemyBullets[i].position.distanceTo(camera.position) < 3) {
287
- scene.remove(enemyBullets[i]);
288
- enemyBullets.splice(i, 1);
289
- playerHealth -= 20;
290
- updateHealthBar();
291
-
292
- if (playerHealth <= 0) {
293
- gameOver(false);
294
- }
295
- continue;
296
- }
297
-
298
- // ์ˆ˜๋ช… ์ œํ•œ
299
- if (enemyBullets[i] && enemyBullets[i].position.distanceTo(camera.position) > 200) {
300
- scene.remove(enemyBullets[i]);
301
- enemyBullets.splice(i, 1);
302
- }
303
- }
304
- }
305
 
306
- // ์Šคํ…Œ์ด์ง€ ์™„๋ฃŒ ์ฒดํฌ
307
- function checkStageCompletion() {
308
- if (enemies.length === 0 && currentStage < MAX_STAGES) {
309
- currentStage++;
310
- showStageMessage();
311
- loadEnemies();
 
 
312
  }
313
- }
314
 
315
- // ์Šคํ…Œ์ด์ง€ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
316
- function showStageMessage() {
317
- const stageDiv = document.getElementById('stage');
318
- stageDiv.textContent = `Stage ${currentStage}`;
319
- stageDiv.style.display = 'block';
320
- setTimeout(() => {
321
- stageDiv.style.display = 'none';
322
- }, 3000);
323
- }
324
 
325
- // UI ์—…๋ฐ์ดํŠธ
326
- function updateHealthBar() {
327
- const healthBar = document.getElementById('health');
328
- healthBar.style.width = `${(playerHealth / MAX_HEALTH) * 100}%`;
329
- }
330
 
331
- function updateAmmoDisplay() {
332
- document.getElementById('ammo').textContent = `Ammo: ${ammo}/${maxAmmo}`;
 
 
333
  }
334
 
335
- function updateGameTimer() {
336
- if (!gameStartTime) return;
337
-
338
- const elapsed = Math.floor((Date.now() - gameStartTime) / 1000);
339
- const remaining = GAME_DURATION - elapsed;
 
 
 
340
 
341
- if (remaining <= 0) {
342
- gameOver(true);
343
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  }
345
-
346
- const minutes = Math.floor(remaining / 60);
347
- const seconds = remaining % 60;
348
- document.getElementById('gameTimer').textContent =
349
- `Time: ${minutes}:${seconds.toString().padStart(2, '0')}`;
350
- }
351
-
352
- // ์žฌ์žฅ์ „
353
- function reload() {
354
- ammo = maxAmmo;
355
- updateAmmoDisplay();
356
  }
357
 
358
- // ์ง€ํ˜• ์ƒ์„ฑ
359
- function createTerrain() {
360
- const geometry = new THREE.PlaneGeometry(500, 500, 100, 100);
361
- const material = new THREE.MeshStandardMaterial({
362
- color: 0x3a8c3a,
363
- roughness: 0.6,
364
- metalness: 0.1
365
  });
366
 
367
- const vertices = geometry.attributes.position.array;
368
- for (let i = 0; i < vertices.length; i += 3) {
369
- vertices[i + 2] = Math.cos(vertices[i] * 0.02) * Math.sin(vertices[i + 1] * 0.02) * 5;
370
- }
371
- geometry.attributes.position.needsUpdate = true;
372
- geometry.computeVertexNormals();
 
 
 
 
 
 
 
 
 
 
373
 
374
- const terrain = new THREE.Mesh(geometry, material);
375
- terrain.rotation.x = -Math.PI / 2;
376
- terrain.receiveShadow = true;
377
- scene.add(terrain);
 
 
 
 
 
378
  }
379
 
380
  // ์  ๋กœ๋“œ
381
  function loadEnemies() {
 
382
  const loader = new GLTFLoader();
383
  const enemiesCount = 3 + currentStage;
384
 
385
  for (let i = 0; i < enemiesCount; i++) {
386
  const modelPath = ENEMY_MODELS[i % ENEMY_MODELS.length];
 
 
387
  loader.load(modelPath, (gltf) => {
388
  const enemy = gltf.scene;
389
  enemy.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
390
 
391
- const angle = (i / enemiesCount) * Math.PI * 2;
392
- const radius = 100;
393
- enemy.position.set(
394
- Math.cos(angle) * radius,
395
- 5,
396
- Math.sin(angle) * radius
397
- );
398
 
 
399
  enemy.traverse((node) => {
400
  if (node.isMesh) {
401
  node.material = node.material.clone();
402
  node.material.metalness = 0.3;
403
  node.material.roughness = 0.5;
 
 
404
  node.castShadow = true;
405
- node
406
-
407
-
408
-
409
- node.material.emissive = new THREE.Color(0x444444);
410
- node.material.emissiveIntensity = 0.2;
411
  }
412
  });
413
 
@@ -415,116 +398,93 @@ node.material.emissive = new THREE.Color(0x444444);
415
  enemies.push({
416
  model: enemy,
417
  health: 100,
418
- speed: 0.2 + (currentStage * 0.05),
419
  velocity: new THREE.Vector3(),
420
  lastShot: 0,
421
- moveTarget: new THREE.Vector3()
 
422
  });
423
 
 
 
424
  updateMoveTarget(enemies[enemies.length - 1]);
 
 
 
 
 
 
425
  });
426
  }
427
  }
428
 
 
 
 
 
 
 
 
 
 
 
 
429
  // ์ ์˜ ์ƒˆ๋กœ์šด ์ด๋™ ๋ชฉํ‘œ์  ์„ค์ •
430
  function updateMoveTarget(enemy) {
 
 
431
  enemy.moveTarget.set(
432
- (Math.random() - 0.5) * 400,
433
- 5,
434
- (Math.random() - 0.5) * 400
435
  );
436
  }
437
 
438
- // ์ปจํŠธ๋กค ์ดˆ๊ธฐํ™”
439
- function initControls() {
440
- controls = new PointerLockControls(camera, document.body);
441
-
442
- const moveState = {
443
- forward: false,
444
- backward: false,
445
- left: false,
446
- right: false
447
- };
448
-
449
- document.addEventListener('keydown', (event) => {
450
- switch(event.code) {
451
- case 'KeyW': moveState.forward = true; break;
452
- case 'KeyS': moveState.backward = true; break;
453
- case 'KeyA': moveState.left = true; break;
454
- case 'KeyD': moveState.right = true; break;
455
- case 'KeyR': reload(); break;
456
- }
457
- });
458
-
459
- document.addEventListener('keyup', (event) => {
460
- switch(event.code) {
461
- case 'KeyW': moveState.forward = false; break;
462
- case 'KeyS': moveState.backward = false; break;
463
- case 'KeyA': moveState.left = false; break;
464
- case 'KeyD': moveState.right = false; break;
465
- }
466
- });
467
-
468
- document.addEventListener('click', () => {
469
- if (controls.isLocked) {
470
- shoot();
471
- } else {
472
- controls.lock();
473
- if (!gameStartTime) {
474
- gameStartTime = Date.now();
475
- sounds.bgm.play();
476
- }
477
- }
478
- });
479
-
480
- controls.addEventListener('lock', () => {
481
- document.getElementById('info').style.display = 'none';
482
- });
483
-
484
- controls.addEventListener('unlock', () => {
485
- document.getElementById('info').style.display = 'block';
486
- });
487
-
488
- return moveState;
489
- }
490
-
491
  // ์  ์—…๋ฐ์ดํŠธ
492
  function updateEnemies() {
493
  if (!isSafePeriod) {
494
  enemies.forEach(enemy => {
495
- // ๋ชฉํ‘œ์ ๊นŒ์ง€์˜ ๋ฐฉํ–ฅ ๊ณ„์‚ฐ
496
  const toTarget = new THREE.Vector3();
497
  toTarget.subVectors(enemy.moveTarget, enemy.model.position);
498
 
499
- // ๋ชฉํ‘œ์ ์— ๊ฑฐ์˜ ๋„๋‹ฌํ–ˆ์œผ๋ฉด ์ƒˆ ๋ชฉํ‘œ์  ์„ค์ •
500
- if (toTarget.length() < 5) {
501
  updateMoveTarget(enemy);
502
  }
503
 
504
- // ์ด๋™ ๋ฐฉํ–ฅ ๊ฒฐ์ • (๋ชฉํ‘œ์  80%, ํ”Œ๋ ˆ์ด์–ด ๋ฐฉํ–ฅ 20%)
505
  const toPlayer = new THREE.Vector3();
506
  toPlayer.subVectors(camera.position, enemy.model.position);
 
 
 
 
507
 
508
  const direction = new THREE.Vector3();
509
- direction.addScaledVector(toTarget.normalize(), 0.8);
510
- direction.addScaledVector(toPlayer.normalize(), 0.2);
511
  direction.normalize();
512
 
513
- // ์†๋„ ์ ์šฉ
514
- enemy.velocity.lerp(direction.multiplyScalar(enemy.speed), 0.02);
515
  enemy.model.position.add(enemy.velocity);
516
-
517
- // ํšŒ์ „
518
  enemy.model.lookAt(camera.position);
519
 
520
  // ๋ฐœ์‚ฌ ๋กœ์ง
521
  const now = Date.now();
522
- const fireInterval = 2000 - (currentStage * 200); // ์Šคํ…Œ์ด์ง€๊ฐ€ ์˜ฌ๋ผ๊ฐˆ์ˆ˜๋ก ๋ฐœ์‚ฌ ๊ฐ„๊ฒฉ ๊ฐ์†Œ
 
 
 
523
  if (now - enemy.lastShot > fireInterval) {
524
  enemyShoot(enemy);
525
  enemy.lastShot = now;
526
  }
527
  });
 
 
528
  }
529
  }
530
 
@@ -535,57 +495,14 @@ node.material.emissive = new THREE.Color(0x444444);
535
  controls.unlock();
536
  sounds.bgm.pause();
537
  setTimeout(() => {
538
- alert(won ? 'Mission Accomplished! You survived!' : 'Game Over! You were eliminated!');
539
  location.reload();
540
  }, 100);
541
  }
542
  }
543
 
544
- // ๊ฒŒ์ž„ ์ดˆ๊ธฐํ™”
545
- function init() {
546
- initScene();
547
- createTerrain();
548
- loadEnemies();
549
- const moveState = initControls();
550
- showStageMessage();
551
-
552
- // ๊ฒŒ์ž„ ๋ฃจํ”„
553
- function animate() {
554
- requestAnimationFrame(animate);
555
-
556
- if (controls.isLocked && !isGameOver) {
557
- // ์ด๋™ ์ฒ˜๋ฆฌ
558
- const speed = 0.5;
559
- if (moveState.forward) controls.moveForward(speed);
560
- if (moveState.backward) controls.moveForward(-speed);
561
- if (moveState.left) controls.moveRight(-speed);
562
- if (moveState.right) controls.moveRight(speed);
563
-
564
- // ์ด์•Œ ์—…๋ฐ์ดํŠธ
565
- updateBullets();
566
-
567
- // ์  ์—…๋ฐ์ดํŠธ
568
- updateEnemies();
569
-
570
- // ๊ฒŒ์ž„ ํƒ€์ด๋จธ ์—…๋ฐ์ดํŠธ
571
- updateGameTimer();
572
- }
573
-
574
- renderer.render(scene, camera);
575
- }
576
-
577
- // ํ™”๋ฉด ํฌ๊ธฐ ์กฐ์ ˆ ์ฒ˜๋ฆฌ
578
- window.addEventListener('resize', () => {
579
- camera.aspect = window.innerWidth / window.innerHeight;
580
- camera.updateProjectionMatrix();
581
- renderer.setSize(window.innerWidth, window.innerHeight);
582
- });
583
-
584
- animate();
585
- }
586
-
587
- // ๊ฒŒ์ž„ ์‹œ์ž‘
588
  init();
589
  </script>
590
  </body>
591
- </html>
 
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
+ <title>Desert Combat Game</title>
5
  <meta charset="utf-8">
6
  <style>
7
  body {
 
100
  z-index: 100;
101
  border-radius: 5px;
102
  }
103
+ #debug {
104
+ position: absolute;
105
+ bottom: 60px;
106
+ left: 20px;
107
+ color: white;
108
+ font-family: Arial;
109
+ font-size: 12px;
110
+ z-index: 100;
111
+ }
112
  </style>
113
  </head>
114
  <body>
 
126
  <div id="healthBar"><div id="health"></div></div>
127
  <div id="ammo">Ammo: 30/30</div>
128
  <div id="stage">Stage 1</div>
129
+ <div id="debug"></div>
130
 
131
  <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
132
 
 
148
  const GAME_DURATION = 180; // 3๋ถ„
149
  const MAX_STAGES = 5;
150
  const ENEMY_MODELS = ['1.GLB', '2.GLB', '3.GLB', '4.GLB'];
151
+ const HELICOPTER_HEIGHT = 50; // ์ฆ๊ฐ€๋œ ํ—ฌ๊ธฐ ๋†’์ด
152
+ const ENEMY_SCALE = 3; // ์ฆ๊ฐ€๋œ ์  ํฌ๊ธฐ
153
+ const MAX_HEALTH = 1000;
154
+ const MAP_SIZE = 2000; // ์ฆ๊ฐ€๋œ ๋งต ํฌ๊ธฐ
155
 
156
  // ๊ฒŒ์ž„ ๋ณ€์ˆ˜
157
  let scene, camera, renderer, controls;
 
176
  // Scene ์ดˆ๊ธฐํ™”
177
  function initScene() {
178
  scene = new THREE.Scene();
179
+ scene.background = new THREE.Color(0xffd700); // ์‚ฌ๋ง‰ ํ•˜๋Š˜์ƒ‰
180
+ scene.fog = new THREE.Fog(0xffd700, 0, 1500);
181
 
182
+ camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 3000);
183
  camera.position.set(0, HELICOPTER_HEIGHT, 0);
184
 
185
  renderer = new THREE.WebGLRenderer({ antialias: true });
 
189
  document.body.appendChild(renderer.domElement);
190
 
191
  // ์กฐ๋ช…
192
+ const ambientLight = new THREE.AmbientLight(0xffffff, 1.5);
193
  scene.add(ambientLight);
194
 
195
+ const dirLight = new THREE.DirectionalLight(0xffffff, 2);
196
+ dirLight.position.set(100, 100, 50);
197
  dirLight.castShadow = true;
198
  dirLight.shadow.mapSize.width = 4096;
199
  dirLight.shadow.mapSize.height = 4096;
200
+ dirLight.shadow.camera.far = 1000;
201
+ dirLight.shadow.camera.left = -500;
202
+ dirLight.shadow.camera.right = 500;
203
+ dirLight.shadow.camera.top = 500;
204
+ dirLight.shadow.camera.bottom = -500;
205
  scene.add(dirLight);
206
 
207
+ // ์‚ฌ๋ง‰ ํ–‡๋น› ํšจ๊ณผ
208
+ const sunLight = new THREE.DirectionalLight(0xfdb813, 1);
209
+ sunLight.position.set(-100, 200, -100);
210
+ scene.add(sunLight);
211
+ }
212
+
213
+ // ๋””๋ฒ„๊ทธ ์ •๋ณด ์—…๋ฐ์ดํŠธ
214
+ function updateDebugInfo() {
215
+ const debugDiv = document.getElementById('debug');
216
+ debugDiv.innerHTML = `
217
+ Enemies: ${enemies.length}<br>
218
+ Stage: ${currentStage}<br>
219
+ Enemy Positions:<br>
220
+ ${enemies.map((e, i) =>
221
+ `Enemy ${i}: x=${e.model.position.x.toFixed(1)},
222
+ y=${e.model.position.y.toFixed(1)},
223
+ z=${e.model.position.z.toFixed(1)}`
224
+ ).join('<br>')}
225
+ `;
226
  }
227
 
228
  // ์ด์•Œ ์ƒ์„ฑ
229
  function createBullet(isEnemy = false) {
230
+ const bulletGeometry = new THREE.SphereGeometry(0.5, 8, 8);
231
  const bulletMaterial = new THREE.MeshBasicMaterial({
232
  color: isEnemy ? 0xff0000 : 0xffff00,
233
  emissive: isEnemy ? 0xff0000 : 0xffff00,
 
239
  bullet.position.copy(camera.position);
240
  const direction = new THREE.Vector3();
241
  camera.getWorldDirection(direction);
242
+ bullet.velocity = direction.multiplyScalar(3);
243
  sounds.gunshot.currentTime = 0;
244
  sounds.gunshot.play();
245
  }
 
248
  return bullet;
249
  }
250
 
 
 
 
 
 
 
 
 
 
 
251
 
252
+ // ์‚ฌ๋ง‰ ์ง€ํ˜• ์ƒ์„ฑ
253
+ function createTerrain() {
254
+ // ์‚ฌ๋ง‰ ํ…์Šค์ฒ˜ ์ƒ์„ฑ
255
+ const textureLoader = new THREE.TextureLoader();
256
+ const sandTexture = textureLoader.load('https://threejs.org/examples/textures/terrain/grasslight-big.jpg');
257
+ sandTexture.wrapS = sandTexture.wrapT = THREE.RepeatWrapping;
258
+ sandTexture.repeat.set(25, 25);
 
 
 
 
259
 
260
+ const geometry = new THREE.PlaneGeometry(MAP_SIZE, MAP_SIZE, 200, 200);
261
+ const material = new THREE.MeshStandardMaterial({
262
+ color: 0xd2b48c,
263
+ map: sandTexture,
264
+ roughness: 0.8,
265
+ metalness: 0.2,
266
+ bumpScale: 0.5,
267
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
+ // ๋ชจ๋ž˜ ์–ธ๋• ์ƒ์„ฑ
270
+ const vertices = geometry.attributes.position.array;
271
+ for (let i = 0; i < vertices.length; i += 3) {
272
+ const x = vertices[i];
273
+ const y = vertices[i + 1];
274
+ vertices[i + 2] = Math.sin(x * 0.01) * Math.cos(y * 0.01) * 20 +
275
+ Math.random() * 5 +
276
+ Math.sin(x * 0.02 + y * 0.02) * 10;
277
  }
 
278
 
279
+ geometry.attributes.position.needsUpdate = true;
280
+ geometry.computeVertexNormals();
 
 
 
 
 
 
 
281
 
282
+ const terrain = new THREE.Mesh(geometry, material);
283
+ terrain.rotation.x = -Math.PI / 2;
284
+ terrain.receiveShadow = true;
285
+ scene.add(terrain);
 
286
 
287
+ // ์‚ฌ๋ง‰ ๋ฐ”์œ„ ์ถ”๊ฐ€
288
+ addDesertRocks();
289
+ // ์„ ์ธ์žฅ ์ถ”๊ฐ€
290
+ addCacti();
291
  }
292
 
293
+ // ์‚ฌ๋ง‰ ๋ฐ”์œ„ ์ถ”๊ฐ€
294
+ function addDesertRocks() {
295
+ const rockGeometry = new THREE.DodecahedronGeometry(10);
296
+ const rockMaterial = new THREE.MeshStandardMaterial({
297
+ color: 0x8b4513,
298
+ roughness: 0.8,
299
+ metalness: 0.2
300
+ });
301
 
302
+ for (let i = 0; i < 50; i++) {
303
+ const rock = new THREE.Mesh(rockGeometry, rockMaterial);
304
+ rock.position.set(
305
+ (Math.random() - 0.5) * MAP_SIZE * 0.9,
306
+ Math.random() * 10 + 5,
307
+ (Math.random() - 0.5) * MAP_SIZE * 0.9
308
+ );
309
+ rock.rotation.set(
310
+ Math.random() * Math.PI,
311
+ Math.random() * Math.PI,
312
+ Math.random() * Math.PI
313
+ );
314
+ rock.scale.set(
315
+ Math.random() * 2 + 1,
316
+ Math.random() * 2 + 1,
317
+ Math.random() * 2 + 1
318
+ );
319
+ rock.castShadow = true;
320
+ rock.receiveShadow = true;
321
+ scene.add(rock);
322
  }
 
 
 
 
 
 
 
 
 
 
 
323
  }
324
 
325
+ // ์„ ์ธ์žฅ ์ถ”๊ฐ€
326
+ function addCacti() {
327
+ const trunkGeometry = new THREE.CylinderGeometry(2, 2, 15, 8);
328
+ const armGeometry = new THREE.CylinderGeometry(1.5, 1.5, 10, 8);
329
+ const cactusMaterial = new THREE.MeshStandardMaterial({
330
+ color: 0x2f4f2f,
331
+ roughness: 0.8
332
  });
333
 
334
+ for (let i = 0; i < 100; i++) {
335
+ const cactusGroup = new THREE.Group();
336
+
337
+ // ๋ชธํ†ต
338
+ const trunk = new THREE.Mesh(trunkGeometry, cactusMaterial);
339
+ cactusGroup.add(trunk);
340
+
341
+ // ํŒ” ์ถ”๊ฐ€
342
+ const armsCount = Math.floor(Math.random() * 3) + 1;
343
+ for (let j = 0; j < armsCount; j++) {
344
+ const arm = new THREE.Mesh(armGeometry, cactusMaterial);
345
+ arm.position.y = Math.random() * 5;
346
+ arm.rotation.z = Math.PI / 2 * (Math.random() > 0.5 ? 1 : -1);
347
+ arm.position.x = (Math.random() > 0.5 ? 1 : -1) * 3;
348
+ cactusGroup.add(arm);
349
+ }
350
 
351
+ cactusGroup.position.set(
352
+ (Math.random() - 0.5) * MAP_SIZE * 0.9,
353
+ 7.5,
354
+ (Math.random() - 0.5) * MAP_SIZE * 0.9
355
+ );
356
+ cactusGroup.castShadow = true;
357
+ cactusGroup.receiveShadow = true;
358
+ scene.add(cactusGroup);
359
+ }
360
  }
361
 
362
  // ์  ๋กœ๋“œ
363
  function loadEnemies() {
364
+ console.log('Loading enemies...');
365
  const loader = new GLTFLoader();
366
  const enemiesCount = 3 + currentStage;
367
 
368
  for (let i = 0; i < enemiesCount; i++) {
369
  const modelPath = ENEMY_MODELS[i % ENEMY_MODELS.length];
370
+ console.log(`Loading enemy model: ${modelPath}`);
371
+
372
  loader.load(modelPath, (gltf) => {
373
  const enemy = gltf.scene;
374
  enemy.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
375
 
376
+ // ๋žœ๋ค ์œ„์น˜์— ์Šคํฐํ•˜๋˜, ํ”Œ๋ ˆ์ด์–ด๋กœ๋ถ€ํ„ฐ ์ตœ์†Œ ๊ฑฐ๋ฆฌ ์œ ์ง€
377
+ let position = getRandomSpawnPosition();
378
+ while (position.distanceTo(camera.position) < 100) {
379
+ position = getRandomSpawnPosition();
380
+ }
381
+
382
+ enemy.position.copy(position);
383
 
384
+ // ๋ฐ๊ธฐ ๊ฐœ์„ ์„ ์œ„ํ•œ ์žฌ์งˆ ์„ค์ •
385
  enemy.traverse((node) => {
386
  if (node.isMesh) {
387
  node.material = node.material.clone();
388
  node.material.metalness = 0.3;
389
  node.material.roughness = 0.5;
390
+ node.material.emissive = new THREE.Color(0x666666);
391
+ node.material.emissiveIntensity = 0.3;
392
  node.castShadow = true;
393
+ node.receiveShadow = true;
 
 
 
 
 
394
  }
395
  });
396
 
 
398
  enemies.push({
399
  model: enemy,
400
  health: 100,
401
+ speed: 0.3 + (currentStage * 0.05),
402
  velocity: new THREE.Vector3(),
403
  lastShot: 0,
404
+ moveTarget: new THREE.Vector3(),
405
+ type: modelPath
406
  });
407
 
408
+ console.log(`Enemy loaded: ${modelPath}, Position:`, position);
409
+ updateDebugInfo();
410
  updateMoveTarget(enemies[enemies.length - 1]);
411
+ },
412
+ (xhr) => {
413
+ console.log(`${modelPath}: ${(xhr.loaded / xhr.total) * 100}% loaded`);
414
+ },
415
+ (error) => {
416
+ console.error(`Error loading ${modelPath}:`, error);
417
  });
418
  }
419
  }
420
 
421
+ // ๋žœ๋ค ์Šคํฐ ์œ„์น˜ ์ƒ์„ฑ
422
+ function getRandomSpawnPosition() {
423
+ const angle = Math.random() * Math.PI * 2;
424
+ const radius = Math.random() * (MAP_SIZE * 0.4) + 100;
425
+ return new THREE.Vector3(
426
+ Math.cos(angle) * radius,
427
+ 10,
428
+ Math.sin(angle) * radius
429
+ );
430
+ }
431
+
432
  // ์ ์˜ ์ƒˆ๋กœ์šด ์ด๋™ ๋ชฉํ‘œ์  ์„ค์ •
433
  function updateMoveTarget(enemy) {
434
+ const angle = Math.random() * Math.PI * 2;
435
+ const radius = Math.random() * (MAP_SIZE * 0.4);
436
  enemy.moveTarget.set(
437
+ Math.cos(angle) * radius,
438
+ 10,
439
+ Math.sin(angle) * radius
440
  );
441
  }
442
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
  // ์  ์—…๋ฐ์ดํŠธ
444
  function updateEnemies() {
445
  if (!isSafePeriod) {
446
  enemies.forEach(enemy => {
447
+ // AI ์ด๋™ ๋กœ์ง
448
  const toTarget = new THREE.Vector3();
449
  toTarget.subVectors(enemy.moveTarget, enemy.model.position);
450
 
451
+ if (toTarget.length() < 10) {
 
452
  updateMoveTarget(enemy);
453
  }
454
 
455
+ // ํ”Œ๋ ˆ์ด์–ด ๋ฐฉํ–ฅ ๊ฐ€์ค‘์น˜ ๊ณ„์‚ฐ
456
  const toPlayer = new THREE.Vector3();
457
  toPlayer.subVectors(camera.position, enemy.model.position);
458
+ const distanceToPlayer = toPlayer.length();
459
+
460
+ // ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ฅธ ๊ฐ€์ค‘์น˜ ์กฐ์ •
461
+ const playerWeight = Math.min(1, 100 / distanceToPlayer);
462
 
463
  const direction = new THREE.Vector3();
464
+ direction.addScaledVector(toTarget.normalize(), 1 - playerWeight);
465
+ direction.addScaledVector(toPlayer.normalize(), playerWeight);
466
  direction.normalize();
467
 
468
+ // ์†๋„ ๋ฐ ์œ„์น˜ ์—…๋ฐ์ดํŠธ
469
+ enemy.velocity.lerp(direction.multiplyScalar(enemy.speed), 0.05);
470
  enemy.model.position.add(enemy.velocity);
471
+
472
+ // ์  ํšŒ์ „
473
  enemy.model.lookAt(camera.position);
474
 
475
  // ๋ฐœ์‚ฌ ๋กœ์ง
476
  const now = Date.now();
477
+ const baseInterval = 3000 - (currentStage * 300); // ๊ธฐ๋ณธ ๋ฐœ์‚ฌ ๊ฐ„๊ฒฉ
478
+ const distanceModifier = Math.max(0.5, Math.min(1.5, distanceToPlayer / 100));
479
+ const fireInterval = baseInterval * distanceModifier;
480
+
481
  if (now - enemy.lastShot > fireInterval) {
482
  enemyShoot(enemy);
483
  enemy.lastShot = now;
484
  }
485
  });
486
+
487
+ updateDebugInfo();
488
  }
489
  }
490
 
 
495
  controls.unlock();
496
  sounds.bgm.pause();
497
  setTimeout(() => {
498
+ alert(won ? 'Mission Accomplished! You survived in the desert!' : 'Game Over! You were eliminated!');
499
  location.reload();
500
  }, 100);
501
  }
502
  }
503
 
504
+ // ๊ฒŒ์ž„ ์ดˆ๊ธฐํ™” ๋ฐ ์‹œ์ž‘
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
505
  init();
506
  </script>
507
  </body>
508
+ </html>