Spaces:
Running
Running
Update game.js
Browse files
game.js
CHANGED
|
@@ -74,6 +74,7 @@ class Fighter {
|
|
| 74 |
// Over-G μμ€ν
|
| 75 |
this.overG = false;
|
| 76 |
this.maxGForce = 9.0;
|
|
|
|
| 77 |
|
| 78 |
// κ²½κ³ μ μμ€ν
|
| 79 |
this.warningAudios = {
|
|
@@ -285,22 +286,56 @@ class Fighter {
|
|
| 285 |
this.rotation.y = THREE.MathUtils.lerp(this.rotation.y, this.targetYaw, rotationSpeed);
|
| 286 |
|
| 287 |
// νμ€μ μΈ μλ κ³μ°
|
| 288 |
-
const minSpeed =
|
| 289 |
const maxSpeed = 600; // μ΅λ μλ 600kt
|
| 290 |
let targetSpeed = minSpeed + (maxSpeed - minSpeed) * this.throttle;
|
| 291 |
|
| 292 |
-
// νΌμΉ κ°λμ λ°λ₯Έ μλ λ³ν
|
| 293 |
const pitchAngle = this.rotation.x;
|
|
|
|
|
|
|
|
|
|
| 294 |
if (pitchAngle < -0.1) { // κΈ°μκ° μλ‘ (μμΉ)
|
| 295 |
-
const climbFactor = Math.abs(pitchAngle) / (Math.PI /
|
| 296 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
} else if (pitchAngle > 0.1) { // κΈ°μκ° μλλ‘ (νκ°)
|
| 298 |
const diveFactor = pitchAngle / (Math.PI / 3);
|
| 299 |
targetSpeed *= (1 + diveFactor * 0.2); // μ΅λ 20% κ°μ
|
| 300 |
}
|
| 301 |
|
| 302 |
-
//
|
| 303 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 304 |
|
| 305 |
// μ€ν¨ κ²½κ³ : 300kt μ΄νμμ μ€ν¨ μν
|
| 306 |
const speedKnots = this.speed * 1.94384; // m/s to knots
|
|
@@ -308,24 +343,38 @@ class Fighter {
|
|
| 308 |
|
| 309 |
// μ€ν¨ μνμμμ 물리 ν¨κ³Ό
|
| 310 |
if (this.stallWarning) {
|
| 311 |
-
//
|
| 312 |
-
this.targetPitch
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
this.rotation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 316 |
}
|
| 317 |
|
| 318 |
// μλ λ²‘ν° κ³μ°
|
| 319 |
const noseDirection = new THREE.Vector3(0, 0, 1);
|
| 320 |
noseDirection.applyEuler(this.rotation);
|
| 321 |
-
this.velocity = noseDirection.multiplyScalar(this.speed);
|
| 322 |
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
|
| 327 |
-
//
|
| 328 |
if (!this.stallWarning) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
const liftFactor = (this.speed / maxSpeed) * 0.8;
|
| 330 |
const lift = gravityEffect * liftFactor;
|
| 331 |
this.velocity.y += lift;
|
|
@@ -370,17 +419,9 @@ class Fighter {
|
|
| 370 |
this.warningBlinkState = !this.warningBlinkState;
|
| 371 |
}
|
| 372 |
|
| 373 |
-
// κ³ λ
|
| 374 |
this.altitude = this.position.y;
|
| 375 |
|
| 376 |
-
// G-Force κ³μ°
|
| 377 |
-
const turnRate = Math.abs(bankTurnRate) * 100;
|
| 378 |
-
const pitchRate = Math.abs(this.rotation.x - this.targetPitch) * 10;
|
| 379 |
-
this.gForce = 1.0 + turnRate + pitchRate + (Math.abs(this.rotation.z) * 3);
|
| 380 |
-
|
| 381 |
-
// Over-G 체ν¬
|
| 382 |
-
this.overG = this.gForce > this.maxGForce;
|
| 383 |
-
|
| 384 |
// κ²½κ³ μ μ
λ°μ΄νΈ (μμ§ μ리λ κ³μ μ μ§)
|
| 385 |
this.updateWarningAudios();
|
| 386 |
|
|
@@ -1007,7 +1048,6 @@ class Game {
|
|
| 1007 |
|
| 1008 |
if (this.fighter.stallWarning) {
|
| 1009 |
warningText += 'STALL WARNING\n';
|
| 1010 |
-
warningText += '<span style="font-size: 16px;">INCREASE THROTTLE</span>\n';
|
| 1011 |
}
|
| 1012 |
|
| 1013 |
if (this.fighter.overG) {
|
|
@@ -1019,6 +1059,75 @@ class Game {
|
|
| 1019 |
document.body.appendChild(warningContainer);
|
| 1020 |
}
|
| 1021 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1022 |
}
|
| 1023 |
|
| 1024 |
updateRadar() {
|
|
@@ -1163,9 +1272,15 @@ class Game {
|
|
| 1163 |
|
| 1164 |
document.exitPointerLock();
|
| 1165 |
|
| 1166 |
-
|
|
|
|
| 1167 |
existingWarnings.forEach(w => w.remove());
|
| 1168 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1169 |
const gameOverDiv = document.createElement('div');
|
| 1170 |
gameOverDiv.className = 'start-screen';
|
| 1171 |
gameOverDiv.style.display = 'flex';
|
|
|
|
| 74 |
// Over-G μμ€ν
|
| 75 |
this.overG = false;
|
| 76 |
this.maxGForce = 9.0;
|
| 77 |
+
this.overGTimer = 0; // Over-G μ§μ μκ°
|
| 78 |
|
| 79 |
// κ²½κ³ μ μμ€ν
|
| 80 |
this.warningAudios = {
|
|
|
|
| 286 |
this.rotation.y = THREE.MathUtils.lerp(this.rotation.y, this.targetYaw, rotationSpeed);
|
| 287 |
|
| 288 |
// νμ€μ μΈ μλ κ³μ°
|
| 289 |
+
const minSpeed = 0; // μ΅μ μλ 0kt
|
| 290 |
const maxSpeed = 600; // μ΅λ μλ 600kt
|
| 291 |
let targetSpeed = minSpeed + (maxSpeed - minSpeed) * this.throttle;
|
| 292 |
|
| 293 |
+
// νΌμΉ κ°λμ λ°λ₯Έ μλ λ³ν
|
| 294 |
const pitchAngle = this.rotation.x;
|
| 295 |
+
const pitchDegrees = Math.abs(pitchAngle) * (180 / Math.PI);
|
| 296 |
+
|
| 297 |
+
// κΈ°μκ° μλ₯Ό ν₯νκ³ μμ κ²½μ° λΉ λ₯Έ μλ κ°μ
|
| 298 |
if (pitchAngle < -0.1) { // κΈ°μκ° μλ‘ (μμΉ)
|
| 299 |
+
const climbFactor = Math.abs(pitchAngle) / (Math.PI / 2); // 90λ κΈ°μ€
|
| 300 |
+
if (pitchDegrees > 30) { // 30λ μ΄μμΌ λ κΈκ²©ν κ°μ
|
| 301 |
+
targetSpeed *= Math.max(0, 1 - climbFactor * 1.5); // μ΅λ 150% κ°μ (0ktκΉμ§)
|
| 302 |
+
} else {
|
| 303 |
+
targetSpeed *= (1 - climbFactor * 0.3); // μ μμ μΈ κ°μ
|
| 304 |
+
}
|
| 305 |
} else if (pitchAngle > 0.1) { // κΈ°μκ° μλλ‘ (νκ°)
|
| 306 |
const diveFactor = pitchAngle / (Math.PI / 3);
|
| 307 |
targetSpeed *= (1 + diveFactor * 0.2); // μ΅λ 20% κ°μ
|
| 308 |
}
|
| 309 |
|
| 310 |
+
// Over-G μν μ²λ¦¬
|
| 311 |
+
const turnRate = Math.abs(bankTurnRate) * 100;
|
| 312 |
+
const pitchRate = Math.abs(this.rotation.x - this.targetPitch) * 10;
|
| 313 |
+
this.gForce = 1.0 + turnRate + pitchRate + (Math.abs(this.rotation.z) * 3);
|
| 314 |
+
|
| 315 |
+
this.overG = this.gForce > this.maxGForce;
|
| 316 |
+
|
| 317 |
+
// Over-G νμ΄λ¨Έ μ
λ°μ΄νΈ
|
| 318 |
+
if (this.overG) {
|
| 319 |
+
this.overGTimer += deltaTime;
|
| 320 |
+
|
| 321 |
+
// 3μ΄ μ΄μ Over-G μνμΌ κ²½μ°
|
| 322 |
+
if (this.overGTimer > 3.0) {
|
| 323 |
+
// μλ κΈκ²©ν κ°μ (0ktκΉμ§)
|
| 324 |
+
targetSpeed *= Math.max(0, 1 - (this.overGTimer - 3.0) * 0.5);
|
| 325 |
+
|
| 326 |
+
// μμΌ νλ¦Ό ν¨κ³Όλ UIμμ μ²λ¦¬
|
| 327 |
+
}
|
| 328 |
+
} else {
|
| 329 |
+
this.overGTimer = 0; // Over-G μνκ° μλλ©΄ νμ΄λ¨Έ 리μ
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
// μλ λ³νλ₯Ό μ²μ²ν μ μ© (μ€ν¨ μνμΌ λλ μ μΈ)
|
| 333 |
+
if (this.stallWarning) {
|
| 334 |
+
// μ€ν¨ μνμμλ μλκ° λΉ λ₯΄κ² κ°μ
|
| 335 |
+
this.speed = Math.max(0, this.speed - deltaTime * 200);
|
| 336 |
+
} else {
|
| 337 |
+
this.speed = THREE.MathUtils.lerp(this.speed, targetSpeed, deltaTime * 0.5);
|
| 338 |
+
}
|
| 339 |
|
| 340 |
// μ€ν¨ κ²½κ³ : 300kt μ΄νμμ μ€ν¨ μν
|
| 341 |
const speedKnots = this.speed * 1.94384; // m/s to knots
|
|
|
|
| 343 |
|
| 344 |
// μ€ν¨ μνμμμ 물리 ν¨κ³Ό
|
| 345 |
if (this.stallWarning) {
|
| 346 |
+
// λ°λ₯μΌλ‘ μΆλ½νλ©° κ°μλκ° λΉ λ₯΄κ² λΆμ
|
| 347 |
+
this.targetPitch = Math.min(Math.PI / 3, this.targetPitch + deltaTime * 2.0); // κΈ°μκ° λΉ λ₯΄κ² μλλ‘
|
| 348 |
+
|
| 349 |
+
// μ‘°μ’
λΆλ₯ μν
|
| 350 |
+
this.rotation.x += (Math.random() - 0.5) * deltaTime * 0.5;
|
| 351 |
+
this.rotation.z += (Math.random() - 0.5) * deltaTime * 0.5;
|
| 352 |
+
|
| 353 |
+
// μ€λ ₯μ μν κ°μ
|
| 354 |
+
const gravityAcceleration = GAME_CONSTANTS.GRAVITY * deltaTime * 3.0; // 3λ°° μ€λ ₯
|
| 355 |
+
this.velocity.y -= gravityAcceleration;
|
| 356 |
}
|
| 357 |
|
| 358 |
// μλ λ²‘ν° κ³μ°
|
| 359 |
const noseDirection = new THREE.Vector3(0, 0, 1);
|
| 360 |
noseDirection.applyEuler(this.rotation);
|
|
|
|
| 361 |
|
| 362 |
+
if (!this.stallWarning) {
|
| 363 |
+
// μ μ λΉν μ
|
| 364 |
+
this.velocity = noseDirection.multiplyScalar(this.speed);
|
| 365 |
+
} else {
|
| 366 |
+
// μ€ν¨ μμλ μ€λ ₯μ΄ μ£Όλμ
|
| 367 |
+
this.velocity.x = noseDirection.x * this.speed * 0.3; // μ λ°© μλ κ°μ
|
| 368 |
+
this.velocity.z = noseDirection.z * this.speed * 0.3;
|
| 369 |
+
// y μλλ μμμ μ€λ ₯μΌλ‘ μ²λ¦¬λ¨
|
| 370 |
+
}
|
| 371 |
|
| 372 |
+
// μ μ λΉν μ μ€λ ₯ ν¨κ³Ό
|
| 373 |
if (!this.stallWarning) {
|
| 374 |
+
const gravityEffect = GAME_CONSTANTS.GRAVITY * deltaTime * 0.15;
|
| 375 |
+
this.velocity.y -= gravityEffect;
|
| 376 |
+
|
| 377 |
+
// μλ ₯ ν¨κ³Ό (μλμ λΉλ‘)
|
| 378 |
const liftFactor = (this.speed / maxSpeed) * 0.8;
|
| 379 |
const lift = gravityEffect * liftFactor;
|
| 380 |
this.velocity.y += lift;
|
|
|
|
| 419 |
this.warningBlinkState = !this.warningBlinkState;
|
| 420 |
}
|
| 421 |
|
| 422 |
+
// κ³ λ κ³μ°
|
| 423 |
this.altitude = this.position.y;
|
| 424 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 425 |
// κ²½κ³ μ μ
λ°μ΄νΈ (μμ§ μ리λ κ³μ μ μ§)
|
| 426 |
this.updateWarningAudios();
|
| 427 |
|
|
|
|
| 1048 |
|
| 1049 |
if (this.fighter.stallWarning) {
|
| 1050 |
warningText += 'STALL WARNING\n';
|
|
|
|
| 1051 |
}
|
| 1052 |
|
| 1053 |
if (this.fighter.overG) {
|
|
|
|
| 1059 |
document.body.appendChild(warningContainer);
|
| 1060 |
}
|
| 1061 |
}
|
| 1062 |
+
|
| 1063 |
+
// μ€ν¨ μνμΌ λ νλ¨μ "Press W to Escape" κ²½κ³ νμ
|
| 1064 |
+
if (this.fighter.stallWarning) {
|
| 1065 |
+
const stallEscapeWarning = document.createElement('div');
|
| 1066 |
+
stallEscapeWarning.className = 'stall-escape-warning';
|
| 1067 |
+
stallEscapeWarning.style.cssText = `
|
| 1068 |
+
position: fixed;
|
| 1069 |
+
bottom: 100px;
|
| 1070 |
+
left: 50%;
|
| 1071 |
+
transform: translateX(-50%);
|
| 1072 |
+
background: rgba(255, 0, 0, 0.8);
|
| 1073 |
+
color: #ffffff;
|
| 1074 |
+
font-size: 28px;
|
| 1075 |
+
font-weight: bold;
|
| 1076 |
+
padding: 15px 30px;
|
| 1077 |
+
border: 3px solid #ff0000;
|
| 1078 |
+
border-radius: 10px;
|
| 1079 |
+
z-index: 1600;
|
| 1080 |
+
text-align: center;
|
| 1081 |
+
animation: blink 0.5s infinite;
|
| 1082 |
+
`;
|
| 1083 |
+
stallEscapeWarning.innerHTML = 'PRESS W TO ESCAPE';
|
| 1084 |
+
document.body.appendChild(stallEscapeWarning);
|
| 1085 |
+
|
| 1086 |
+
// μ λλ©μ΄μ
μ€νμΌ μΆκ°
|
| 1087 |
+
if (!document.getElementById('blinkAnimation')) {
|
| 1088 |
+
const style = document.createElement('style');
|
| 1089 |
+
style.id = 'blinkAnimation';
|
| 1090 |
+
style.innerHTML = `
|
| 1091 |
+
@keyframes blink {
|
| 1092 |
+
0%, 50% { opacity: 1; }
|
| 1093 |
+
51%, 100% { opacity: 0.3; }
|
| 1094 |
+
}
|
| 1095 |
+
`;
|
| 1096 |
+
document.head.appendChild(style);
|
| 1097 |
+
}
|
| 1098 |
+
}
|
| 1099 |
+
|
| 1100 |
+
// Over-G 3μ΄ μ΄μμΌ λ μμΌ νλ¦Ό ν¨κ³Ό
|
| 1101 |
+
if (this.fighter.overG && this.fighter.overGTimer > 3.0) {
|
| 1102 |
+
let blurEffect = document.getElementById('overGBlurEffect');
|
| 1103 |
+
if (!blurEffect) {
|
| 1104 |
+
blurEffect = document.createElement('div');
|
| 1105 |
+
blurEffect.id = 'overGBlurEffect';
|
| 1106 |
+
document.body.appendChild(blurEffect);
|
| 1107 |
+
}
|
| 1108 |
+
|
| 1109 |
+
// Over-G μ§μ μκ°μ λ°λΌ νλ¦Ό μ λ μ¦κ°
|
| 1110 |
+
const blurIntensity = Math.min((this.fighter.overGTimer - 3.0) * 0.3, 0.8);
|
| 1111 |
+
blurEffect.style.cssText = `
|
| 1112 |
+
position: fixed;
|
| 1113 |
+
top: 0;
|
| 1114 |
+
left: 0;
|
| 1115 |
+
width: 100%;
|
| 1116 |
+
height: 100%;
|
| 1117 |
+
background: radial-gradient(ellipse at center,
|
| 1118 |
+
transparent 20%,
|
| 1119 |
+
rgba(0, 0, 0, ${blurIntensity}) 50%,
|
| 1120 |
+
rgba(0, 0, 0, ${blurIntensity + 0.2}) 100%);
|
| 1121 |
+
pointer-events: none;
|
| 1122 |
+
z-index: 1400;
|
| 1123 |
+
`;
|
| 1124 |
+
} else {
|
| 1125 |
+
// Over-G μνκ° μλλ©΄ ν¨κ³Ό μ κ±°
|
| 1126 |
+
const blurEffect = document.getElementById('overGBlurEffect');
|
| 1127 |
+
if (blurEffect) {
|
| 1128 |
+
blurEffect.remove();
|
| 1129 |
+
}
|
| 1130 |
+
}
|
| 1131 |
}
|
| 1132 |
|
| 1133 |
updateRadar() {
|
|
|
|
| 1272 |
|
| 1273 |
document.exitPointerLock();
|
| 1274 |
|
| 1275 |
+
// λͺ¨λ κ²½κ³ λ° ν¨κ³Ό μ κ±°
|
| 1276 |
+
const existingWarnings = document.querySelectorAll('.warning-message, .stall-escape-warning');
|
| 1277 |
existingWarnings.forEach(w => w.remove());
|
| 1278 |
|
| 1279 |
+
const blurEffect = document.getElementById('overGBlurEffect');
|
| 1280 |
+
if (blurEffect) {
|
| 1281 |
+
blurEffect.remove();
|
| 1282 |
+
}
|
| 1283 |
+
|
| 1284 |
const gameOverDiv = document.createElement('div');
|
| 1285 |
gameOverDiv.className = 'start-screen';
|
| 1286 |
gameOverDiv.style.display = 'flex';
|