cutechicken commited on
Commit
5ca5ee0
โ€ข
1 Parent(s): 375a269

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +175 -2
game.js CHANGED
@@ -579,6 +579,10 @@ class Enemy {
579
  this.lastAttackTime = 0;
580
  this.bullets = [];
581
  this.isLoaded = false;
 
 
 
 
582
  this.moveSpeed = type === 'tank' ? ENEMY_MOVE_SPEED : ENEMY_MOVE_SPEED * 0.7;
583
 
584
  // AI ์ƒํƒœ ๊ด€๋ฆฌ
@@ -735,6 +739,57 @@ class Enemy {
735
 
736
  // ์žฅ์• ๋ฌผ ๊ฐ์ง€
737
  const obstacles = this.detectObstacles();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
738
 
739
  // ์ด๋™ ๋ฐ ํšŒํ”ผ ๋กœ์ง
740
  if (obstacles.length > 0 && !this.pathfinding.isAvoidingObstacle) {
@@ -789,6 +844,65 @@ class Enemy {
789
  // ํƒฑํฌ ๊ธฐ์šธ๊ธฐ ์กฐ์ •
790
  this.adjustTankTilt();
791
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
792
  async initialize(loader) {
793
  try {
794
  const modelPath = this.type === 'tank' ? '/models/t90.glb' : '/models/t90.glb';
@@ -812,20 +926,79 @@ class Enemy {
812
  }
813
  }
814
 
 
815
  checkLineOfSight(playerPosition) {
816
  if (!this.mesh) return false;
817
 
818
  const startPos = this.mesh.position.clone();
819
- startPos.y += 2;
820
- const direction = new THREE.Vector3().subVectors(playerPosition, startPos).normalize();
 
 
821
  const distance = startPos.distanceTo(playerPosition);
822
 
823
  const raycaster = new THREE.Raycaster(startPos, direction, 0, distance);
824
  const intersects = raycaster.intersectObjects(window.gameInstance.obstacles, true);
825
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
826
  return intersects.length === 0;
827
  }
828
 
 
 
 
 
 
 
 
 
 
 
 
 
829
  updateAIState(playerPosition) {
830
  const currentTime = Date.now();
831
  const distanceToPlayer = this.mesh.position.distanceTo(playerPosition);
 
579
  this.lastAttackTime = 0;
580
  this.bullets = [];
581
  this.isLoaded = false;
582
+ this.alternativePath = null;
583
+ this.pathFindingTimeout = 0;
584
+ this.lastPathUpdateTime = 0;
585
+ this.pathUpdateInterval = 1000; // 1์ดˆ๋งˆ๋‹ค ๊ฒฝ๋กœ ์—…๋ฐ์ดํŠธ
586
  this.moveSpeed = type === 'tank' ? ENEMY_MOVE_SPEED : ENEMY_MOVE_SPEED * 0.7;
587
 
588
  // AI ์ƒํƒœ ๊ด€๋ฆฌ
 
739
 
740
  // ์žฅ์• ๋ฌผ ๊ฐ์ง€
741
  const obstacles = this.detectObstacles();
742
+ const currentTime = Date.now();
743
+ const hasLineOfSight = this.checkLineOfSight(playerPosition);
744
+ const distanceToPlayer = this.mesh.position.distanceTo(playerPosition);
745
+ // ๊ฒฝ๋กœ ์—…๋ฐ์ดํŠธ ์ฃผ๊ธฐ ์ฒดํฌ
746
+ if (currentTime - this.lastPathUpdateTime > this.pathUpdateInterval) {
747
+ if (!hasLineOfSight) {
748
+ this.alternativePath = this.findAlternativePath(playerPosition);
749
+ }
750
+ this.lastPathUpdateTime = currentTime;
751
+ }
752
+ if (!hasLineOfSight) {
753
+ if (this.alternativePath) {
754
+ const pathDirection = new THREE.Vector3()
755
+ .subVectors(this.alternativePath, this.mesh.position)
756
+ .normalize();
757
+ this.mesh.position.add(pathDirection.multiplyScalar(this.moveSpeed));
758
+
759
+ const targetRotation = Math.atan2(pathDirection.x, pathDirection.z);
760
+ this.mesh.rotation.y = this.smoothRotation(this.mesh.rotation.y, targetRotation, 0.1);
761
+ }
762
+ } else {
763
+ this.alternativePath = null;
764
+
765
+ // ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ฅธ ํ–‰๋™ ๊ฒฐ์ •
766
+ if (distanceToPlayer > ENEMY_CONFIG.ATTACK_RANGE * 0.7) {
767
+ const moveDirection = new THREE.Vector3()
768
+ .subVectors(playerPosition, this.mesh.position)
769
+ .normalize();
770
+ this.mesh.position.add(moveDirection.multiplyScalar(this.moveSpeed));
771
+ } else if (distanceToPlayer < ENEMY_CONFIG.ATTACK_RANGE * 0.3) {
772
+ const retreatDirection = new THREE.Vector3()
773
+ .subVectors(this.mesh.position, playerPosition)
774
+ .normalize();
775
+ this.mesh.position.add(retreatDirection.multiplyScalar(this.moveSpeed));
776
+ }
777
+
778
+ // ํ”Œ๋ ˆ์ด์–ด ๋ฐฉํ–ฅ์œผ๋กœ ํšŒ์ „
779
+ const directionToPlayer = new THREE.Vector3()
780
+ .subVectors(playerPosition, this.mesh.position)
781
+ .normalize();
782
+ const targetRotation = Math.atan2(directionToPlayer.x, directionToPlayer.z);
783
+ this.mesh.rotation.y = this.smoothRotation(this.mesh.rotation.y, targetRotation, 0.1);
784
+
785
+ // ๊ณต๊ฒฉ ์กฐ๊ฑด ํ™•์ธ
786
+ if (hasLineOfSight && distanceToPlayer <= ENEMY_CONFIG.ATTACK_RANGE) {
787
+ this.shoot(playerPosition);
788
+ }
789
+ }
790
+ }
791
+ }
792
+
793
 
794
  // ์ด๋™ ๋ฐ ํšŒํ”ผ ๋กœ์ง
795
  if (obstacles.length > 0 && !this.pathfinding.isAvoidingObstacle) {
 
844
  // ํƒฑํฌ ๊ธฐ์šธ๊ธฐ ์กฐ์ •
845
  this.adjustTankTilt();
846
  }
847
+ checkLineOfSight(targetPosition) {
848
+ if (!this.mesh) return false;
849
+
850
+ const startPos = this.mesh.position.clone();
851
+ startPos.y += 2; // ํฌํƒ‘ ๋†’์ด
852
+ const direction = new THREE.Vector3()
853
+ .subVectors(targetPosition, startPos)
854
+ .normalize();
855
+ const distance = startPos.distanceTo(targetPosition);
856
+
857
+ const raycaster = new THREE.Raycaster(startPos, direction, 0, distance);
858
+ const intersects = raycaster.intersectObjects(window.gameInstance.obstacles, true);
859
+
860
+ return intersects.length === 0;
861
+ }
862
+
863
+ findAlternativePath(playerPosition) {
864
+ const currentPos = this.mesh.position.clone();
865
+ const directionToPlayer = new THREE.Vector3()
866
+ .subVectors(playerPosition, currentPos)
867
+ .normalize();
868
+
869
+ // ์ขŒ์šฐ 90๋„ ๋ฐฉํ–ฅ ๊ณ„์‚ฐ
870
+ const leftDirection = new THREE.Vector3()
871
+ .copy(directionToPlayer)
872
+ .applyAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
873
+ const rightDirection = new THREE.Vector3()
874
+ .copy(directionToPlayer)
875
+ .applyAxisAngle(new THREE.Vector3(0, 1, 0), -Math.PI / 2);
876
+
877
+ // ์ขŒ์šฐ 30๋ฏธํ„ฐ ์ง€์  ํ™•์ธ
878
+ const checkDistance = 30;
879
+ const leftPoint = currentPos.clone().add(leftDirection.multiplyScalar(checkDistance));
880
+ const rightPoint = currentPos.clone().add(rightDirection.multiplyScalar(checkDistance));
881
+
882
+ // ๊ฐ ๋ฐฉํ–ฅ์˜ ์žฅ์• ๋ฌผ ์ฒดํฌ
883
+ const leftClear = this.checkPathClear(currentPos, leftPoint);
884
+ const rightClear = this.checkPathClear(currentPos, rightPoint);
885
+
886
+ if (leftClear && rightClear) {
887
+ // ๋‘˜ ๋‹ค ๊ฐ€๋Šฅํ•˜๋ฉด ๋žœ๋ค ์„ ํƒ
888
+ return Math.random() < 0.5 ? leftPoint : rightPoint;
889
+ } else if (leftClear) {
890
+ return leftPoint;
891
+ } else if (rightClear) {
892
+ return rightPoint;
893
+ }
894
+
895
+ return null;
896
+ }
897
+
898
+ checkPathClear(start, end) {
899
+ const direction = new THREE.Vector3().subVectors(end, start).normalize();
900
+ const distance = start.distanceTo(end);
901
+ const raycaster = new THREE.Raycaster(start, direction, 0, distance);
902
+ const intersects = raycaster.intersectObjects(window.gameInstance.obstacles, true);
903
+ return intersects.length === 0;
904
+ }
905
+
906
  async initialize(loader) {
907
  try {
908
  const modelPath = this.type === 'tank' ? '/models/t90.glb' : '/models/t90.glb';
 
926
  }
927
  }
928
 
929
+ // ์‹œ์•ผ ํ™•์ธ ๋ฉ”์„œ๋“œ (๊ธฐ์กด ์ฝ”๋“œ ์ˆ˜์ •)
930
  checkLineOfSight(playerPosition) {
931
  if (!this.mesh) return false;
932
 
933
  const startPos = this.mesh.position.clone();
934
+ startPos.y += 2; // ํฌํƒ‘ ๋†’์ด
935
+ const direction = new THREE.Vector3()
936
+ .subVectors(playerPosition, startPos)
937
+ .normalize();
938
  const distance = startPos.distanceTo(playerPosition);
939
 
940
  const raycaster = new THREE.Raycaster(startPos, direction, 0, distance);
941
  const intersects = raycaster.intersectObjects(window.gameInstance.obstacles, true);
942
 
943
+ // ์žฅ์• ๋ฌผ๊ณผ์˜ ์ถฉ๋Œ์ด ์žˆ๋Š”์ง€ ํ™•์ธ
944
+ return intersects.length === 0;
945
+ }
946
+ // ๋Œ€์ฒด ๊ฒฝ๋กœ ์ฐพ๊ธฐ ๋ฉ”์„œ๋“œ
947
+ findAlternativePath(playerPosition) {
948
+ const currentPos = this.mesh.position.clone();
949
+ const directionToPlayer = new THREE.Vector3()
950
+ .subVectors(playerPosition, currentPos)
951
+ .normalize();
952
+
953
+ // ์ขŒ์šฐ 90๋„ ๋ฐฉํ–ฅ ๊ณ„์‚ฐ
954
+ const leftDirection = new THREE.Vector3()
955
+ .copy(directionToPlayer)
956
+ .applyAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
957
+ const rightDirection = new THREE.Vector3()
958
+ .copy(directionToPlayer)
959
+ .applyAxisAngle(new THREE.Vector3(0, 1, 0), -Math.PI / 2);
960
+
961
+ // ์ขŒ์šฐ 30๋ฏธํ„ฐ ์ง€์  ํ™•์ธ
962
+ const checkDistance = 30;
963
+ const leftPoint = currentPos.clone().add(leftDirection.multiplyScalar(checkDistance));
964
+ const rightPoint = currentPos.clone().add(rightDirection.multiplyScalar(checkDistance));
965
+
966
+ // ๊ฐ ๋ฐฉํ–ฅ์˜ ์žฅ์• ๋ฌผ ์ฒดํฌ
967
+ const leftClear = this.checkPathClear(currentPos, leftPoint);
968
+ const rightClear = this.checkPathClear(currentPos, rightPoint);
969
+
970
+ if (leftClear && rightClear) {
971
+ // ๋‘˜ ๋‹ค ๊ฐ€๋Šฅํ•˜๋ฉด ๋žœ๋ค ์„ ํƒ
972
+ return Math.random() < 0.5 ? leftPoint : rightPoint;
973
+ } else if (leftClear) {
974
+ return leftPoint;
975
+ } else if (rightClear) {
976
+ return rightPoint;
977
+ }
978
+
979
+ return null;
980
+ }
981
+ // ๊ฒฝ๋กœ ์œ ํšจ์„ฑ ํ™•์ธ
982
+ checkPathClear(start, end) {
983
+ const direction = new THREE.Vector3().subVectors(end, start).normalize();
984
+ const distance = start.distanceTo(end);
985
+ const raycaster = new THREE.Raycaster(start, direction, 0, distance);
986
+ const intersects = raycaster.intersectObjects(window.gameInstance.obstacles, true);
987
  return intersects.length === 0;
988
  }
989
 
990
+ // ๋ถ€๋“œ๋Ÿฌ์šด ํšŒ์ „ ์ฒ˜๋ฆฌ
991
+ smoothRotation(current, target, factor) {
992
+ let delta = target - current;
993
+
994
+ // ๊ฐ๋„ ์ฐจ์ด๋ฅผ -PI์—์„œ PI ์‚ฌ์ด๋กœ ์ •๊ทœํ™”
995
+ while (delta > Math.PI) delta -= Math.PI * 2;
996
+ while (delta < -Math.PI) delta += Math.PI * 2;
997
+
998
+ return current + delta * factor;
999
+ }
1000
+
1001
+
1002
  updateAIState(playerPosition) {
1003
  const currentTime = Date.now();
1004
  const distanceToPlayer = this.mesh.position.distanceTo(playerPosition);