Update game.js
Browse files
game.js
CHANGED
|
@@ -737,6 +737,13 @@ class EnemyFighter {
|
|
| 737 |
this.targetPosition = null;
|
| 738 |
this.playerFighter = null;
|
| 739 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 740 |
// ์ ํฌ ์์คํ
|
| 741 |
this.bullets = [];
|
| 742 |
this.burstCounter = 0; // ํ์ฌ ์ฐ๋ฐ ์นด์ดํฐ
|
|
@@ -804,18 +811,31 @@ class EnemyFighter {
|
|
| 804 |
update(playerPosition, deltaTime) {
|
| 805 |
if (!this.mesh || !this.isLoaded) return;
|
| 806 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 807 |
const distanceToPlayer = this.position.distanceTo(playerPosition);
|
| 808 |
|
| 809 |
-
// ์ํ ๊ฒฐ์ -
|
| 810 |
-
if (
|
|
|
|
|
|
|
| 811 |
this.aiState = 'combat';
|
| 812 |
} else {
|
| 813 |
this.aiState = 'patrol';
|
| 814 |
}
|
| 815 |
|
| 816 |
-
// ์ถฉ๋ ํํผ ๊ณ์ฐ
|
| 817 |
this.calculateAvoidance();
|
| 818 |
|
|
|
|
|
|
|
|
|
|
| 819 |
// AI ํ๋ ์คํ
|
| 820 |
switch (this.aiState) {
|
| 821 |
case 'patrol':
|
|
@@ -824,6 +844,9 @@ class EnemyFighter {
|
|
| 824 |
case 'combat':
|
| 825 |
this.executeCombat(playerPosition, deltaTime);
|
| 826 |
break;
|
|
|
|
|
|
|
|
|
|
| 827 |
}
|
| 828 |
|
| 829 |
// ๋ฌผ๋ฆฌ ์
๋ฐ์ดํธ
|
|
@@ -883,7 +906,82 @@ class EnemyFighter {
|
|
| 883 |
}
|
| 884 |
}
|
| 885 |
|
| 886 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 887 |
|
| 888 |
smoothTurnToTarget(targetPos, deltaTime) {
|
| 889 |
// ํ๊ฒ ๋ฐฉํฅ ๊ณ์ฐ
|
|
@@ -891,9 +989,11 @@ class EnemyFighter {
|
|
| 891 |
direction.y *= 0.5; // ์์ง ์ด๋์ ๋ ์ ํ์ ์ผ๋ก
|
| 892 |
direction.normalize();
|
| 893 |
|
| 894 |
-
// ์ถฉ๋ ํํผ ๋ฒกํฐ ์ ์ฉ
|
| 895 |
if (this.avoidanceVector.length() > 0) {
|
| 896 |
-
|
|
|
|
|
|
|
| 897 |
direction.normalize();
|
| 898 |
}
|
| 899 |
|
|
@@ -902,12 +1002,11 @@ class EnemyFighter {
|
|
| 902 |
const targetPitch = Math.asin(-direction.y);
|
| 903 |
|
| 904 |
// ๋ถ๋๋ฌ์ด ํ์ (์ต๋ ํ์ ์๋ ์ ํ)
|
| 905 |
-
const
|
| 906 |
-
const
|
| 907 |
-
|
| 908 |
-
const maxTurnRate = this.turnSpeed * deltaTime;
|
| 909 |
|
| 910 |
// Yaw ํ์
|
|
|
|
| 911 |
if (Math.abs(yawDiff) > maxTurnRate) {
|
| 912 |
this.rotation.y += Math.sign(yawDiff) * maxTurnRate;
|
| 913 |
} else {
|
|
@@ -916,8 +1015,8 @@ class EnemyFighter {
|
|
| 916 |
|
| 917 |
// Pitch ํ์ (์ ํ์ )
|
| 918 |
const maxPitchRate = maxTurnRate * 0.7; // ํผ์น๋ ์กฐ๊ธ ๋ ๋น ๋ฅด๊ฒ
|
| 919 |
-
if (Math.abs(
|
| 920 |
-
this.rotation.x += Math.sign(
|
| 921 |
} else {
|
| 922 |
this.rotation.x = targetPitch;
|
| 923 |
}
|
|
@@ -925,6 +1024,12 @@ class EnemyFighter {
|
|
| 925 |
// Pitch ์ ํ (ยฑ40๋)
|
| 926 |
const maxPitchAngle = Math.PI * 40 / 180; // 40๋๋ฅผ ๋ผ๋์์ผ๋ก
|
| 927 |
this.rotation.x = THREE.MathUtils.clamp(this.rotation.x, -maxPitchAngle, maxPitchAngle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 928 |
}
|
| 929 |
|
| 930 |
calculateAvoidance() {
|
|
@@ -933,15 +1038,36 @@ class EnemyFighter {
|
|
| 933 |
if (!this.nearbyEnemies) return;
|
| 934 |
|
| 935 |
let avoidCount = 0;
|
|
|
|
| 936 |
|
| 937 |
this.nearbyEnemies.forEach(enemy => {
|
| 938 |
if (enemy === this || !enemy.position) return;
|
| 939 |
|
| 940 |
const distance = this.position.distanceTo(enemy.position);
|
| 941 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 942 |
// ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ํํผ
|
| 943 |
const avoidDir = this.position.clone().sub(enemy.position).normalize();
|
| 944 |
-
const strength = (300 - distance) /
|
| 945 |
this.avoidanceVector.add(avoidDir.multiplyScalar(strength));
|
| 946 |
avoidCount++;
|
| 947 |
}
|
|
@@ -950,6 +1076,14 @@ class EnemyFighter {
|
|
| 950 |
if (avoidCount > 0) {
|
| 951 |
this.avoidanceVector.divideScalar(avoidCount);
|
| 952 |
this.avoidanceVector.normalize();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 953 |
}
|
| 954 |
}
|
| 955 |
|
|
|
|
| 737 |
this.targetPosition = null;
|
| 738 |
this.playerFighter = null;
|
| 739 |
|
| 740 |
+
// ํํผ ์์คํ
|
| 741 |
+
this.temporaryEvadeMode = false;
|
| 742 |
+
this.evadeTimer = 0;
|
| 743 |
+
|
| 744 |
+
// ์ถฉ๋ ์์ธก
|
| 745 |
+
this.predictedPosition = new THREE.Vector3();
|
| 746 |
+
|
| 747 |
// ์ ํฌ ์์คํ
|
| 748 |
this.bullets = [];
|
| 749 |
this.burstCounter = 0; // ํ์ฌ ์ฐ๋ฐ ์นด์ดํฐ
|
|
|
|
| 811 |
update(playerPosition, deltaTime) {
|
| 812 |
if (!this.mesh || !this.isLoaded) return;
|
| 813 |
|
| 814 |
+
// ํํผ ํ์ด๋จธ ์
๋ฐ์ดํธ
|
| 815 |
+
if (this.temporaryEvadeMode && this.evadeTimer > 0) {
|
| 816 |
+
this.evadeTimer -= deltaTime;
|
| 817 |
+
if (this.evadeTimer <= 0) {
|
| 818 |
+
this.temporaryEvadeMode = false;
|
| 819 |
+
}
|
| 820 |
+
}
|
| 821 |
+
|
| 822 |
const distanceToPlayer = this.position.distanceTo(playerPosition);
|
| 823 |
|
| 824 |
+
// ์ํ ๊ฒฐ์ - ์ผ์์ ํํผ ๋ชจ๋๊ฐ ์ฐ์
|
| 825 |
+
if (this.temporaryEvadeMode) {
|
| 826 |
+
this.aiState = 'evade';
|
| 827 |
+
} else if (distanceToPlayer <= 3000) {
|
| 828 |
this.aiState = 'combat';
|
| 829 |
} else {
|
| 830 |
this.aiState = 'patrol';
|
| 831 |
}
|
| 832 |
|
| 833 |
+
// ์ถฉ๋ ํํผ ๊ณ์ฐ (ํญ์ ์คํ)
|
| 834 |
this.calculateAvoidance();
|
| 835 |
|
| 836 |
+
// ์ถฉ๋ ์์ธก ๊ฒ์ฌ
|
| 837 |
+
this.checkCollisionPrediction(deltaTime);
|
| 838 |
+
|
| 839 |
// AI ํ๋ ์คํ
|
| 840 |
switch (this.aiState) {
|
| 841 |
case 'patrol':
|
|
|
|
| 844 |
case 'combat':
|
| 845 |
this.executeCombat(playerPosition, deltaTime);
|
| 846 |
break;
|
| 847 |
+
case 'evade':
|
| 848 |
+
this.executeEmergencyEvade(deltaTime);
|
| 849 |
+
break;
|
| 850 |
}
|
| 851 |
|
| 852 |
// ๋ฌผ๋ฆฌ ์
๋ฐ์ดํธ
|
|
|
|
| 906 |
}
|
| 907 |
}
|
| 908 |
|
| 909 |
+
// ์๋ก์ด ๋ฉ์๋: ์ถฉ๋ ์์ธก
|
| 910 |
+
checkCollisionPrediction(deltaTime) {
|
| 911 |
+
if (!this.nearbyEnemies) return;
|
| 912 |
+
|
| 913 |
+
// 2์ด ํ ์์ ์์น ๊ณ์ฐ
|
| 914 |
+
const predictTime = 2.0;
|
| 915 |
+
const forward = new THREE.Vector3(0, 0, 1).applyEuler(this.rotation);
|
| 916 |
+
this.predictedPosition.copy(this.position).add(forward.multiplyScalar(this.speed * predictTime));
|
| 917 |
+
|
| 918 |
+
this.nearbyEnemies.forEach(enemy => {
|
| 919 |
+
if (enemy === this || !enemy.position) return;
|
| 920 |
+
|
| 921 |
+
// ๋ค๋ฅธ ์ ๊ธฐ์ ์์ ์์น
|
| 922 |
+
const enemyForward = new THREE.Vector3(0, 0, 1).applyEuler(enemy.rotation);
|
| 923 |
+
const enemyPredicted = enemy.position.clone().add(enemyForward.multiplyScalar(enemy.speed * predictTime));
|
| 924 |
+
|
| 925 |
+
// ์์ ๊ฑฐ๋ฆฌ
|
| 926 |
+
const predictedDistance = this.predictedPosition.distanceTo(enemyPredicted);
|
| 927 |
+
|
| 928 |
+
// 150m ์ด๋ด๋ก ์ ๊ทผ ์์ ์ ์ฌ์ ํํผ
|
| 929 |
+
if (predictedDistance < 150) {
|
| 930 |
+
// ์๋ฐฉ์ ํํผ ๋ฐฉํฅ ์ค์
|
| 931 |
+
const avoidDir = this.predictedPosition.clone().sub(enemyPredicted).normalize();
|
| 932 |
+
|
| 933 |
+
// ์์ง ๋ถ๋ฆฌ ์ถ๊ฐ
|
| 934 |
+
if (this.position.y > enemy.position.y) {
|
| 935 |
+
avoidDir.y += 0.3;
|
| 936 |
+
} else {
|
| 937 |
+
avoidDir.y -= 0.3;
|
| 938 |
+
}
|
| 939 |
+
|
| 940 |
+
this.avoidanceVector.add(avoidDir.multiplyScalar(1.5));
|
| 941 |
+
}
|
| 942 |
+
});
|
| 943 |
+
}
|
| 944 |
+
|
| 945 |
+
// ์๋ก์ด ๋ฉ์๋: ๊ธด๊ธ ํํผ ์คํ
|
| 946 |
+
executeEmergencyEvade(deltaTime) {
|
| 947 |
+
// ํํผ ๋ฒกํฐ๊ฐ ์์ผ๋ฉด ๊ทธ ๋ฐฉํฅ์ผ๋ก, ์์ผ๋ฉด ๊ธ์์น
|
| 948 |
+
if (this.avoidanceVector.length() > 0) {
|
| 949 |
+
const evadeDirection = this.avoidanceVector.clone().normalize();
|
| 950 |
+
|
| 951 |
+
// ๊ธ์ ํ
|
| 952 |
+
const targetYaw = Math.atan2(evadeDirection.x, evadeDirection.z);
|
| 953 |
+
const targetPitch = Math.asin(-evadeDirection.y) * 0.5; // ํผ์น๋ ์ ๋ฐ๋ง
|
| 954 |
+
|
| 955 |
+
// ๋น ๋ฅธ ํ์ ์๋
|
| 956 |
+
const emergencyTurnSpeed = this.turnSpeed * 2.0;
|
| 957 |
+
const maxTurnRate = emergencyTurnSpeed * deltaTime;
|
| 958 |
+
|
| 959 |
+
// Yaw ํ์
|
| 960 |
+
const yawDiff = this.normalizeAngle(targetYaw - this.rotation.y);
|
| 961 |
+
if (Math.abs(yawDiff) > maxTurnRate) {
|
| 962 |
+
this.rotation.y += Math.sign(yawDiff) * maxTurnRate;
|
| 963 |
+
} else {
|
| 964 |
+
this.rotation.y = targetYaw;
|
| 965 |
+
}
|
| 966 |
+
|
| 967 |
+
// Pitch ํ์
|
| 968 |
+
const pitchDiff = targetPitch - this.rotation.x;
|
| 969 |
+
if (Math.abs(pitchDiff) > maxTurnRate * 0.7) {
|
| 970 |
+
this.rotation.x += Math.sign(pitchDiff) * maxTurnRate * 0.7;
|
| 971 |
+
} else {
|
| 972 |
+
this.rotation.x = targetPitch;
|
| 973 |
+
}
|
| 974 |
+
|
| 975 |
+
// ๊ธ์ ํ ์ ์ฝ๊ฐ์ ๋กค ์ถ๊ฐ
|
| 976 |
+
this.rotation.z = Math.sign(yawDiff) * Math.min(Math.abs(yawDiff), Math.PI / 6);
|
| 977 |
+
} else {
|
| 978 |
+
// ๊ธฐ๋ณธ ํํผ: ๊ธ์์น
|
| 979 |
+
this.rotation.x = -Math.PI / 6; // 30๋ ์์น
|
| 980 |
+
}
|
| 981 |
+
|
| 982 |
+
// ํํผ ์ค์๋ ์ต๋ ์๋
|
| 983 |
+
this.speed = this.maxSpeed;
|
| 984 |
+
}
|
| 985 |
|
| 986 |
smoothTurnToTarget(targetPos, deltaTime) {
|
| 987 |
// ํ๊ฒ ๋ฐฉํฅ ๊ณ์ฐ
|
|
|
|
| 989 |
direction.y *= 0.5; // ์์ง ์ด๋์ ๋ ์ ํ์ ์ผ๋ก
|
| 990 |
direction.normalize();
|
| 991 |
|
| 992 |
+
// ์ถฉ๋ ํํผ ๋ฒกํฐ ์ ์ฉ (ํํผ๊ฐ ์ฐ์ )
|
| 993 |
if (this.avoidanceVector.length() > 0) {
|
| 994 |
+
// ํํผ ๋ชจ๋์์๋ ํํผ ๋ฒกํฐ์ ์ํฅ์ ํฌ๊ฒ
|
| 995 |
+
const avoidanceStrength = this.temporaryEvadeMode ? 1.0 : 0.5;
|
| 996 |
+
direction.add(this.avoidanceVector.multiplyScalar(avoidanceStrength));
|
| 997 |
direction.normalize();
|
| 998 |
}
|
| 999 |
|
|
|
|
| 1002 |
const targetPitch = Math.asin(-direction.y);
|
| 1003 |
|
| 1004 |
// ๋ถ๋๋ฌ์ด ํ์ (์ต๋ ํ์ ์๋ ์ ํ)
|
| 1005 |
+
const turnSpeed = this.temporaryEvadeMode ? this.turnSpeed * 1.5 : this.turnSpeed;
|
| 1006 |
+
const maxTurnRate = turnSpeed * deltaTime;
|
|
|
|
|
|
|
| 1007 |
|
| 1008 |
// Yaw ํ์
|
| 1009 |
+
const yawDiff = this.normalizeAngle(targetYaw - this.rotation.y);
|
| 1010 |
if (Math.abs(yawDiff) > maxTurnRate) {
|
| 1011 |
this.rotation.y += Math.sign(yawDiff) * maxTurnRate;
|
| 1012 |
} else {
|
|
|
|
| 1015 |
|
| 1016 |
// Pitch ํ์ (์ ํ์ )
|
| 1017 |
const maxPitchRate = maxTurnRate * 0.7; // ํผ์น๋ ์กฐ๊ธ ๋ ๋น ๋ฅด๊ฒ
|
| 1018 |
+
if (Math.abs(targetPitch - this.rotation.x) > maxPitchRate) {
|
| 1019 |
+
this.rotation.x += Math.sign(targetPitch - this.rotation.x) * maxPitchRate;
|
| 1020 |
} else {
|
| 1021 |
this.rotation.x = targetPitch;
|
| 1022 |
}
|
|
|
|
| 1024 |
// Pitch ์ ํ (ยฑ40๋)
|
| 1025 |
const maxPitchAngle = Math.PI * 40 / 180; // 40๋๋ฅผ ๋ผ๋์์ผ๋ก
|
| 1026 |
this.rotation.x = THREE.MathUtils.clamp(this.rotation.x, -maxPitchAngle, maxPitchAngle);
|
| 1027 |
+
|
| 1028 |
+
// ๋กค ์๋ ๊ณ์ฐ (์ ํ ์)
|
| 1029 |
+
if (!this.temporaryEvadeMode) {
|
| 1030 |
+
this.rotation.z = -yawDiff * 0.5; // ์ ํ ๋ฐฉํฅ์ผ๋ก ๊ธฐ์ธ๊ธฐ
|
| 1031 |
+
this.rotation.z = THREE.MathUtils.clamp(this.rotation.z, -Math.PI / 4, Math.PI / 4);
|
| 1032 |
+
}
|
| 1033 |
}
|
| 1034 |
|
| 1035 |
calculateAvoidance() {
|
|
|
|
| 1038 |
if (!this.nearbyEnemies) return;
|
| 1039 |
|
| 1040 |
let avoidCount = 0;
|
| 1041 |
+
let criticalAvoidance = false;
|
| 1042 |
|
| 1043 |
this.nearbyEnemies.forEach(enemy => {
|
| 1044 |
if (enemy === this || !enemy.position) return;
|
| 1045 |
|
| 1046 |
const distance = this.position.distanceTo(enemy.position);
|
| 1047 |
+
|
| 1048 |
+
// 100m ๋ฏธ๋ง: ๊ธด๊ธ ํํผ
|
| 1049 |
+
if (distance < 100 && distance > 0) {
|
| 1050 |
+
criticalAvoidance = true;
|
| 1051 |
+
|
| 1052 |
+
// ๊ฐํ ๋ฐ๋ฐ๋ ฅ
|
| 1053 |
+
const avoidDir = this.position.clone().sub(enemy.position).normalize();
|
| 1054 |
+
const strength = 2.0; // ๋งค์ฐ ๊ฐํ ํํผ
|
| 1055 |
+
this.avoidanceVector.add(avoidDir.multiplyScalar(strength));
|
| 1056 |
+
|
| 1057 |
+
// ๊ณ ๋ ์ฐจ์ด ์ถ๊ฐ (์/์๋๋ก ๋ถ์ฐ)
|
| 1058 |
+
if (this.position.y > enemy.position.y) {
|
| 1059 |
+
this.avoidanceVector.y += 0.5; // ์๋ก
|
| 1060 |
+
} else {
|
| 1061 |
+
this.avoidanceVector.y -= 0.5; // ์๋๋ก
|
| 1062 |
+
}
|
| 1063 |
+
|
| 1064 |
+
avoidCount++;
|
| 1065 |
+
}
|
| 1066 |
+
// 100-300m: ์๋ฐฉ์ ํํผ
|
| 1067 |
+
else if (distance < 300) {
|
| 1068 |
// ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ํํผ
|
| 1069 |
const avoidDir = this.position.clone().sub(enemy.position).normalize();
|
| 1070 |
+
const strength = (300 - distance) / 200; // 100-300m ๋ฒ์์์ ๊ฐ๋ ๊ณ์ฐ
|
| 1071 |
this.avoidanceVector.add(avoidDir.multiplyScalar(strength));
|
| 1072 |
avoidCount++;
|
| 1073 |
}
|
|
|
|
| 1076 |
if (avoidCount > 0) {
|
| 1077 |
this.avoidanceVector.divideScalar(avoidCount);
|
| 1078 |
this.avoidanceVector.normalize();
|
| 1079 |
+
|
| 1080 |
+
// ๊ธด๊ธ ํํผ ์ ๋ ๊ฐํ ํํผ๋ ฅ ์ ์ฉ
|
| 1081 |
+
if (criticalAvoidance) {
|
| 1082 |
+
this.avoidanceVector.multiplyScalar(2.0);
|
| 1083 |
+
// ์ผ์์ ์ผ๋ก ์ ํฌ ์ํ ํด์
|
| 1084 |
+
this.temporaryEvadeMode = true;
|
| 1085 |
+
this.evadeTimer = 2.0; // 2์ด ๋๏ฟฝ๏ฟฝ๏ฟฝ ํํผ ์ฐ์
|
| 1086 |
+
}
|
| 1087 |
}
|
| 1088 |
}
|
| 1089 |
|