Spaces:
Running
Running
cutechicken
commited on
Commit
โข
080df83
1
Parent(s):
49edc42
Update game.js
Browse files
game.js
CHANGED
@@ -41,41 +41,48 @@ class TankPlayer {
|
|
41 |
}
|
42 |
|
43 |
async initialize(scene, loader) {
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
});
|
69 |
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
}
|
75 |
-
|
76 |
-
this.isLoaded = false;
|
77 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
}
|
|
|
79 |
|
80 |
shoot(scene) {
|
81 |
if (this.isReloading || this.ammo <= 0) return null;
|
@@ -488,73 +495,91 @@ class Game {
|
|
488 |
}
|
489 |
|
490 |
async initialize() {
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
517 |
-
ground.rotation.x = -Math.PI / 2;
|
518 |
-
ground.receiveShadow = true;
|
519 |
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
for (let i = 0; i < vertices.length; i += 3) {
|
524 |
-
vertices[i + 2] = Math.sin(vertices[i] * 0.01) * Math.cos(vertices[i + 1] * 0.01) * 20;
|
525 |
-
}
|
526 |
|
527 |
-
|
528 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
529 |
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
throw new Error('Tank loading failed');
|
536 |
-
}
|
537 |
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
tankPosition.z - 30
|
543 |
-
);
|
544 |
-
this.camera.lookAt(tankPosition);
|
545 |
-
|
546 |
-
this.isLoading = false;
|
547 |
-
document.getElementById('loading').style.display = 'none';
|
548 |
-
|
549 |
-
this.animate();
|
550 |
-
this.spawnEnemies();
|
551 |
-
this.startGameTimer();
|
552 |
|
553 |
-
|
554 |
-
|
555 |
-
|
|
|
|
|
|
|
|
|
556 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
557 |
}
|
|
|
558 |
|
559 |
// ๋ ์ด๋ ์
๋ฐ์ดํธ ๋ฉ์๋ ์ถ๊ฐ
|
560 |
updateRadar() {
|
@@ -669,6 +694,90 @@ class Game {
|
|
669 |
}
|
670 |
}
|
671 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
672 |
setupEventListeners() {
|
673 |
document.addEventListener('keydown', (event) => {
|
674 |
if (this.isLoading || this.isGameOver) return;
|
@@ -886,37 +995,48 @@ class Game {
|
|
886 |
}
|
887 |
|
888 |
getValidEnemySpawnPosition() {
|
889 |
-
|
890 |
-
|
891 |
-
|
892 |
-
|
893 |
-
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
|
898 |
-
|
899 |
-
|
|
|
900 |
|
901 |
-
|
902 |
-
|
903 |
|
904 |
-
|
905 |
-
|
906 |
-
|
907 |
-
|
908 |
-
|
909 |
-
|
910 |
-
|
911 |
-
|
|
|
|
|
|
|
|
|
912 |
|
913 |
-
|
914 |
|
915 |
-
|
916 |
-
|
|
|
|
|
|
|
917 |
|
918 |
-
|
919 |
-
|
|
|
|
|
|
|
920 |
updateParticles() {
|
921 |
for (let i = this.particles.length - 1; i >= 0; i--) {
|
922 |
const particle = this.particles[i];
|
|
|
41 |
}
|
42 |
|
43 |
async initialize(scene, loader) {
|
44 |
+
try {
|
45 |
+
const bodyResult = await loader.loadAsync('/models/abramsBody.glb');
|
46 |
+
this.body = bodyResult.scene;
|
47 |
+
|
48 |
+
const turretResult = await loader.loadAsync('/models/abramsTurret.glb');
|
49 |
+
this.turret = turretResult.scene;
|
50 |
+
|
51 |
+
this.turretGroup.position.y = 0.2;
|
52 |
+
this.turretGroup.add(this.turret);
|
53 |
+
this.body.add(this.turretGroup);
|
54 |
+
|
55 |
+
this.body.traverse((child) => {
|
56 |
+
if (child.isMesh) {
|
57 |
+
child.castShadow = true;
|
58 |
+
child.receiveShadow = true;
|
59 |
+
}
|
60 |
+
});
|
61 |
+
|
62 |
+
this.turret.traverse((child) => {
|
63 |
+
if (child.isMesh) {
|
64 |
+
child.castShadow = true;
|
65 |
+
child.receiveShadow = true;
|
66 |
+
}
|
67 |
+
});
|
|
|
68 |
|
69 |
+
// ์ฌ๊ธฐ์ ์ ํจํ ์คํฐ ์์น๋ฅผ ์ฐพ์ ์ ์ฉ
|
70 |
+
if (window.gameInstance) {
|
71 |
+
const spawnPos = window.gameInstance.findValidSpawnPosition();
|
72 |
+
this.body.position.copy(spawnPos);
|
73 |
+
} else {
|
74 |
+
this.body.position.copy(this.position);
|
|
|
75 |
}
|
76 |
+
|
77 |
+
scene.add(this.body);
|
78 |
+
this.isLoaded = true;
|
79 |
+
this.updateAmmoDisplay();
|
80 |
+
|
81 |
+
} catch (error) {
|
82 |
+
console.error('Error loading tank models:', error);
|
83 |
+
this.isLoaded = false;
|
84 |
}
|
85 |
+
}
|
86 |
|
87 |
shoot(scene) {
|
88 |
if (this.isReloading || this.ammo <= 0) return null;
|
|
|
495 |
}
|
496 |
|
497 |
async initialize() {
|
498 |
+
try {
|
499 |
+
// ์๊ฐ ํจ๊ณผ ์ ๊ฑฐ
|
500 |
+
this.scene.fog = null;
|
501 |
+
this.scene.background = new THREE.Color(0x87CEEB);
|
502 |
+
|
503 |
+
// ์ฃผ๋ณ๊ด ์ค์ - ๋ ๋ฐ๊ฒ
|
504 |
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
505 |
+
this.scene.add(ambientLight);
|
506 |
+
|
507 |
+
// ํ์๊ด ์ค์
|
508 |
+
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
509 |
+
directionalLight.position.set(100, 100, 50);
|
510 |
+
directionalLight.castShadow = true;
|
511 |
+
directionalLight.shadow.mapSize.width = 1024;
|
512 |
+
directionalLight.shadow.mapSize.height = 1024;
|
513 |
+
this.scene.add(directionalLight);
|
514 |
+
|
515 |
+
// ์งํ ์์ฑ ์์
|
516 |
+
const groundGeometry = new THREE.PlaneGeometry(MAP_SIZE, MAP_SIZE, 100, 100);
|
517 |
+
const groundMaterial = new THREE.MeshStandardMaterial({
|
518 |
+
color: 0xD2B48C,
|
519 |
+
roughness: 0.8,
|
520 |
+
metalness: 0.2
|
521 |
+
});
|
|
|
|
|
|
|
|
|
522 |
|
523 |
+
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
524 |
+
ground.rotation.x = -Math.PI / 2;
|
525 |
+
ground.receiveShadow = true;
|
|
|
|
|
|
|
526 |
|
527 |
+
// ๋ ์๋งํ ์งํ ์์ฑ
|
528 |
+
const vertices = ground.geometry.attributes.position.array;
|
529 |
+
const heightScale = 10; // ๋์ด ์ค์ผ์ผ ๊ฐ์
|
530 |
+
const frequency = 0.005; // ์ฃผํ์ ๊ฐ์๋ก ๋ ์๋งํ ๊ฒฝ์ฌ ์์ฑ
|
531 |
+
|
532 |
+
for (let i = 0; i < vertices.length; i += 3) {
|
533 |
+
const x = vertices[i];
|
534 |
+
const y = vertices[i + 1];
|
535 |
+
// Perlin ๋
ธ์ด์ฆ์ ์ ์ฌํ ํจ๊ณผ๋ฅผ ๋ด๋ ์์ ๋ ์์
|
536 |
+
vertices[i + 2] =
|
537 |
+
(Math.sin(x * frequency) * Math.cos(y * frequency) * heightScale) +
|
538 |
+
(Math.sin(x * frequency * 2) * Math.cos(y * frequency * 2) * heightScale * 0.5);
|
539 |
|
540 |
+
// ๋งต ๊ฐ์ฅ์๋ฆฌ๋ก ๊ฐ์๋ก ๋์ด๋ฅผ ์ ์ง์ ์ผ๋ก ์ค์
|
541 |
+
const distanceFromCenter = Math.sqrt(x * x + y * y) / (MAP_SIZE * 0.5);
|
542 |
+
const edgeFactor = Math.max(0, 1 - distanceFromCenter);
|
543 |
+
vertices[i + 2] *= edgeFactor;
|
544 |
+
}
|
|
|
|
|
545 |
|
546 |
+
ground.geometry.attributes.position.needsUpdate = true;
|
547 |
+
ground.geometry.computeVertexNormals();
|
548 |
+
this.ground = ground; // ์งํ ์ฐธ์กฐ ์ ์ฅ
|
549 |
+
this.scene.add(ground);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
550 |
|
551 |
+
// ์ฌ๋ง ์ฅ์ ์ถ๊ฐ
|
552 |
+
await this.addDesertDecorations();
|
553 |
+
|
554 |
+
// ํฑํฌ ์ด๊ธฐํ
|
555 |
+
await this.tank.initialize(this.scene, this.loader);
|
556 |
+
if (!this.tank.isLoaded) {
|
557 |
+
throw new Error('Tank loading failed');
|
558 |
}
|
559 |
+
|
560 |
+
// ์นด๋ฉ๋ผ ์ด๊ธฐ ์์น ์ค์
|
561 |
+
const tankPosition = this.tank.getPosition();
|
562 |
+
this.camera.position.set(
|
563 |
+
tankPosition.x,
|
564 |
+
tankPosition.y + 15,
|
565 |
+
tankPosition.z - 30
|
566 |
+
);
|
567 |
+
this.camera.lookAt(tankPosition);
|
568 |
+
|
569 |
+
// ๋ก๋ฉ ์๋ฃ
|
570 |
+
this.isLoading = false;
|
571 |
+
document.getElementById('loading').style.display = 'none';
|
572 |
+
|
573 |
+
// ๊ฒ์ ์์
|
574 |
+
this.animate();
|
575 |
+
this.spawnEnemies();
|
576 |
+
this.startGameTimer();
|
577 |
+
|
578 |
+
} catch (error) {
|
579 |
+
console.error('Game initialization error:', error);
|
580 |
+
this.handleLoadingError();
|
581 |
}
|
582 |
+
}
|
583 |
|
584 |
// ๋ ์ด๋ ์
๋ฐ์ดํธ ๋ฉ์๋ ์ถ๊ฐ
|
585 |
updateRadar() {
|
|
|
694 |
}
|
695 |
}
|
696 |
|
697 |
+
getHeightAtPosition(x, z) {
|
698 |
+
if (!this.ground) return 0;
|
699 |
+
|
700 |
+
// ์งํ์ ์ ์ ๋ฐ์ดํฐ
|
701 |
+
const vertices = this.ground.geometry.attributes.position.array;
|
702 |
+
const segmentsX = Math.sqrt(vertices.length / 3) - 1;
|
703 |
+
const segmentsZ = segmentsX;
|
704 |
+
|
705 |
+
// ๋งต ์ขํ๋ฅผ ์งํ ๊ฒฉ์ ์ขํ๋ก ๋ณํ
|
706 |
+
const gridX = ((x + MAP_SIZE / 2) / MAP_SIZE) * segmentsX;
|
707 |
+
const gridZ = ((z + MAP_SIZE / 2) / MAP_SIZE) * segmentsZ;
|
708 |
+
|
709 |
+
// ๊ฐ์ฅ ๊ฐ๊น์ด ๊ฒฉ์์ ์ฐพ๊ธฐ
|
710 |
+
const x1 = Math.floor(gridX);
|
711 |
+
const z1 = Math.floor(gridZ);
|
712 |
+
const x2 = Math.min(x1 + 1, segmentsX);
|
713 |
+
const z2 = Math.min(z1 + 1, segmentsZ);
|
714 |
+
|
715 |
+
// ๊ฒฉ์์ ๋ค์ ๋์ด ๊ฐ์ ธ์ค๊ธฐ
|
716 |
+
const getHeight = (x, z) => {
|
717 |
+
if (x < 0 || x > segmentsX || z < 0 || z > segmentsZ) return 0;
|
718 |
+
const index = (z * (segmentsX + 1) + x) * 3 + 2;
|
719 |
+
return vertices[index];
|
720 |
+
};
|
721 |
+
|
722 |
+
const h11 = getHeight(x1, z1);
|
723 |
+
const h21 = getHeight(x2, z1);
|
724 |
+
const h12 = getHeight(x1, z2);
|
725 |
+
const h22 = getHeight(x2, z2);
|
726 |
+
|
727 |
+
// ๋ณด๊ฐ์ผ๋ก ์ ํํ ๋์ด ๊ณ์ฐ
|
728 |
+
const fx = gridX - x1;
|
729 |
+
const fz = gridZ - z1;
|
730 |
+
|
731 |
+
const h1 = h11 * (1 - fx) + h21 * fx;
|
732 |
+
const h2 = h12 * (1 - fx) + h22 * fx;
|
733 |
+
|
734 |
+
return h1 * (1 - fz) + h2 * fz;
|
735 |
+
}
|
736 |
+
|
737 |
+
findValidSpawnPosition() {
|
738 |
+
const margin = 50;
|
739 |
+
let position;
|
740 |
+
let attempts = 0;
|
741 |
+
const maxAttempts = 50;
|
742 |
+
const maxSlope = 0.3; // ์ต๋ ํ์ฉ ๊ฒฝ์ฌ
|
743 |
+
|
744 |
+
while (attempts < maxAttempts) {
|
745 |
+
position = new THREE.Vector3(
|
746 |
+
(Math.random() - 0.5) * (MAP_SIZE - margin * 2),
|
747 |
+
0,
|
748 |
+
(Math.random() - 0.5) * (MAP_SIZE - margin * 2)
|
749 |
+
);
|
750 |
+
|
751 |
+
// ํ์ฌ ์์น์ ๋์ด ๊ฐ์ ธ์ค๊ธฐ
|
752 |
+
const height = this.getHeightAtPosition(position.x, position.z);
|
753 |
+
position.y = height + TANK_HEIGHT;
|
754 |
+
|
755 |
+
// ์ฃผ๋ณ ์งํ์ ๊ฒฝ์ฌ ์ฒดํฌ
|
756 |
+
const checkPoints = [
|
757 |
+
{ x: position.x + 2, z: position.z },
|
758 |
+
{ x: position.x - 2, z: position.z },
|
759 |
+
{ x: position.x, z: position.z + 2 },
|
760 |
+
{ x: position.x, z: position.z - 2 }
|
761 |
+
];
|
762 |
+
|
763 |
+
const slopes = checkPoints.map(point => {
|
764 |
+
const pointHeight = this.getHeightAtPosition(point.x, point.z);
|
765 |
+
return Math.abs(pointHeight - height) / 2;
|
766 |
+
});
|
767 |
+
|
768 |
+
const maxCurrentSlope = Math.max(...slopes);
|
769 |
+
|
770 |
+
if (maxCurrentSlope <= maxSlope) {
|
771 |
+
return position;
|
772 |
+
}
|
773 |
+
|
774 |
+
attempts++;
|
775 |
+
}
|
776 |
+
|
777 |
+
// ์คํจ ์ ๊ธฐ๋ณธ ์์น ๋ฐํ
|
778 |
+
return new THREE.Vector3(0, TANK_HEIGHT, 0);
|
779 |
+
}
|
780 |
+
|
781 |
setupEventListeners() {
|
782 |
document.addEventListener('keydown', (event) => {
|
783 |
if (this.isLoading || this.isGameOver) return;
|
|
|
995 |
}
|
996 |
|
997 |
getValidEnemySpawnPosition() {
|
998 |
+
const margin = 50;
|
999 |
+
let position;
|
1000 |
+
let attempts = 0;
|
1001 |
+
const maxAttempts = 50;
|
1002 |
+
const maxSlope = 0.3;
|
1003 |
+
|
1004 |
+
do {
|
1005 |
+
position = new THREE.Vector3(
|
1006 |
+
(Math.random() - 0.5) * (MAP_SIZE - margin * 2),
|
1007 |
+
0,
|
1008 |
+
(Math.random() - 0.5) * (MAP_SIZE - margin * 2)
|
1009 |
+
);
|
1010 |
|
1011 |
+
const height = this.getHeightAtPosition(position.x, position.z);
|
1012 |
+
position.y = height + TANK_HEIGHT;
|
1013 |
|
1014 |
+
// ์ฃผ๋ณ ๊ฒฝ์ฌ ์ฒดํฌ
|
1015 |
+
const checkPoints = [
|
1016 |
+
{ x: position.x + 2, z: position.z },
|
1017 |
+
{ x: position.x - 2, z: position.z },
|
1018 |
+
{ x: position.x, z: position.z + 2 },
|
1019 |
+
{ x: position.x, z: position.z - 2 }
|
1020 |
+
];
|
1021 |
+
|
1022 |
+
const slopes = checkPoints.map(point => {
|
1023 |
+
const pointHeight = this.getHeightAtPosition(point.x, point.z);
|
1024 |
+
return Math.abs(pointHeight - height) / 2;
|
1025 |
+
});
|
1026 |
|
1027 |
+
const maxCurrentSlope = Math.max(...slopes);
|
1028 |
|
1029 |
+
// ํ๋ ์ด์ด์์ ๊ฑฐ๋ฆฌ ์ฒดํฌ
|
1030 |
+
const distanceToPlayer = position.distanceTo(this.tank.getPosition());
|
1031 |
+
if (distanceToPlayer > 100 && maxCurrentSlope <= maxSlope) {
|
1032 |
+
return position;
|
1033 |
+
}
|
1034 |
|
1035 |
+
attempts++;
|
1036 |
+
} while (attempts < maxAttempts);
|
1037 |
+
|
1038 |
+
return null;
|
1039 |
+
}
|
1040 |
updateParticles() {
|
1041 |
for (let i = this.particles.length - 1; i >= 0; i--) {
|
1042 |
const particle = this.particles[i];
|