cutechicken commited on
Commit
ae1840c
ยท
verified ยท
1 Parent(s): 03a1358

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +227 -36
game.js CHANGED
@@ -691,16 +691,37 @@ class EnemyFighter {
691
  this.velocity = new THREE.Vector3(0, 0, 120);
692
  this.rotation = new THREE.Euler(0, 0, 0);
693
  this.health = GAME_CONSTANTS.MAX_HEALTH; // ์ฒด๋ ฅ 1000
694
- this.speed = 120;
695
  this.bullets = [];
696
  this.lastShootTime = 0;
697
 
 
698
  this.aiState = 'patrol';
699
  this.targetPosition = position.clone();
700
  this.patrolCenter = position.clone();
701
- this.patrolRadius = 2000;
702
  this.lastStateChange = 0;
703
  this.playerFighter = null; // ํ”Œ๋ ˆ์ด์–ด ์ฐธ์กฐ ์ €์žฅ์šฉ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
704
  }
705
 
706
  async initialize(loader) {
@@ -757,52 +778,86 @@ class EnemyFighter {
757
  const currentTime = Date.now();
758
  const distanceToPlayer = this.position.distanceTo(playerPosition);
759
 
760
- if (distanceToPlayer < 5000) {
761
- const direction = new THREE.Vector3()
762
- .subVectors(playerPosition, this.position)
763
- .normalize();
764
-
765
- this.velocity = direction.multiplyScalar(this.speed);
766
- this.rotation.y = Math.atan2(direction.x, direction.z);
767
-
768
- if (distanceToPlayer < 2000 && currentTime - this.lastShootTime > 1500) {
769
- this.shoot();
770
  }
771
  } else {
772
- if (this.position.distanceTo(this.targetPosition) < 300) {
773
- const angle = Math.random() * Math.PI * 2;
774
- this.targetPosition = this.patrolCenter.clone().add(
775
- new THREE.Vector3(
776
- Math.cos(angle) * this.patrolRadius,
777
- (Math.random() - 0.5) * 1000,
778
- Math.sin(angle) * this.patrolRadius
779
- )
780
- );
781
  }
782
-
783
- const direction = new THREE.Vector3()
784
- .subVectors(this.targetPosition, this.position)
785
- .normalize();
786
-
787
- this.velocity = direction.multiplyScalar(this.speed * 0.7);
788
- this.rotation.y = Math.atan2(direction.x, direction.z);
789
  }
790
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
791
  this.position.add(this.velocity.clone().multiplyScalar(deltaTime));
792
 
793
- if (this.position.y < GAME_CONSTANTS.MIN_ALTITUDE) {
794
- this.position.y = GAME_CONSTANTS.MIN_ALTITUDE;
 
 
795
  }
796
- if (this.position.y > GAME_CONSTANTS.MAX_ALTITUDE) {
797
- this.position.y = GAME_CONSTANTS.MAX_ALTITUDE;
 
798
  }
799
 
 
800
  const mapLimit = GAME_CONSTANTS.MAP_SIZE / 2;
801
- if (this.position.x > mapLimit) this.position.x = -mapLimit;
802
- if (this.position.x < -mapLimit) this.position.x = mapLimit;
803
- if (this.position.z > mapLimit) this.position.z = -mapLimit;
804
- if (this.position.z < -mapLimit) this.position.z = mapLimit;
 
 
805
 
 
806
  this.mesh.position.copy(this.position);
807
  this.mesh.rotation.x = this.rotation.x;
808
  this.mesh.rotation.y = this.rotation.y + 3 * Math.PI / 2;
@@ -810,6 +865,137 @@ class EnemyFighter {
810
 
811
  this.updateBullets(deltaTime);
812
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
813
 
814
  shoot() {
815
  this.lastShootTime = Date.now();
@@ -1861,6 +2047,11 @@ class Game {
1861
  }
1862
 
1863
  if (this.isStarted) {
 
 
 
 
 
1864
  this.enemies.forEach(enemy => {
1865
  enemy.update(this.fighter.position, deltaTime);
1866
  });
 
691
  this.velocity = new THREE.Vector3(0, 0, 120);
692
  this.rotation = new THREE.Euler(0, 0, 0);
693
  this.health = GAME_CONSTANTS.MAX_HEALTH; // ์ฒด๋ ฅ 1000
694
+ this.speed = 386; // 750kt in m/s (750 * 0.514444)
695
  this.bullets = [];
696
  this.lastShootTime = 0;
697
 
698
+ // ๊ฐœ์„ ๋œ AI ์ƒํƒœ
699
  this.aiState = 'patrol';
700
  this.targetPosition = position.clone();
701
  this.patrolCenter = position.clone();
702
+ this.patrolRadius = 3000;
703
  this.lastStateChange = 0;
704
  this.playerFighter = null; // ํ”Œ๋ ˆ์ด์–ด ์ฐธ์กฐ ์ €์žฅ์šฉ
705
+
706
+ // ํ˜„์‹ค์ ์ธ ๋น„ํ–‰ ๋ฌผ๋ฆฌ
707
+ this.targetRotation = new THREE.Euler(0, 0, 0);
708
+ this.currentRoll = 0;
709
+ this.currentPitch = 0;
710
+ this.currentYaw = 0;
711
+ this.throttle = 0.75; // ๊ธฐ๋ณธ ์Šค๋กœํ‹€ 75%
712
+
713
+ // ํšŒํ”ผ ๊ธฐ๋™
714
+ this.evasionTimer = 0;
715
+ this.evasionPattern = 0;
716
+
717
+ // ๋‹ค๋ฅธ ์ ๊ธฐ๋“ค๊ณผ์˜ ์ถฉ๋Œ ํšŒํ”ผ
718
+ this.nearbyEnemies = [];
719
+ this.separationRadius = 300; // ์ตœ์†Œ ๊ฑฐ๋ฆฌ 300m
720
+
721
+ // ๊ณต๊ฒฉ ํŒจํ„ด
722
+ this.attackAngle = 0;
723
+ this.attackDistance = 0;
724
+ this.lastAttackTime = 0;
725
  }
726
 
727
  async initialize(loader) {
 
778
  const currentTime = Date.now();
779
  const distanceToPlayer = this.position.distanceTo(playerPosition);
780
 
781
+ // AI ์ƒํƒœ ๊ฒฐ์ •
782
+ if (distanceToPlayer < 3000) {
783
+ if (this.aiState !== 'combat') {
784
+ this.aiState = 'combat';
785
+ this.lastStateChange = currentTime;
786
+ }
787
+ } else if (distanceToPlayer < 6000) {
788
+ if (this.aiState !== 'approach') {
789
+ this.aiState = 'approach';
790
+ this.lastStateChange = currentTime;
791
  }
792
  } else {
793
+ if (this.aiState !== 'patrol') {
794
+ this.aiState = 'patrol';
795
+ this.lastStateChange = currentTime;
 
 
 
 
 
 
796
  }
 
 
 
 
 
 
 
797
  }
798
 
799
+ // ์ƒํƒœ๋ณ„ ํ–‰๋™
800
+ switch(this.aiState) {
801
+ case 'combat':
802
+ this.updateCombatBehavior(playerPosition, deltaTime, currentTime);
803
+ break;
804
+ case 'approach':
805
+ this.updateApproachBehavior(playerPosition, deltaTime);
806
+ break;
807
+ case 'patrol':
808
+ this.updatePatrolBehavior(deltaTime);
809
+ break;
810
+ }
811
+
812
+ // ๋‹ค๋ฅธ ์ ๊ธฐ์™€์˜ ์ถฉ๋Œ ํšŒํ”ผ
813
+ this.avoidOtherEnemies(deltaTime);
814
+
815
+ // ๋ถ€๋“œ๋Ÿฌ์šด ํšŒ์ „ ๋ณด๊ฐ„
816
+ this.rotation.x = THREE.MathUtils.lerp(this.rotation.x, this.targetRotation.x, deltaTime * 2.0);
817
+ this.rotation.y = THREE.MathUtils.lerp(this.rotation.y, this.targetRotation.y, deltaTime * 3.0);
818
+ this.rotation.z = THREE.MathUtils.lerp(this.rotation.z, this.targetRotation.z, deltaTime * 2.5);
819
+
820
+ // ์†๋„ ๊ณ„์‚ฐ (์Šค๋กœํ‹€ ๊ธฐ๋ฐ˜)
821
+ const targetSpeed = this.speed * this.throttle;
822
+ const currentSpeed = this.velocity.length();
823
+ const newSpeed = THREE.MathUtils.lerp(currentSpeed, targetSpeed, deltaTime * 0.5);
824
+
825
+ // ๋ฐฉํ–ฅ ๋ฒกํ„ฐ ๊ณ„์‚ฐ
826
+ const forward = new THREE.Vector3(0, 0, 1);
827
+ forward.applyEuler(this.rotation);
828
+
829
+ // ์ค‘๋ ฅ๊ณผ ์–‘๋ ฅ ํšจ๊ณผ
830
+ const liftFactor = Math.min(1.0, newSpeed / this.speed);
831
+ const gravityEffect = GAME_CONSTANTS.GRAVITY * deltaTime * 0.15;
832
+ const lift = gravityEffect * liftFactor * 0.8;
833
+
834
+ // ์†๋„ ๋ฒกํ„ฐ ์—…๋ฐ์ดํŠธ
835
+ this.velocity = forward.multiplyScalar(newSpeed);
836
+ this.velocity.y += (lift - gravityEffect);
837
+
838
+ // ์œ„์น˜ ์—…๋ฐ์ดํŠธ
839
  this.position.add(this.velocity.clone().multiplyScalar(deltaTime));
840
 
841
+ // ๊ณ ๋„ ์ œํ•œ
842
+ if (this.position.y < 500) {
843
+ this.position.y = 500;
844
+ this.targetRotation.x = Math.max(this.targetRotation.x, -0.2); // ๊ธฐ์ˆ˜๋ฅผ ๋“ค์–ด ์ƒ์Šน
845
  }
846
+ if (this.position.y > GAME_CONSTANTS.MAX_ALTITUDE - 500) {
847
+ this.position.y = GAME_CONSTANTS.MAX_ALTITUDE - 500;
848
+ this.targetRotation.x = Math.min(this.targetRotation.x, 0.2); // ๊ธฐ์ˆ˜๋ฅผ ๋‚ด๋ ค ํ•˜๊ฐ•
849
  }
850
 
851
+ // ๋งต ๊ฒฝ๊ณ„ ์ฒ˜๋ฆฌ
852
  const mapLimit = GAME_CONSTANTS.MAP_SIZE / 2;
853
+ if (Math.abs(this.position.x) > mapLimit * 0.8 || Math.abs(this.position.z) > mapLimit * 0.8) {
854
+ // ๋งต ๊ฒฝ๊ณ„์— ๊ฐ€๊นŒ์›Œ์ง€๋ฉด ์ค‘์•™์œผ๋กœ ์„ ํšŒ
855
+ const centerDirection = this.position.clone().negate().normalize();
856
+ const angle = Math.atan2(centerDirection.x, centerDirection.z);
857
+ this.targetRotation.y = angle;
858
+ }
859
 
860
+ // ๋ฉ”์‹œ ์—…๋ฐ์ดํŠธ
861
  this.mesh.position.copy(this.position);
862
  this.mesh.rotation.x = this.rotation.x;
863
  this.mesh.rotation.y = this.rotation.y + 3 * Math.PI / 2;
 
865
 
866
  this.updateBullets(deltaTime);
867
  }
868
+
869
+ updateCombatBehavior(playerPosition, deltaTime, currentTime) {
870
+ const toPlayer = playerPosition.clone().sub(this.position);
871
+ const distance = toPlayer.length();
872
+ toPlayer.normalize();
873
+
874
+ // ๊ณต๊ฒฉ ๊ฐ๋„ ๊ณ„์‚ฐ
875
+ const angle = Math.atan2(toPlayer.x, toPlayer.z);
876
+ const pitchAngle = Math.atan2(-toPlayer.y, Math.sqrt(toPlayer.x * toPlayer.x + toPlayer.z * toPlayer.z));
877
+
878
+ // BFM (Basic Fighter Maneuvers) ๊ตฌํ˜„
879
+ if (distance < 1000) {
880
+ // ๋„ˆ๋ฌด ๊ฐ€๊นŒ์šฐ๋ฉด ํšŒํ”ผ ๊ธฐ๋™
881
+ this.evasionTimer += deltaTime;
882
+ if (this.evasionTimer > 2.0) {
883
+ this.evasionTimer = 0;
884
+ this.evasionPattern = Math.floor(Math.random() * 3);
885
+ }
886
+
887
+ switch(this.evasionPattern) {
888
+ case 0: // ๋ฐฐ๋Ÿด ๋กค
889
+ this.targetRotation.z = Math.sin(this.evasionTimer * Math.PI) * 1.5;
890
+ this.targetRotation.x = pitchAngle + Math.sin(this.evasionTimer * Math.PI * 2) * 0.3;
891
+ break;
892
+ case 1: // ์ด๋ฉœ๋งŒ ํ„ด
893
+ this.targetRotation.x = -0.5 + this.evasionTimer * 0.5;
894
+ this.targetRotation.z = this.evasionTimer * Math.PI;
895
+ break;
896
+ case 2: // ์Šคํ”Œ๋ฆฟ S
897
+ this.targetRotation.x = 0.5 - this.evasionTimer * 0.5;
898
+ this.targetRotation.z = Math.PI;
899
+ break;
900
+ }
901
+ this.throttle = 1.0; // ์ตœ๋Œ€ ์†๋„๋กœ ํšŒํ”ผ
902
+ } else if (distance < 2000) {
903
+ // ๊ณต๊ฒฉ ์œ„์น˜ ์„ ์ •
904
+ this.targetRotation.y = angle;
905
+ this.targetRotation.x = pitchAngle;
906
+ this.targetRotation.z = 0;
907
+
908
+ // ๋ฆฌ๋“œ ์•ต๊ธ€ ๊ณ„์‚ฐ (์˜ˆ์ธก ์‚ฌ๊ฒฉ)
909
+ const playerVelocity = this.playerFighter ? this.playerFighter.velocity : new THREE.Vector3();
910
+ const bulletTime = distance / 1200; // ํƒ„ํ™˜ ์†๋„
911
+ const leadPosition = playerPosition.clone().add(playerVelocity.clone().multiplyScalar(bulletTime));
912
+ const leadDirection = leadPosition.sub(this.position).normalize();
913
+ const leadAngle = Math.atan2(leadDirection.x, leadDirection.z);
914
+
915
+ this.targetRotation.y = leadAngle;
916
+
917
+ // ์‚ฌ๊ฒฉ
918
+ if (currentTime - this.lastShootTime > 800) {
919
+ this.shoot();
920
+ }
921
+
922
+ this.throttle = 0.9;
923
+ } else {
924
+ // ์ถ”์ 
925
+ this.targetRotation.y = angle;
926
+ this.targetRotation.x = pitchAngle * 0.5; // ๋ถ€๋“œ๋Ÿฌ์šด ํ”ผ์น˜ ์กฐ์ •
927
+ this.targetRotation.z = 0;
928
+ this.throttle = 0.85;
929
+ }
930
+ }
931
+
932
+ updateApproachBehavior(playerPosition, deltaTime) {
933
+ const toPlayer = playerPosition.clone().sub(this.position);
934
+ const angle = Math.atan2(toPlayer.x, toPlayer.z);
935
+
936
+ // ๋ถ€๋“œ๋Ÿฌ์šด ์ ‘๊ทผ
937
+ this.targetRotation.y = angle;
938
+ this.targetRotation.x = 0;
939
+ this.targetRotation.z = Math.sin(Date.now() * 0.001) * 0.3; // ์•ฝ๊ฐ„์˜ ๋กค๋ง
940
+ this.throttle = 0.8;
941
+ }
942
+
943
+ updatePatrolBehavior(deltaTime) {
944
+ // ์ˆœ์ฐฐ ํŒจํ„ด
945
+ if (this.position.distanceTo(this.targetPosition) < 500) {
946
+ // ์ƒˆ๋กœ์šด ์ˆœ์ฐฐ ์ง€์  ์„ค์ •
947
+ const angle = Math.random() * Math.PI * 2;
948
+ const distance = this.patrolRadius * (0.5 + Math.random() * 0.5);
949
+ this.targetPosition = this.patrolCenter.clone().add(
950
+ new THREE.Vector3(
951
+ Math.cos(angle) * distance,
952
+ (Math.random() - 0.5) * 1000,
953
+ Math.sin(angle) * distance
954
+ )
955
+ );
956
+ }
957
+
958
+ const toTarget = this.targetPosition.clone().sub(this.position);
959
+ const angle = Math.atan2(toTarget.x, toTarget.z);
960
+
961
+ this.targetRotation.y = angle;
962
+ this.targetRotation.x = 0;
963
+ this.targetRotation.z = Math.sin(Date.now() * 0.0005) * 0.2; // ์™„๋งŒํ•œ ๋กค๋ง
964
+ this.throttle = 0.7;
965
+ }
966
+
967
+ avoidOtherEnemies(deltaTime) {
968
+ if (!this.nearbyEnemies) return;
969
+
970
+ let avoidanceVector = new THREE.Vector3();
971
+ let tooClose = false;
972
+
973
+ this.nearbyEnemies.forEach(enemy => {
974
+ if (enemy === this || !enemy.position) return;
975
+
976
+ const distance = this.position.distanceTo(enemy.position);
977
+ if (distance < this.separationRadius && distance > 0) {
978
+ // ๋ฐ˜๋ฐœ๋ ฅ ๊ณ„์‚ฐ
979
+ const force = this.position.clone().sub(enemy.position).normalize();
980
+ const strength = 1 - (distance / this.separationRadius);
981
+ avoidanceVector.add(force.multiplyScalar(strength));
982
+ tooClose = true;
983
+ }
984
+ });
985
+
986
+ if (tooClose) {
987
+ // ํšŒํ”ผ ๋ฐฉํ–ฅ์œผ๋กœ ์„ ํšŒ
988
+ const avoidAngle = Math.atan2(avoidanceVector.x, avoidanceVector.z);
989
+ this.targetRotation.y = THREE.MathUtils.lerp(this.targetRotation.y, avoidAngle, deltaTime * 5);
990
+
991
+ // ๊ณ ๋„ ๋ณ€๊ฒฝ์œผ๋กœ ์ถ”๊ฐ€ ํšŒํ”ผ
992
+ if (avoidanceVector.y > 0.5) {
993
+ this.targetRotation.x = -0.3; // ์ƒ์Šน
994
+ } else if (avoidanceVector.y < -0.5) {
995
+ this.targetRotation.x = 0.3; // ํ•˜๊ฐ•
996
+ }
997
+ }
998
+ }
999
 
1000
  shoot() {
1001
  this.lastShootTime = Date.now();
 
2047
  }
2048
 
2049
  if (this.isStarted) {
2050
+ // ์ ๊ธฐ๋“ค์—๊ฒŒ ์„œ๋กœ์˜ ์ฐธ์กฐ ์ „๋‹ฌ (์ถฉ๋Œ ํšŒํ”ผ์šฉ)
2051
+ this.enemies.forEach(enemy => {
2052
+ enemy.nearbyEnemies = this.enemies;
2053
+ });
2054
+
2055
  this.enemies.forEach(enemy => {
2056
  enemy.update(this.fighter.position, deltaTime);
2057
  });