Spaces:
Running
Running
cutechicken
commited on
Commit
โข
d71ef0b
1
Parent(s):
96cd899
Update game.js
Browse files
game.js
CHANGED
@@ -553,330 +553,97 @@ class Enemy {
|
|
553 |
this.isLoaded = false;
|
554 |
this.moveSpeed = type === 'tank' ? ENEMY_MOVE_SPEED : ENEMY_MOVE_SPEED * 0.7;
|
555 |
}
|
556 |
-
createCollisionBox() {
|
557 |
-
const dimensions = new THREE.Vector3(4, 2, 7);
|
558 |
-
const geometry = new THREE.BoxGeometry(dimensions.x, dimensions.y, dimensions.z);
|
559 |
-
const material = new THREE.MeshBasicMaterial({
|
560 |
-
color: 0xff0000,
|
561 |
-
wireframe: true,
|
562 |
-
visible: false
|
563 |
-
});
|
564 |
-
|
565 |
-
this.collisionBox = new THREE.Mesh(geometry, material);
|
566 |
-
this.mesh.add(this.collisionBox);
|
567 |
-
this.collisionBox.position.set(0, dimensions.y / 2, 0);
|
568 |
-
}
|
569 |
|
570 |
async initialize(loader) {
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
const flameMaterial = new THREE.MeshBasicMaterial({
|
583 |
-
color: 0xffa500, // ๋
ธ๋์์ผ๋ก ๋ณ๊ฒฝ
|
584 |
-
transparent: true,
|
585 |
-
opacity: 0.8
|
586 |
-
});
|
587 |
-
const flame = new THREE.Mesh(flameGeometry, flameMaterial);
|
588 |
-
flame.scale.set(2, 2, 3);
|
589 |
-
flashGroup.add(flame);
|
590 |
-
|
591 |
-
// ์ฐ๊ธฐ ํจ๊ณผ ํฌ๊ธฐ ์ฆ๊ฐ
|
592 |
-
const smokeGeometry = new THREE.SphereGeometry(0.8, 8, 8);
|
593 |
-
const smokeMaterial = new THREE.MeshBasicMaterial({
|
594 |
-
color: 0x555555,
|
595 |
-
transparent: true,
|
596 |
-
opacity: 0.5
|
597 |
-
});
|
598 |
-
|
599 |
-
for (let i = 0; i < 5; i++) {
|
600 |
-
const smoke = new THREE.Mesh(smokeGeometry, smokeMaterial);
|
601 |
-
smoke.position.set(
|
602 |
-
Math.random() * 1 - 0.5,
|
603 |
-
Math.random() * 1 - 0.5,
|
604 |
-
-1 - Math.random()
|
605 |
-
);
|
606 |
-
smoke.scale.set(1.5, 1.5, 1.5);
|
607 |
-
flashGroup.add(smoke);
|
608 |
}
|
609 |
|
610 |
-
|
611 |
-
|
612 |
-
const muzzlePosition = new THREE.Vector3();
|
613 |
-
const meshWorldQuaternion = new THREE.Quaternion();
|
614 |
-
|
615 |
-
this.mesh.getWorldPosition(muzzlePosition);
|
616 |
-
this.mesh.getWorldQuaternion(meshWorldQuaternion);
|
617 |
-
|
618 |
-
muzzleOffset.applyQuaternion(meshWorldQuaternion);
|
619 |
-
muzzlePosition.add(muzzleOffset);
|
620 |
-
|
621 |
-
flashGroup.position.copy(muzzlePosition);
|
622 |
-
flashGroup.quaternion.copy(meshWorldQuaternion);
|
623 |
-
|
624 |
-
this.scene.add(flashGroup);
|
625 |
-
|
626 |
-
// ์ดํํธ ์ง์ ์๊ฐ ์ฆ๊ฐ
|
627 |
-
setTimeout(() => {
|
628 |
-
this.scene.remove(flashGroup);
|
629 |
-
}, 500);
|
630 |
-
}
|
631 |
-
|
632 |
-
async initialize(loader) {
|
633 |
-
try {
|
634 |
-
const modelPath = this.type === 'tank' ? '/models/t90.glb' : '/models/t90.glb';
|
635 |
-
const result = await loader.loadAsync(modelPath);
|
636 |
-
this.mesh = result.scene;
|
637 |
-
this.mesh.position.copy(this.position);
|
638 |
-
this.mesh.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
|
639 |
-
|
640 |
-
this.mesh.traverse((child) => {
|
641 |
if (child.isMesh) {
|
642 |
child.castShadow = true;
|
643 |
child.receiveShadow = true;
|
644 |
}
|
645 |
});
|
646 |
-
|
647 |
this.createCollisionBox();
|
648 |
this.scene.add(this.mesh);
|
649 |
-
this.isLoaded = true;
|
650 |
-
} catch (error) {
|
651 |
-
console.error('Error loading enemy model:', error);
|
652 |
-
this.isLoaded = false;
|
653 |
}
|
654 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
655 |
|
656 |
update(playerPosition) {
|
657 |
-
|
|
|
|
|
|
|
658 |
|
659 |
-
|
660 |
-
.
|
661 |
-
|
662 |
-
|
663 |
-
const distanceToPlayer = this.mesh.position.distanceTo(playerPosition);
|
664 |
-
const minDistance = 50;
|
665 |
-
|
666 |
-
// ์ด์ ์์น ์ ์ฅ
|
667 |
-
const previousPosition = this.mesh.position.clone();
|
668 |
-
|
669 |
-
if (distanceToPlayer > minDistance) {
|
670 |
-
const moveVector = direction.multiplyScalar(this.moveSpeed);
|
671 |
-
const newPosition = this.mesh.position.clone().add(moveVector);
|
672 |
-
|
673 |
-
// ์งํ ๋์ด ๊ฐ์ ธ์ค๊ธฐ
|
674 |
-
const heightAtNewPos = window.gameInstance.getHeightAtPosition(
|
675 |
-
newPosition.x,
|
676 |
-
newPosition.z
|
677 |
-
);
|
678 |
-
newPosition.y = heightAtNewPos + TANK_HEIGHT;
|
679 |
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
// ์ฅ์ ๋ฌผ๊ณผ ์ถฉ๋ ์ฒดํฌ
|
685 |
-
const enemyBox = new THREE.Box3().setFromObject(this.mesh);
|
686 |
-
let hasCollision = false;
|
687 |
-
|
688 |
-
// ๋ชจ๋ ์ฅ์ ๋ฌผ์ ๋ํด ์ถฉ๋ ๊ฒ์ฌ
|
689 |
-
for (const obstacle of window.gameInstance.obstacles) {
|
690 |
-
const obstacleBox = new THREE.Box3().setFromObject(obstacle);
|
691 |
-
if (enemyBox.intersectsBox(obstacleBox)) {
|
692 |
-
hasCollision = true;
|
693 |
-
break;
|
694 |
-
}
|
695 |
-
}
|
696 |
-
|
697 |
-
// ๋ค๋ฅธ ์ ํฑํฌ์์ ์ถฉ๋ ๊ฒ์ฌ
|
698 |
-
if (!hasCollision) {
|
699 |
-
for (const otherEnemy of window.gameInstance.enemies) {
|
700 |
-
if (otherEnemy !== this && otherEnemy.mesh) {
|
701 |
-
const otherEnemyBox = new THREE.Box3().setFromObject(otherEnemy.mesh);
|
702 |
-
if (enemyBox.intersectsBox(otherEnemyBox)) {
|
703 |
-
hasCollision = true;
|
704 |
-
break;
|
705 |
-
}
|
706 |
-
}
|
707 |
-
}
|
708 |
-
}
|
709 |
-
|
710 |
-
// ๋งต ๊ฒฝ๊ณ ์ฒดํฌ
|
711 |
-
const mapBoundary = MAP_SIZE / 2;
|
712 |
-
if (Math.abs(newPosition.x) > mapBoundary ||
|
713 |
-
Math.abs(newPosition.z) > mapBoundary) {
|
714 |
-
hasCollision = true;
|
715 |
-
}
|
716 |
-
|
717 |
-
// ์ถฉ๋์ด ์์ผ๋ฉด ์ด์ ์์น๋ก ๋ณต๊ท, ์์ผ๋ฉด ์ ์์น ์ ์ง
|
718 |
-
if (hasCollision) {
|
719 |
-
this.mesh.position.copy(previousPosition);
|
720 |
-
|
721 |
-
// ์ถฉ๋ ์ ์ฐํ ๊ฒฝ๋ก ์๋
|
722 |
-
const alternateDirections = [
|
723 |
-
new THREE.Vector3(-direction.z, 0, direction.x), // ์ผ์ชฝ์ผ๋ก 90๋
|
724 |
-
new THREE.Vector3(direction.z, 0, -direction.x), // ์ค๋ฅธ์ชฝ์ผ๋ก 90๋
|
725 |
-
new THREE.Vector3(-direction.x, 0, -direction.z) // 180๋ ํ์
|
726 |
-
];
|
727 |
-
|
728 |
-
for (const altDirection of alternateDirections) {
|
729 |
-
const altMoveVector = altDirection.multiplyScalar(this.moveSpeed);
|
730 |
-
const altNewPosition = previousPosition.clone().add(altMoveVector);
|
731 |
-
|
732 |
-
this.mesh.position.copy(altNewPosition);
|
733 |
-
const altEnemyBox = new THREE.Box3().setFromObject(this.mesh);
|
734 |
-
|
735 |
-
let altHasCollision = false;
|
736 |
-
|
737 |
-
// ์๋ก์ด ๋ฐฉํฅ์ ๋ํ ์ถฉ๋ ๊ฒ์ฌ
|
738 |
-
for (const obstacle of window.gameInstance.obstacles) {
|
739 |
-
const obstacleBox = new THREE.Box3().setFromObject(obstacle);
|
740 |
-
if (altEnemyBox.intersectsBox(obstacleBox)) {
|
741 |
-
altHasCollision = true;
|
742 |
-
break;
|
743 |
-
}
|
744 |
-
}
|
745 |
-
|
746 |
-
if (!altHasCollision) {
|
747 |
-
// ์ฐํ ๊ฒฝ๋ก๊ฐ ๊ฐ๋ฅํ๋ฉด ๊ทธ ๋ฐฉํฅ์ผ๋ก ์ด๋
|
748 |
-
break;
|
749 |
-
} else {
|
750 |
-
// ์ฐํ๋ ๋ถ๊ฐ๋ฅํ๋ฉด ์ด์ ์์น๋ก ๋ณต๊ท
|
751 |
-
this.mesh.position.copy(previousPosition);
|
752 |
-
}
|
753 |
-
}
|
754 |
}
|
755 |
-
|
756 |
-
// ์งํ์ ๋ฐ๋ฅธ ๊ธฐ์ธ๊ธฐ ์กฐ์
|
757 |
-
const forwardVector = new THREE.Vector3(0, 0, 1).applyQuaternion(this.mesh.quaternion);
|
758 |
-
const rightVector = new THREE.Vector3(1, 0, 0).applyQuaternion(this.mesh.quaternion);
|
759 |
-
|
760 |
-
const frontHeight = window.gameInstance.getHeightAtPosition(
|
761 |
-
this.mesh.position.x + forwardVector.x,
|
762 |
-
this.mesh.position.z + forwardVector.z
|
763 |
-
);
|
764 |
-
const backHeight = window.gameInstance.getHeightAtPosition(
|
765 |
-
this.mesh.position.x - forwardVector.x,
|
766 |
-
this.mesh.position.z - forwardVector.z
|
767 |
-
);
|
768 |
-
const rightHeight = window.gameInstance.getHeightAtPosition(
|
769 |
-
this.mesh.position.x + rightVector.x,
|
770 |
-
this.mesh.position.z + rightVector.z
|
771 |
-
);
|
772 |
-
const leftHeight = window.gameInstance.getHeightAtPosition(
|
773 |
-
this.mesh.position.x - rightVector.x,
|
774 |
-
this.mesh.position.z - rightVector.z
|
775 |
-
);
|
776 |
-
|
777 |
-
const pitch = Math.atan2(frontHeight - backHeight, 2);
|
778 |
-
const roll = Math.atan2(rightHeight - leftHeight, 2);
|
779 |
-
|
780 |
-
// ํ์ฌ ํ์ ์ ์งํ๋ฉด์ ๊ธฐ์ธ๊ธฐ๋ง ์ ์ฉ
|
781 |
-
const currentRotation = this.mesh.rotation.y;
|
782 |
-
this.mesh.rotation.set(pitch, currentRotation, roll);
|
783 |
}
|
784 |
-
|
785 |
-
// ํ๋ ๏ฟฝ๏ฟฝ์ด๋ฅผ ํฅํด ํฌํ ํ์
|
786 |
-
this.mesh.lookAt(playerPosition);
|
787 |
-
|
788 |
-
// ์ด์ ์
๋ฐ์ดํธ
|
789 |
-
if (this.bullets) {
|
790 |
-
for (let i = this.bullets.length - 1; i >= 0; i--) {
|
791 |
-
const bullet = this.bullets[i];
|
792 |
-
bullet.position.add(bullet.velocity);
|
793 |
-
|
794 |
-
// ์ด์์ด ๋งต ๋ฐ์ผ๋ก ๋๊ฐ๊ฑฐ๋ ์ฅ์ ๋ฌผ๊ณผ ์ถฉ๋ํ๋ฉด ์ ๊ฑฐ
|
795 |
-
if (Math.abs(bullet.position.x) > MAP_SIZE / 2 ||
|
796 |
-
Math.abs(bullet.position.z) > MAP_SIZE / 2) {
|
797 |
-
this.scene.remove(bullet);
|
798 |
-
this.bullets.splice(i, 1);
|
799 |
-
continue;
|
800 |
-
}
|
801 |
-
|
802 |
-
// ์ด์๊ณผ ์ฅ์ ๋ฌผ ์ถฉ๋ ์ฒดํฌ
|
803 |
-
const bulletBox = new THREE.Box3().setFromObject(bullet);
|
804 |
-
for (const obstacle of window.gameInstance.obstacles) {
|
805 |
-
const obstacleBox = new THREE.Box3().setFromObject(obstacle);
|
806 |
-
if (bulletBox.intersectsBox(obstacleBox)) {
|
807 |
-
this.scene.remove(bullet);
|
808 |
-
this.bullets.splice(i, 1);
|
809 |
-
break;
|
810 |
-
}
|
811 |
-
}
|
812 |
-
}
|
813 |
-
}
|
814 |
-
}
|
815 |
-
|
816 |
|
817 |
shoot(playerPosition) {
|
818 |
-
|
819 |
-
const attackInterval = this.type === 'tank' ?
|
820 |
-
ENEMY_CONFIG.ATTACK_INTERVAL :
|
821 |
-
ENEMY_CONFIG.ATTACK_INTERVAL * 1.5;
|
822 |
|
823 |
-
|
|
|
|
|
|
|
824 |
|
825 |
-
|
826 |
-
|
827 |
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
|
|
832 |
|
833 |
-
|
834 |
-
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
|
|
|
|
841 |
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
bullet.position.copy(muzzlePosition);
|
850 |
-
|
851 |
-
// ํฌํ ํ์ ์ค์
|
852 |
-
bullet.quaternion.copy(this.mesh.quaternion);
|
853 |
-
|
854 |
-
const direction = new THREE.Vector3()
|
855 |
-
.subVectors(playerPosition, muzzlePosition)
|
856 |
-
.normalize();
|
857 |
-
|
858 |
-
const bulletSpeed = this.type === 'tank' ?
|
859 |
-
ENEMY_CONFIG.BULLET_SPEED :
|
860 |
-
ENEMY_CONFIG.BULLET_SPEED * 0.8;
|
861 |
-
|
862 |
-
bullet.velocity = direction.multiplyScalar(bulletSpeed);
|
863 |
-
|
864 |
-
// ํฌํ ํธ๋ ์ผ ํจ๊ณผ ์ถ๊ฐ
|
865 |
-
const trailGeometry = new THREE.CylinderGeometry(0.1, 0.1, 1, 8);
|
866 |
-
const trailMaterial = new THREE.MeshBasicMaterial({
|
867 |
-
color: 0xff4444,
|
868 |
-
transparent: true,
|
869 |
-
opacity: 0.5
|
870 |
-
});
|
871 |
-
|
872 |
-
const trail = new THREE.Mesh(trailGeometry, trailMaterial);
|
873 |
-
trail.position.z = -1;
|
874 |
-
bullet.add(trail);
|
875 |
-
|
876 |
-
this.scene.add(bullet);
|
877 |
-
this.bullets.push(bullet);
|
878 |
-
this.lastAttackTime = currentTime;
|
879 |
-
}
|
880 |
|
881 |
takeDamage(damage) {
|
882 |
this.health -= damage;
|
|
|
553 |
this.isLoaded = false;
|
554 |
this.moveSpeed = type === 'tank' ? ENEMY_MOVE_SPEED : ENEMY_MOVE_SPEED * 0.7;
|
555 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
556 |
|
557 |
async initialize(loader) {
|
558 |
+
try {
|
559 |
+
const result = await loader.loadAsync('/models/t90.glb');
|
560 |
+
this.mesh = result.scene;
|
561 |
+
this.mesh.position.copy(this.position);
|
562 |
+
this.mesh.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
|
563 |
+
this.setupMesh();
|
564 |
+
this.isLoaded = true;
|
565 |
+
} catch (error) {
|
566 |
+
console.error('Error loading enemy model:', error);
|
567 |
+
this.isLoaded = false;
|
568 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
569 |
}
|
570 |
|
571 |
+
setupMesh() {
|
572 |
+
this.mesh.traverse(child => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
573 |
if (child.isMesh) {
|
574 |
child.castShadow = true;
|
575 |
child.receiveShadow = true;
|
576 |
}
|
577 |
});
|
|
|
578 |
this.createCollisionBox();
|
579 |
this.scene.add(this.mesh);
|
|
|
|
|
|
|
|
|
580 |
}
|
581 |
+
|
582 |
+
createCollisionBox() {
|
583 |
+
const geometry = new THREE.BoxGeometry(4, 2, 7);
|
584 |
+
const material = new THREE.MeshBasicMaterial({
|
585 |
+
color: 0xff0000,
|
586 |
+
wireframe: true,
|
587 |
+
visible: false
|
588 |
+
});
|
589 |
+
this.collisionBox = new THREE.Mesh(geometry, material);
|
590 |
+
this.mesh.add(this.collisionBox);
|
591 |
+
this.collisionBox.position.set(0, 1, 0);
|
592 |
+
}
|
593 |
|
594 |
update(playerPosition) {
|
595 |
+
if (!this.isLoaded) return;
|
596 |
+
this.moveTowardsPlayer(playerPosition);
|
597 |
+
this.updateBullets();
|
598 |
+
}
|
599 |
|
600 |
+
moveTowardsPlayer(playerPosition) {
|
601 |
+
const direction = new THREE.Vector3()
|
602 |
+
.subVectors(playerPosition, this.mesh.position)
|
603 |
+
.normalize();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
604 |
|
605 |
+
const distanceToPlayer = this.mesh.position.distanceTo(playerPosition);
|
606 |
+
if (distanceToPlayer > 50) {
|
607 |
+
this.tryMove(direction);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
608 |
}
|
609 |
+
this.mesh.lookAt(playerPosition);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
610 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
611 |
|
612 |
shoot(playerPosition) {
|
613 |
+
if (Date.now() - this.lastAttackTime < ENEMY_CONFIG.ATTACK_INTERVAL) return;
|
|
|
|
|
|
|
614 |
|
615 |
+
const bullet = this.createBullet(playerPosition);
|
616 |
+
this.scene.add(bullet);
|
617 |
+
this.bullets.push(bullet);
|
618 |
+
this.lastAttackTime = Date.now();
|
619 |
|
620 |
+
new Audio('sounds/mbtfire5.ogg').play();
|
621 |
+
}
|
622 |
|
623 |
+
createBullet(targetPosition) {
|
624 |
+
const bullet = new THREE.Mesh(
|
625 |
+
new THREE.CylinderGeometry(0.2, 0.2, 2, 8),
|
626 |
+
new THREE.MeshBasicMaterial({ color: 0xff0000 })
|
627 |
+
);
|
628 |
|
629 |
+
const muzzlePosition = this.getMuzzlePosition();
|
630 |
+
bullet.position.copy(muzzlePosition);
|
631 |
+
|
632 |
+
const direction = new THREE.Vector3()
|
633 |
+
.subVectors(targetPosition, muzzlePosition)
|
634 |
+
.normalize();
|
635 |
+
|
636 |
+
bullet.velocity = direction.multiplyScalar(ENEMY_CONFIG.BULLET_SPEED);
|
637 |
+
return bullet;
|
638 |
+
}
|
639 |
|
640 |
+
getMuzzlePosition() {
|
641 |
+
const muzzleOffset = new THREE.Vector3(0, 0.5, 4);
|
642 |
+
const position = new THREE.Vector3();
|
643 |
+
this.mesh.getWorldPosition(position);
|
644 |
+
muzzleOffset.applyQuaternion(this.mesh.quaternion);
|
645 |
+
return position.add(muzzleOffset);
|
646 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
647 |
|
648 |
takeDamage(damage) {
|
649 |
this.health -= damage;
|