cutechicken commited on
Commit
1413f15
โ€ข
1 Parent(s): eaa874e

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +190 -148
game.js CHANGED
@@ -572,200 +572,230 @@ class Enemy {
572
  constructor(scene, position, type = 'tank') {
573
  this.scene = scene;
574
  this.position = position;
575
- this.mesh = null;
576
  this.type = type;
577
  this.health = type === 'tank' ? 100 : 200;
578
  this.lastAttackTime = 0;
579
  this.bullets = [];
580
  this.isLoaded = false;
581
  this.moveSpeed = type === 'tank' ? ENEMY_MOVE_SPEED : ENEMY_MOVE_SPEED * 0.7;
 
 
 
 
 
582
  }
583
 
584
  async initialize(loader) {
585
  try {
586
- const modelPath = this.type === 'tank' ? '/models/enemy1.glb' : '/models/enemy4.glb';
587
- const result = await loader.loadAsync(modelPath);
588
- this.mesh = result.scene;
589
- this.mesh.position.copy(this.position);
590
- this.mesh.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
591
 
592
- this.mesh.traverse((child) => {
593
  if (child.isMesh) {
594
  child.castShadow = true;
595
  child.receiveShadow = true;
 
596
  }
597
  });
598
 
599
- this.scene.add(this.mesh);
600
  this.isLoaded = true;
601
  } catch (error) {
602
- console.error('Error loading enemy model:', error);
603
  this.isLoaded = false;
604
  }
605
  }
606
 
607
  update(playerPosition) {
608
- if (!this.mesh || !this.isLoaded) return;
609
 
610
- const direction = new THREE.Vector3()
611
- .subVectors(playerPosition, this.mesh.position)
612
- .normalize();
613
-
614
- const distanceToPlayer = this.mesh.position.distanceTo(playerPosition);
615
- const minDistance = 50;
616
-
617
- // ์ด์ „ ์œ„์น˜ ์ €์žฅ
618
- const previousPosition = this.mesh.position.clone();
619
-
620
- if (distanceToPlayer > minDistance) {
621
- const moveVector = direction.multiplyScalar(this.moveSpeed);
622
- const newPosition = this.mesh.position.clone().add(moveVector);
623
-
624
- // ์ง€ํ˜• ๋†’์ด ๊ฐ€์ ธ์˜ค๊ธฐ
625
- const heightAtNewPos = window.gameInstance.getHeightAtPosition(
626
- newPosition.x,
627
- newPosition.z
628
- );
629
- newPosition.y = heightAtNewPos + TANK_HEIGHT;
630
 
631
- // ์ž„์‹œ๋กœ ์œ„์น˜ ์ด๋™ํ•˜์—ฌ ์ถฉ๋Œ ์ฒดํฌ
632
- const originalPosition = this.mesh.position.clone();
633
- this.mesh.position.copy(newPosition);
634
 
635
- // ์žฅ์• ๋ฌผ๊ณผ ์ถฉ๋Œ ์ฒดํฌ
636
- const enemyBox = new THREE.Box3().setFromObject(this.mesh);
637
- let hasCollision = false;
638
 
639
- // ๋ชจ๋“  ์žฅ์• ๋ฌผ์— ๋Œ€ํ•ด ์ถฉ๋Œ ๊ฒ€์‚ฌ
640
- for (const obstacle of window.gameInstance.obstacles) {
641
- const obstacleBox = new THREE.Box3().setFromObject(obstacle);
642
- if (enemyBox.intersectsBox(obstacleBox)) {
643
- hasCollision = true;
644
- break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
645
  }
646
- }
647
-
648
- // ๋‹ค๋ฅธ ์  ํƒฑํฌ์™€์˜ ์ถฉ๋Œ ๊ฒ€์‚ฌ
649
- if (!hasCollision) {
650
- for (const otherEnemy of window.gameInstance.enemies) {
651
- if (otherEnemy !== this && otherEnemy.mesh) {
652
- const otherEnemyBox = new THREE.Box3().setFromObject(otherEnemy.mesh);
653
- if (enemyBox.intersectsBox(otherEnemyBox)) {
654
- hasCollision = true;
655
- break;
656
  }
657
  }
658
  }
659
- }
660
-
661
- // ๋งต ๊ฒฝ๊ณ„ ์ฒดํฌ
662
- const mapBoundary = MAP_SIZE / 2;
663
- if (Math.abs(newPosition.x) > mapBoundary ||
664
- Math.abs(newPosition.z) > mapBoundary) {
665
- hasCollision = true;
666
- }
667
-
668
- // ์ถฉ๋Œ์ด ์žˆ์œผ๋ฉด ์ด์ „ ์œ„์น˜๋กœ ๋ณต๊ท€, ์—†์œผ๋ฉด ์ƒˆ ์œ„์น˜ ์œ ์ง€
669
- if (hasCollision) {
670
- this.mesh.position.copy(previousPosition);
671
 
672
- // ์ถฉ๋Œ ์‹œ ์šฐํšŒ ๊ฒฝ๋กœ ์‹œ๋„
673
- const alternateDirections = [
674
- new THREE.Vector3(-direction.z, 0, direction.x), // ์™ผ์ชฝ์œผ๋กœ 90๋„
675
- new THREE.Vector3(direction.z, 0, -direction.x), // ์˜ค๋ฅธ์ชฝ์œผ๋กœ 90๋„
676
- new THREE.Vector3(-direction.x, 0, -direction.z) // 180๋„ ํšŒ์ „
677
- ];
678
 
679
- for (const altDirection of alternateDirections) {
680
- const altMoveVector = altDirection.multiplyScalar(this.moveSpeed);
681
- const altNewPosition = previousPosition.clone().add(altMoveVector);
682
-
683
- this.mesh.position.copy(altNewPosition);
684
- const altEnemyBox = new THREE.Box3().setFromObject(this.mesh);
685
 
686
- let altHasCollision = false;
 
 
 
 
 
687
 
688
- // ์ƒˆ๋กœ์šด ๋ฐฉํ–ฅ์— ๋Œ€ํ•œ ์ถฉ๋Œ ๊ฒ€์‚ฌ
689
- for (const obstacle of window.gameInstance.obstacles) {
690
- const obstacleBox = new THREE.Box3().setFromObject(obstacle);
691
- if (altEnemyBox.intersectsBox(obstacleBox)) {
692
- altHasCollision = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
693
  break;
 
 
 
694
  }
695
  }
696
-
697
- if (!altHasCollision) {
698
- // ์šฐํšŒ ๊ฒฝ๋กœ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋ฉด ๊ทธ ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™
699
- break;
700
- } else {
701
- // ์šฐํšŒ๋„ ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉด ์ด์ „ ์œ„์น˜๋กœ ๋ณต๊ท€
702
- this.mesh.position.copy(previousPosition);
703
- }
704
  }
705
- }
706
-
707
- // ์ง€ํ˜•์— ๋”ฐ๋ฅธ ๊ธฐ์šธ๊ธฐ ์กฐ์ •
708
- const forwardVector = new THREE.Vector3(0, 0, 1).applyQuaternion(this.mesh.quaternion);
709
- const rightVector = new THREE.Vector3(1, 0, 0).applyQuaternion(this.mesh.quaternion);
710
-
711
- const frontHeight = window.gameInstance.getHeightAtPosition(
712
- this.mesh.position.x + forwardVector.x,
713
- this.mesh.position.z + forwardVector.z
714
- );
715
- const backHeight = window.gameInstance.getHeightAtPosition(
716
- this.mesh.position.x - forwardVector.x,
717
- this.mesh.position.z - forwardVector.z
718
- );
719
- const rightHeight = window.gameInstance.getHeightAtPosition(
720
- this.mesh.position.x + rightVector.x,
721
- this.mesh.position.z + rightVector.z
722
- );
723
- const leftHeight = window.gameInstance.getHeightAtPosition(
724
- this.mesh.position.x - rightVector.x,
725
- this.mesh.position.z - rightVector.z
726
- );
727
-
728
- const pitch = Math.atan2(frontHeight - backHeight, 2);
729
- const roll = Math.atan2(rightHeight - leftHeight, 2);
730
-
731
- // ํ˜„์žฌ ํšŒ์ „ ์œ ์ง€ํ•˜๋ฉด์„œ ๊ธฐ์šธ๊ธฐ๋งŒ ์ ์šฉ
732
- const currentRotation = this.mesh.rotation.y;
733
- this.mesh.rotation.set(pitch, currentRotation, roll);
734
- }
735
-
736
- // ํ”Œ๋ ˆ์ด์–ด๋ฅผ ํ–ฅํ•ด ํฌํƒ‘ ํšŒ์ „
737
- this.mesh.lookAt(playerPosition);
738
-
739
- // ์ด์•Œ ์—…๋ฐ์ดํŠธ
740
- if (this.bullets) {
741
- for (let i = this.bullets.length - 1; i >= 0; i--) {
742
- const bullet = this.bullets[i];
743
- bullet.position.add(bullet.velocity);
744
 
745
- // ์ด์•Œ์ด ๋งต ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ€๊ฑฐ๋‚˜ ์žฅ์• ๋ฌผ๊ณผ ์ถฉ๋Œํ•˜๋ฉด ์ œ๊ฑฐ
746
- if (Math.abs(bullet.position.x) > MAP_SIZE / 2 ||
747
- Math.abs(bullet.position.z) > MAP_SIZE / 2) {
748
- this.scene.remove(bullet);
749
- this.bullets.splice(i, 1);
750
- continue;
751
- }
752
 
753
- // ์ด์•Œ๊ณผ ์žฅ์• ๋ฌผ ์ถฉ๋Œ ์ฒดํฌ
754
- const bulletBox = new THREE.Box3().setFromObject(bullet);
755
- for (const obstacle of window.gameInstance.obstacles) {
756
- const obstacleBox = new THREE.Box3().setFromObject(obstacle);
757
- if (bulletBox.intersectsBox(obstacleBox)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
758
  this.scene.remove(bullet);
759
  this.bullets.splice(i, 1);
760
- break;
 
 
 
 
 
 
 
 
 
 
 
761
  }
762
  }
763
  }
764
  }
765
- }
766
-
767
 
768
  shoot(playerPosition) {
 
 
769
  const currentTime = Date.now();
770
  const attackInterval = this.type === 'tank' ?
771
  ENEMY_CONFIG.ATTACK_INTERVAL :
@@ -773,16 +803,28 @@ class Enemy {
773
 
774
  if (currentTime - this.lastAttackTime < attackInterval) return;
775
 
 
776
  const bulletGeometry = new THREE.SphereGeometry(this.type === 'tank' ? 0.2 : 0.3);
777
  const bulletMaterial = new THREE.MeshBasicMaterial({
778
  color: this.type === 'tank' ? 0xff0000 : 0xff6600
779
  });
780
  const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
781
 
782
- bullet.position.copy(this.mesh.position);
 
 
 
 
 
 
 
 
 
 
 
783
 
784
  const direction = new THREE.Vector3()
785
- .subVectors(playerPosition, this.mesh.position)
786
  .normalize();
787
 
788
  const bulletSpeed = this.type === 'tank' ?
@@ -802,8 +844,8 @@ class Enemy {
802
  }
803
 
804
  destroy() {
805
- if (this.mesh) {
806
- this.scene.remove(this.mesh);
807
  this.bullets.forEach(bullet => this.scene.remove(bullet));
808
  this.bullets = [];
809
  this.isLoaded = false;
 
572
  constructor(scene, position, type = 'tank') {
573
  this.scene = scene;
574
  this.position = position;
 
575
  this.type = type;
576
  this.health = type === 'tank' ? 100 : 200;
577
  this.lastAttackTime = 0;
578
  this.bullets = [];
579
  this.isLoaded = false;
580
  this.moveSpeed = type === 'tank' ? ENEMY_MOVE_SPEED : ENEMY_MOVE_SPEED * 0.7;
581
+
582
+ // Add new properties for body and turret
583
+ this.body = null;
584
+ this.turret = null;
585
+ this.turretGroup = new THREE.Group();
586
  }
587
 
588
  async initialize(loader) {
589
  try {
590
+ // Load body and turret separately
591
+ const bodyResult = await loader.loadAsync('/models/t90Body.glb');
592
+ this.body = bodyResult.scene;
593
+
594
+ const turretResult = await loader.loadAsync('/models/t90Turret.glb');
595
+ this.turret = turretResult.scene;
596
+
597
+ // Set up turret group
598
+ this.turretGroup.position.y = 0.2;
599
+ this.turretGroup.add(this.turret);
600
+ this.body.add(this.turretGroup);
601
+
602
+ // Apply transforms to body
603
+ this.body.position.copy(this.position);
604
+ this.body.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
605
+
606
+ // Set up shadows for both body and turret
607
+ this.body.traverse((child) => {
608
+ if (child.isMesh) {
609
+ child.castShadow = true;
610
+ child.receiveShadow = true;
611
+ child.material.shadowSide = THREE.BackSide;
612
+ }
613
+ });
614
 
615
+ this.turret.traverse((child) => {
616
  if (child.isMesh) {
617
  child.castShadow = true;
618
  child.receiveShadow = true;
619
+ child.material.shadowSide = THREE.BackSide;
620
  }
621
  });
622
 
623
+ this.scene.add(this.body);
624
  this.isLoaded = true;
625
  } catch (error) {
626
+ console.error('Error loading enemy tank model:', error);
627
  this.isLoaded = false;
628
  }
629
  }
630
 
631
  update(playerPosition) {
632
+ if (!this.body || !this.isLoaded) return;
633
 
634
+ const direction = new THREE.Vector3()
635
+ .subVectors(playerPosition, this.body.position)
636
+ .normalize();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637
 
638
+ const distanceToPlayer = this.body.position.distanceTo(playerPosition);
639
+ const minDistance = 50;
 
640
 
641
+ // Store previous position
642
+ const previousPosition = this.body.position.clone();
 
643
 
644
+ if (distanceToPlayer > minDistance) {
645
+ const moveVector = direction.multiplyScalar(this.moveSpeed);
646
+ const newPosition = this.body.position.clone().add(moveVector);
647
+
648
+ // Get terrain height
649
+ const heightAtNewPos = window.gameInstance.getHeightAtPosition(
650
+ newPosition.x,
651
+ newPosition.z
652
+ );
653
+ newPosition.y = heightAtNewPos + TANK_HEIGHT;
654
+
655
+ // Move tank body
656
+ const originalPosition = this.body.position.clone();
657
+ this.body.position.copy(newPosition);
658
+
659
+ // Collision checks
660
+ const enemyBox = new THREE.Box3().setFromObject(this.body);
661
+ let hasCollision = false;
662
+
663
+ // Check collisions with obstacles
664
+ for (const obstacle of window.gameInstance.obstacles) {
665
+ const obstacleBox = new THREE.Box3().setFromObject(obstacle);
666
+ if (enemyBox.intersectsBox(obstacleBox)) {
667
+ hasCollision = true;
668
+ break;
669
+ }
670
  }
671
+
672
+ // Check collisions with other enemies
673
+ if (!hasCollision) {
674
+ for (const otherEnemy of window.gameInstance.enemies) {
675
+ if (otherEnemy !== this && otherEnemy.body) {
676
+ const otherEnemyBox = new THREE.Box3().setFromObject(otherEnemy.body);
677
+ if (enemyBox.intersectsBox(otherEnemyBox)) {
678
+ hasCollision = true;
679
+ break;
680
+ }
681
  }
682
  }
683
  }
 
 
 
 
 
 
 
 
 
 
 
 
684
 
685
+ // Check map boundaries
686
+ const mapBoundary = MAP_SIZE / 2;
687
+ if (Math.abs(newPosition.x) > mapBoundary ||
688
+ Math.abs(newPosition.z) > mapBoundary) {
689
+ hasCollision = true;
690
+ }
691
 
692
+ // Handle collision response
693
+ if (hasCollision) {
694
+ this.body.position.copy(previousPosition);
 
 
 
695
 
696
+ // Try alternate paths when collision occurs
697
+ const alternateDirections = [
698
+ new THREE.Vector3(-direction.z, 0, direction.x), // Left 90 degrees
699
+ new THREE.Vector3(direction.z, 0, -direction.x), // Right 90 degrees
700
+ new THREE.Vector3(-direction.x, 0, -direction.z) // 180 degrees
701
+ ];
702
 
703
+ for (const altDirection of alternateDirections) {
704
+ const altMoveVector = altDirection.multiplyScalar(this.moveSpeed);
705
+ const altNewPosition = previousPosition.clone().add(altMoveVector);
706
+
707
+ this.body.position.copy(altNewPosition);
708
+ const altEnemyBox = new THREE.Box3().setFromObject(this.body);
709
+
710
+ let altHasCollision = false;
711
+
712
+ // Check alternate path collisions
713
+ for (const obstacle of window.gameInstance.obstacles) {
714
+ const obstacleBox = new THREE.Box3().setFromObject(obstacle);
715
+ if (altEnemyBox.intersectsBox(obstacleBox)) {
716
+ altHasCollision = true;
717
+ break;
718
+ }
719
+ }
720
+
721
+ if (!altHasCollision) {
722
+ // Use alternate path if no collision
723
  break;
724
+ } else {
725
+ // Revert to previous position if alternate path also collides
726
+ this.body.position.copy(previousPosition);
727
  }
728
  }
 
 
 
 
 
 
 
 
729
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
730
 
731
+ // Adjust tank orientation based on terrain
732
+ const forwardVector = new THREE.Vector3(0, 0, 1).applyQuaternion(this.body.quaternion);
733
+ const rightVector = new THREE.Vector3(1, 0, 0).applyQuaternion(this.body.quaternion);
 
 
 
 
734
 
735
+ const frontHeight = window.gameInstance.getHeightAtPosition(
736
+ this.body.position.x + forwardVector.x,
737
+ this.body.position.z + forwardVector.z
738
+ );
739
+ const backHeight = window.gameInstance.getHeightAtPosition(
740
+ this.body.position.x - forwardVector.x,
741
+ this.body.position.z - forwardVector.z
742
+ );
743
+ const rightHeight = window.gameInstance.getHeightAtPosition(
744
+ this.body.position.x + rightVector.x,
745
+ this.body.position.z + rightVector.z
746
+ );
747
+ const leftHeight = window.gameInstance.getHeightAtPosition(
748
+ this.body.position.x - rightVector.x,
749
+ this.body.position.z - rightVector.z
750
+ );
751
+
752
+ const pitch = Math.atan2(frontHeight - backHeight, 2);
753
+ const roll = Math.atan2(rightHeight - leftHeight, 2);
754
+
755
+ // Apply rotation while maintaining current heading
756
+ const currentRotation = this.body.rotation.y;
757
+ this.body.rotation.set(pitch, currentRotation, roll);
758
+ }
759
+
760
+ // Update turret rotation to face player
761
+ const turretDirection = new THREE.Vector2(
762
+ playerPosition.x - this.body.position.x,
763
+ playerPosition.z - this.body.position.z
764
+ );
765
+ const turretAngle = Math.atan2(turretDirection.x, turretDirection.y);
766
+ this.turretGroup.rotation.y = turretAngle - this.body.rotation.y;
767
+
768
+ // Update bullets
769
+ if (this.bullets) {
770
+ for (let i = this.bullets.length - 1; i >= 0; i--) {
771
+ const bullet = this.bullets[i];
772
+ bullet.position.add(bullet.velocity);
773
+
774
+ // Check bullet bounds and collisions
775
+ if (Math.abs(bullet.position.x) > MAP_SIZE / 2 ||
776
+ Math.abs(bullet.position.z) > MAP_SIZE / 2) {
777
  this.scene.remove(bullet);
778
  this.bullets.splice(i, 1);
779
+ continue;
780
+ }
781
+
782
+ // Check bullet collisions with obstacles
783
+ const bulletBox = new THREE.Box3().setFromObject(bullet);
784
+ for (const obstacle of window.gameInstance.obstacles) {
785
+ const obstacleBox = new THREE.Box3().setFromObject(obstacle);
786
+ if (bulletBox.intersectsBox(obstacleBox)) {
787
+ this.scene.remove(bullet);
788
+ this.bullets.splice(i, 1);
789
+ break;
790
+ }
791
  }
792
  }
793
  }
794
  }
 
 
795
 
796
  shoot(playerPosition) {
797
+ if (!this.turret) return;
798
+
799
  const currentTime = Date.now();
800
  const attackInterval = this.type === 'tank' ?
801
  ENEMY_CONFIG.ATTACK_INTERVAL :
 
803
 
804
  if (currentTime - this.lastAttackTime < attackInterval) return;
805
 
806
+ // Create bullet
807
  const bulletGeometry = new THREE.SphereGeometry(this.type === 'tank' ? 0.2 : 0.3);
808
  const bulletMaterial = new THREE.MeshBasicMaterial({
809
  color: this.type === 'tank' ? 0xff0000 : 0xff6600
810
  });
811
  const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
812
 
813
+ // Get turret world position for bullet spawn
814
+ const muzzleOffset = new THREE.Vector3(0, 0.5, 4);
815
+ const muzzlePosition = new THREE.Vector3();
816
+ const turretWorldQuaternion = new THREE.Quaternion();
817
+
818
+ this.turret.getWorldPosition(muzzlePosition);
819
+ this.turret.getWorldQuaternion(turretWorldQuaternion);
820
+
821
+ muzzleOffset.applyQuaternion(turretWorldQuaternion);
822
+ muzzlePosition.add(muzzleOffset);
823
+
824
+ bullet.position.copy(muzzlePosition);
825
 
826
  const direction = new THREE.Vector3()
827
+ .subVectors(playerPosition, muzzlePosition)
828
  .normalize();
829
 
830
  const bulletSpeed = this.type === 'tank' ?
 
844
  }
845
 
846
  destroy() {
847
+ if (this.body) {
848
+ this.scene.remove(this.body);
849
  this.bullets.forEach(bullet => this.scene.remove(bullet));
850
  this.bullets = [];
851
  this.isLoaded = false;