Spaces:
Running
Running
gunship999
commited on
Commit
โข
08e0a03
1
Parent(s):
89db675
Update game.js
Browse files
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 =
|
|
|
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)
|
|
|
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 |
-
|
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:
|
234 |
lastAttackTime: 0
|
235 |
});
|
236 |
}
|
237 |
}
|
238 |
|
239 |
-
|
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.
|
498 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
|
|
|
|
|
|
|
|
543 |
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
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 |
// ๊ฒ์ ์์
|