Spaces:
Running
Running
cutechicken
commited on
Commit
โข
7aa0006
1
Parent(s):
7827e8b
Update game.js
Browse files
game.js
CHANGED
@@ -102,21 +102,29 @@ class TankPlayer {
|
|
102 |
return bullet;
|
103 |
}
|
104 |
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
}
|
117 |
}
|
118 |
}
|
119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
move(direction) {
|
121 |
if (!this.body) return;
|
122 |
|
@@ -318,60 +326,75 @@ class Game {
|
|
318 |
}
|
319 |
|
320 |
async initialize() {
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
throw new Error('Tank loading failed');
|
349 |
-
}
|
350 |
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
tankPosition.y + 15,
|
355 |
-
tankPosition.z - 30
|
356 |
-
);
|
357 |
-
this.camera.lookAt(new THREE.Vector3(
|
358 |
-
tankPosition.x,
|
359 |
-
tankPosition.y + 2,
|
360 |
-
tankPosition.z
|
361 |
-
));
|
362 |
|
363 |
-
|
364 |
-
|
|
|
|
|
|
|
|
|
|
|
365 |
|
366 |
-
|
367 |
-
this.spawnEnemies();
|
368 |
-
this.startGameTimer();
|
369 |
|
370 |
-
|
371 |
-
|
372 |
-
|
|
|
|
|
373 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
374 |
}
|
|
|
375 |
|
376 |
setupEventListeners() {
|
377 |
document.addEventListener('keydown', (event) => {
|
@@ -426,54 +449,56 @@ class Game {
|
|
426 |
}
|
427 |
|
428 |
handleMovement() {
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
if (this.keys.left) this.tank.rotate(-1);
|
442 |
-
if (this.keys.right) this.tank.rotate(1);
|
443 |
-
|
444 |
-
direction.applyEuler(this.tank.body.rotation);
|
445 |
-
this.tank.move(direction);
|
446 |
-
}
|
447 |
-
|
448 |
-
const mouseVector = new THREE.Vector2(this.mouse.x, -this.mouse.y);
|
449 |
-
const rotationAngle = Math.atan2(mouseVector.x, mouseVector.y);
|
450 |
-
|
451 |
-
if (this.tank.turretGroup) {
|
452 |
-
this.tank.turretGroup.rotation.y = rotationAngle;
|
453 |
-
}
|
454 |
-
|
455 |
-
const tankPos = this.tank.getPosition();
|
456 |
-
const cameraDistance = 30;
|
457 |
-
const cameraHeight = 15;
|
458 |
-
const lookAtHeight = 5;
|
459 |
|
460 |
-
|
|
|
461 |
|
462 |
-
this.
|
463 |
-
|
464 |
-
|
465 |
-
tankPos.z - Math.cos(tankRotation) * cameraDistance
|
466 |
-
);
|
467 |
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
this.camera.lookAt(lookAtPoint);
|
475 |
}
|
476 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
477 |
createBuildings() {
|
478 |
const buildingTypes = [
|
479 |
{ width: 10, height: 30, depth: 10, color: 0x808080 },
|
@@ -574,23 +599,24 @@ class Game {
|
|
574 |
}
|
575 |
|
576 |
spawnEnemies() {
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
}
|
586 |
}
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
}
|
591 |
-
|
592 |
-
|
593 |
-
|
|
|
|
|
594 |
|
595 |
getValidEnemySpawnPosition() {
|
596 |
const margin = 20;
|
|
|
102 |
return bullet;
|
103 |
}
|
104 |
|
105 |
+
update(mouseX, mouseY) {
|
106 |
+
if (!this.body || !this.turretGroup) return;
|
107 |
+
|
108 |
+
for (let i = this.bullets.length - 1; i >= 0; i--) {
|
109 |
+
const bullet = this.bullets[i];
|
110 |
+
bullet.position.add(bullet.velocity);
|
111 |
+
|
112 |
+
if (Math.abs(bullet.position.x) > MAP_SIZE/2 ||
|
113 |
+
Math.abs(bullet.position.z) > MAP_SIZE/2) {
|
114 |
+
scene.remove(bullet);
|
115 |
+
this.bullets.splice(i, 1);
|
|
|
116 |
}
|
117 |
}
|
118 |
|
119 |
+
// ํฌํ ํ์ ๋ฐฉํฅ ์์
|
120 |
+
if (this.turretGroup) {
|
121 |
+
// ๋ง์ฐ์ค ์์ง์์ ๊ธฐ๋ฐ์ผ๋ก ํ ํ์ ๊ฐ ๊ณ์ฐ
|
122 |
+
const rotationAngle = -Math.atan2(mouseX, mouseY); // ๋ถํธ๋ฅผ ๋ฐ๋๋ก ํ์ฌ ํ์ ๋ฐฉํฅ ๋ณ๊ฒฝ
|
123 |
+
this.turretRotation = rotationAngle;
|
124 |
+
this.turretGroup.rotation.y = this.turretRotation;
|
125 |
+
}
|
126 |
+
}
|
127 |
+
|
128 |
move(direction) {
|
129 |
if (!this.body) return;
|
130 |
|
|
|
326 |
}
|
327 |
|
328 |
async initialize() {
|
329 |
+
try {
|
330 |
+
// ์๊ฐ ํจ๊ณผ ์ถ๊ฐ
|
331 |
+
this.scene.fog = new THREE.Fog(0xffd700, 1, 100);
|
332 |
+
this.scene.background = new THREE.Color(0xffd700); // ์ฌ๋ง ํ๋์
|
333 |
+
|
334 |
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.7); // ์ฌ๋ง ํ๊ฒฝ์ ๋ง๊ฒ ๋ฐ๊ธฐ ์ฆ๊ฐ
|
335 |
+
this.scene.add(ambientLight);
|
336 |
+
|
337 |
+
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0); // ์ฌ๋ง์ ๊ฐํ ํ๋น
|
338 |
+
directionalLight.position.set(50, 50, 50);
|
339 |
+
directionalLight.castShadow = true;
|
340 |
+
directionalLight.shadow.mapSize.width = 2048;
|
341 |
+
directionalLight.shadow.mapSize.height = 2048;
|
342 |
+
this.scene.add(directionalLight);
|
343 |
+
|
344 |
+
// ์ฌ๋ง ์งํ ์์ฑ
|
345 |
+
const groundGeometry = new THREE.PlaneGeometry(MAP_SIZE, MAP_SIZE, 100, 100);
|
346 |
+
const groundTexture = new THREE.TextureLoader().load('/textures/sand.jpg');
|
347 |
+
groundTexture.wrapS = groundTexture.wrapT = THREE.RepeatWrapping;
|
348 |
+
groundTexture.repeat.set(50, 50);
|
349 |
+
|
350 |
+
const groundMaterial = new THREE.MeshStandardMaterial({
|
351 |
+
map: groundTexture,
|
352 |
+
roughness: 1.0,
|
353 |
+
metalness: 0.0,
|
354 |
+
color: 0xd2b48c // ์ฌ๋ง์
|
355 |
+
});
|
|
|
|
|
356 |
|
357 |
+
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
358 |
+
ground.rotation.x = -Math.PI / 2;
|
359 |
+
ground.receiveShadow = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
360 |
|
361 |
+
// ์งํ์ ๋๋ฎ์ด ๋ณํ ์ถ๊ฐ
|
362 |
+
const vertices = ground.geometry.attributes.position.array;
|
363 |
+
for (let i = 0; i < vertices.length; i += 3) {
|
364 |
+
vertices[i + 1] = Math.sin(vertices[i] / 20) * Math.cos(vertices[i + 2] / 20) * 2;
|
365 |
+
}
|
366 |
+
ground.geometry.attributes.position.needsUpdate = true;
|
367 |
+
ground.geometry.computeVertexNormals();
|
368 |
|
369 |
+
this.scene.add(ground);
|
|
|
|
|
370 |
|
371 |
+
await this.createBuildings();
|
372 |
+
await this.tank.initialize(this.scene, this.loader);
|
373 |
+
|
374 |
+
if (!this.tank.isLoaded) {
|
375 |
+
throw new Error('Tank loading failed');
|
376 |
}
|
377 |
+
|
378 |
+
const tankPosition = this.tank.getPosition();
|
379 |
+
this.camera.position.set(
|
380 |
+
tankPosition.x,
|
381 |
+
tankPosition.y + 15,
|
382 |
+
tankPosition.z - 30
|
383 |
+
);
|
384 |
+
this.camera.lookAt(tankPosition);
|
385 |
+
|
386 |
+
this.isLoading = false;
|
387 |
+
document.getElementById('loading').style.display = 'none';
|
388 |
+
|
389 |
+
this.animate();
|
390 |
+
this.spawnEnemies();
|
391 |
+
this.startGameTimer();
|
392 |
+
|
393 |
+
} catch (error) {
|
394 |
+
console.error('Game initialization error:', error);
|
395 |
+
this.handleLoadingError();
|
396 |
}
|
397 |
+
}
|
398 |
|
399 |
setupEventListeners() {
|
400 |
document.addEventListener('keydown', (event) => {
|
|
|
449 |
}
|
450 |
|
451 |
handleMovement() {
|
452 |
+
if (!this.tank.isLoaded || this.isGameOver) return;
|
453 |
+
|
454 |
+
const direction = new THREE.Vector3();
|
455 |
+
|
456 |
+
if (this.keys.forward) direction.z += 1;
|
457 |
+
if (this.keys.backward) direction.z -= 1;
|
458 |
+
if (this.keys.left) direction.x -= 1;
|
459 |
+
if (this.keys.right) direction.x += 1;
|
460 |
+
|
461 |
+
if (direction.length() > 0) {
|
462 |
+
direction.normalize();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
463 |
|
464 |
+
if (this.keys.left) this.tank.rotate(-1);
|
465 |
+
if (this.keys.right) this.tank.rotate(1);
|
466 |
|
467 |
+
direction.applyEuler(this.tank.body.rotation);
|
468 |
+
this.tank.move(direction);
|
469 |
+
}
|
|
|
|
|
470 |
|
471 |
+
// ํฌํ ํ์ ์
๋ฐ์ดํธ
|
472 |
+
const mouseVector = new THREE.Vector2(this.mouse.x, -this.mouse.y);
|
473 |
+
const rotationAngle = -Math.atan2(mouseVector.x, mouseVector.y); // ๋ถํธ ๋ณ๊ฒฝ
|
474 |
+
|
475 |
+
if (this.tank.turretGroup) {
|
476 |
+
this.tank.turretGroup.rotation.y = rotationAngle;
|
|
|
477 |
}
|
478 |
|
479 |
+
// ์นด๋ฉ๋ผ ์์น ์
๋ฐ์ดํธ
|
480 |
+
const tankPos = this.tank.getPosition();
|
481 |
+
const cameraDistance = 30;
|
482 |
+
const cameraHeight = 15;
|
483 |
+
const lookAtHeight = 5;
|
484 |
+
|
485 |
+
const tankRotation = this.tank.body.rotation.y;
|
486 |
+
|
487 |
+
this.camera.position.set(
|
488 |
+
tankPos.x - Math.sin(tankRotation) * cameraDistance,
|
489 |
+
tankPos.y + cameraHeight,
|
490 |
+
tankPos.z - Math.cos(tankRotation) * cameraDistance
|
491 |
+
);
|
492 |
+
|
493 |
+
const lookAtPoint = new THREE.Vector3(
|
494 |
+
tankPos.x + Math.sin(tankRotation) * 10,
|
495 |
+
tankPos.y + lookAtHeight,
|
496 |
+
tankPos.z + Math.cos(tankRotation) * 10
|
497 |
+
);
|
498 |
+
|
499 |
+
this.camera.lookAt(lookAtPoint);
|
500 |
+
}
|
501 |
+
|
502 |
createBuildings() {
|
503 |
const buildingTypes = [
|
504 |
{ width: 10, height: 30, depth: 10, color: 0x808080 },
|
|
|
599 |
}
|
600 |
|
601 |
spawnEnemies() {
|
602 |
+
const spawnEnemy = () => {
|
603 |
+
if (this.enemies.length < 3 && !this.isGameOver) { // ์ต๋ 3๋๋ก ์ ํ
|
604 |
+
const position = this.getValidEnemySpawnPosition();
|
605 |
+
if (position) {
|
606 |
+
const type = Math.random() < 0.7 ? 'tank' : 'heavy';
|
607 |
+
const enemy = new Enemy(this.scene, position, type);
|
608 |
+
enemy.initialize(this.loader);
|
609 |
+
this.enemies.push(enemy);
|
|
|
610 |
}
|
611 |
+
}
|
612 |
+
if (!this.isGameOver) {
|
613 |
+
setTimeout(spawnEnemy, 10000); // 10์ด๋ง๋ค ์คํฐ
|
614 |
+
}
|
615 |
+
};
|
616 |
+
|
617 |
+
spawnEnemy();
|
618 |
+
}
|
619 |
+
|
620 |
|
621 |
getValidEnemySpawnPosition() {
|
622 |
const margin = 20;
|