Spaces:
Running
Running
Update game.js
Browse files
game.js
CHANGED
@@ -461,230 +461,23 @@ class Fighter {
|
|
461 |
this.stallEscapeProgress = 0; // μ€ν¨ μ§μ
μ μ§νλ μ΄κΈ°ν
|
462 |
}
|
463 |
|
464 |
-
//
|
465 |
-
} else if (this.stallWarning && !this.escapeKeyPressed) {
|
466 |
-
// Fν€λ₯Ό λμΌλ©΄ μ§νλ κ°μ
|
467 |
-
this.stallEscapeProgress = Math.max(0, this.stallEscapeProgress - deltaTime * 2);
|
468 |
-
}
|
469 |
-
|
470 |
-
// μλ λ³ν μ μ©
|
471 |
if (this.stallWarning) {
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
477 |
} else {
|
478 |
-
//
|
479 |
-
this.
|
480 |
}
|
481 |
-
} else {
|
482 |
-
// μ μ λΉν μ μλ λ³ν
|
483 |
-
this.speed = THREE.MathUtils.lerp(this.speed, targetSpeed, deltaTime * 0.5);
|
484 |
-
}
|
485 |
-
|
486 |
-
// μ€ν¨ μνμμμ 물리 ν¨κ³Ό
|
487 |
-
if (this.stallWarning) {
|
488 |
-
// λ°λ₯μΌλ‘ μΆλ½νλ©° κ°μλκ° λΉ λ₯΄κ² λΆμ
|
489 |
-
this.targetPitch = Math.min(Math.PI / 2.5, this.targetPitch + deltaTime * 2.5); // κΈ°μκ° λ κ·Ήλ¨μ μΌλ‘ μλλ‘ (72λκΉμ§)
|
490 |
-
|
491 |
-
// μ‘°μ’
λΆλ₯ μν - λ μ¬ν νλ€λ¦Ό
|
492 |
-
this.rotation.x += (Math.random() - 0.5) * deltaTime * 0.8;
|
493 |
-
this.rotation.z += (Math.random() - 0.5) * deltaTime * 0.8;
|
494 |
-
|
495 |
-
// μ€λ ₯μ μν κ°μ
|
496 |
-
const gravityAcceleration = GAME_CONSTANTS.GRAVITY * deltaTime * 3.0; // 3λ°° μ€λ ₯
|
497 |
-
this.velocity.y -= gravityAcceleration;
|
498 |
-
}
|
499 |
-
|
500 |
-
// μλ λ²‘ν° κ³μ°
|
501 |
-
const noseDirection = new THREE.Vector3(0, 0, 1);
|
502 |
-
noseDirection.applyEuler(this.rotation);
|
503 |
-
|
504 |
-
if (!this.stallWarning) {
|
505 |
-
// μ μ λΉν μ
|
506 |
-
this.velocity = noseDirection.multiplyScalar(this.speed);
|
507 |
-
} else {
|
508 |
-
// μ€ν¨ μμλ μ€λ ₯μ΄ μ£Όλμ μ΄μ§λ§ λ€μ΄λΉ μλλ λ°μ
|
509 |
-
this.velocity.x = noseDirection.x * this.speed * 0.5; // μ λ°© μλ μ¦κ°
|
510 |
-
this.velocity.z = noseDirection.z * this.speed * 0.5;
|
511 |
-
// y μλλ μμμ μ€λ ₯μΌλ‘ μ²λ¦¬λ¨
|
512 |
-
}
|
513 |
-
|
514 |
-
// μ μ λΉν μ μ€λ ₯ ν¨κ³Ό
|
515 |
-
if (!this.stallWarning) {
|
516 |
-
const gravityEffect = GAME_CONSTANTS.GRAVITY * deltaTime * 0.15;
|
517 |
-
this.velocity.y -= gravityEffect;
|
518 |
-
|
519 |
-
// μλ ₯ ν¨κ³Ό (μλμ λΉλ‘)
|
520 |
-
const liftFactor = (this.speed / maxSpeed) * 0.8;
|
521 |
-
const lift = gravityEffect * liftFactor;
|
522 |
-
this.velocity.y += lift;
|
523 |
-
}
|
524 |
-
|
525 |
-
// μμΉ μ
λ°μ΄νΈ
|
526 |
-
this.position.add(this.velocity.clone().multiplyScalar(deltaTime));
|
527 |
-
|
528 |
-
// μ§ν μΆ©λ κ°μ§ (Game ν΄λμ€μμ μ²λ¦¬νλλ‘ μμ )
|
529 |
-
|
530 |
-
// μ§λ©΄ μΆ©λ
|
531 |
-
if (this.position.y <= GAME_CONSTANTS.MIN_ALTITUDE) {
|
532 |
-
this.position.y = GAME_CONSTANTS.MIN_ALTITUDE;
|
533 |
-
this.health = 0;
|
534 |
-
return;
|
535 |
-
}
|
536 |
-
|
537 |
-
// μ΅λ κ³ λ μ ν
|
538 |
-
if (this.position.y > GAME_CONSTANTS.MAX_ALTITUDE) {
|
539 |
-
this.position.y = GAME_CONSTANTS.MAX_ALTITUDE;
|
540 |
-
this.altitudeWarning = true;
|
541 |
-
if (this.velocity.y > 0) this.velocity.y = 0;
|
542 |
-
} else {
|
543 |
-
this.altitudeWarning = false;
|
544 |
-
}
|
545 |
-
|
546 |
-
// λ§΅ κ²½κ³ μ²λ¦¬
|
547 |
-
const mapLimit = GAME_CONSTANTS.MAP_SIZE / 2;
|
548 |
-
if (this.position.x > mapLimit) this.position.x = -mapLimit;
|
549 |
-
if (this.position.x < -mapLimit) this.position.x = mapLimit;
|
550 |
-
if (this.position.z > mapLimit) this.position.z = -mapLimit;
|
551 |
-
if (this.position.z < -mapLimit) this.position.z = mapLimit;
|
552 |
-
|
553 |
-
// λ©μ μμΉ λ° νμ μ
λ°μ΄νΈ
|
554 |
-
this.mesh.position.copy(this.position);
|
555 |
-
this.mesh.rotation.x = this.rotation.x;
|
556 |
-
this.mesh.rotation.y = this.rotation.y + 3 * Math.PI / 2;
|
557 |
-
this.mesh.rotation.z = this.rotation.z;
|
558 |
-
|
559 |
-
// κ²½κ³ κΉλΉ‘μ νμ΄λ¨Έ
|
560 |
-
this.warningBlinkTimer += deltaTime;
|
561 |
-
if (this.warningBlinkTimer >= 1.0) {
|
562 |
-
this.warningBlinkTimer = 0;
|
563 |
-
this.warningBlinkState = !this.warningBlinkState;
|
564 |
-
}
|
565 |
-
|
566 |
-
// κ³ λ κ³μ°
|
567 |
-
this.altitude = this.position.y;
|
568 |
-
|
569 |
-
// κ²½κ³ μ μ
λ°μ΄νΈ (μμ§ μ리λ κ³μ μ μ§)
|
570 |
-
this.updateWarningAudios();
|
571 |
-
|
572 |
-
// μμ§ μ리 λ³Όλ₯¨μ μ€λ‘νμ μ°λ
|
573 |
-
if (this.warningAudios.normal && !this.warningAudios.normal.paused) {
|
574 |
-
this.warningAudios.normal.volume = 0.3 + this.throttle * 0.4; // 0.3~0.7
|
575 |
-
}
|
576 |
-
} Fν€λ₯Ό λμΌλ©΄ μ§νλ κ°μ
|
577 |
-
this.stallEscapeProgress = Math.max(0, this.stallEscapeProgress - deltaTime * 2);
|
578 |
-
}
|
579 |
-
|
580 |
-
// μλ λ³ν μ μ©
|
581 |
-
if (this.stallWarning) {
|
582 |
-
// μ€ν¨ μνμμμ μλ λ³ν
|
583 |
-
if (pitchAngle > 0.1) { // κΈ°μκ° μλλ₯Ό ν₯ν λ
|
584 |
-
// λ€μ΄λΉμΌλ‘ μΈν μλ μ¦κ°
|
585 |
-
const diveSpeedGain = Math.min(pitchAngle * 300, 200); // μ΅λ 200m/s μ¦κ°
|
586 |
-
this.speed = Math.min(maxSpeed, this.speed + diveSpeedGain * deltaTime);
|
587 |
-
} else {
|
588 |
-
// κΈ°μκ° μλ₯Ό ν₯νκ±°λ μνμΌ λλ μλ κ°μ
|
589 |
-
this.speed = Math.max(0, this.speed - deltaTime * 100);
|
590 |
-
}
|
591 |
-
} else {
|
592 |
-
// μ μ λΉν μ μλ λ³ν
|
593 |
-
this.speed = THREE.MathUtils.lerp(this.speed, targetSpeed, deltaTime * 0.5);
|
594 |
-
}
|
595 |
-
|
596 |
-
// μ€ν¨ μνμμμ 물리 ν¨κ³Ό
|
597 |
-
if (this.stallWarning) {
|
598 |
-
// λ°λ₯μΌλ‘ μΆλ½νλ©° κ°μλκ° λΉ λ₯΄κ² λΆμ
|
599 |
-
this.targetPitch = Math.min(Math.PI / 2.5, this.targetPitch + deltaTime * 2.5); // κΈ°μκ° λ κ·Ήλ¨μ μΌλ‘ μλλ‘ (72λκΉμ§)
|
600 |
-
|
601 |
-
// μ‘°μ’
λΆλ₯ μν - λ μ¬ν νλ€λ¦Ό
|
602 |
-
this.rotation.x += (Math.random() - 0.5) * deltaTime * 0.8;
|
603 |
-
this.rotation.z += (Math.random() - 0.5) * deltaTime * 0.8;
|
604 |
-
|
605 |
-
// μ€λ ₯μ μν κ°μ
|
606 |
-
const gravityAcceleration = GAME_CONSTANTS.GRAVITY * deltaTime * 3.0; // 3λ°° μ€λ ₯
|
607 |
-
this.velocity.y -= gravityAcceleration;
|
608 |
-
}
|
609 |
-
|
610 |
-
// μλ λ²‘ν° κ³μ°
|
611 |
-
const noseDirection = new THREE.Vector3(0, 0, 1);
|
612 |
-
noseDirection.applyEuler(this.rotation);
|
613 |
-
|
614 |
-
if (!this.stallWarning) {
|
615 |
-
// μ μ λΉν μ
|
616 |
-
this.velocity = noseDirection.multiplyScalar(this.speed);
|
617 |
-
} else {
|
618 |
-
// μ€ν¨ μμλ μ€λ ₯μ΄ μ£Όλμ μ΄μ§λ§ λ€μ΄λΉ μλλ λ°μ
|
619 |
-
this.velocity.x = noseDirection.x * this.speed * 0.5; // μ λ°© μλ μ¦κ°
|
620 |
-
this.velocity.z = noseDirection.z * this.speed * 0.5;
|
621 |
-
// y μλλ μμμ μ€λ ₯μΌλ‘ μ²λ¦¬λ¨
|
622 |
-
}
|
623 |
-
|
624 |
-
// μ μ λΉν μ μ€λ ₯ ν¨κ³Ό
|
625 |
-
if (!this.stallWarning) {
|
626 |
-
const gravityEffect = GAME_CONSTANTS.GRAVITY * deltaTime * 0.15;
|
627 |
-
this.velocity.y -= gravityEffect;
|
628 |
-
|
629 |
-
// μλ ₯ ν¨κ³Ό (μλμ λΉλ‘)
|
630 |
-
const liftFactor = (this.speed / maxSpeed) * 0.8;
|
631 |
-
const lift = gravityEffect * liftFactor;
|
632 |
-
this.velocity.y += lift;
|
633 |
-
}
|
634 |
-
|
635 |
-
// μμΉ μ
λ°μ΄νΈ
|
636 |
-
this.position.add(this.velocity.clone().multiplyScalar(deltaTime));
|
637 |
-
|
638 |
-
// μ§ν μΆ©λ κ°μ§ (Game ν΄λμ€μμ μ²λ¦¬νλλ‘ μμ )
|
639 |
-
|
640 |
-
// μ§λ©΄ μΆ©λ
|
641 |
-
if (this.position.y <= GAME_CONSTANTS.MIN_ALTITUDE) {
|
642 |
-
this.position.y = GAME_CONSTANTS.MIN_ALTITUDE;
|
643 |
-
this.health = 0;
|
644 |
-
return;
|
645 |
-
}
|
646 |
-
|
647 |
-
// μ΅λ κ³ λ μ ν
|
648 |
-
if (this.position.y > GAME_CONSTANTS.MAX_ALTITUDE) {
|
649 |
-
this.position.y = GAME_CONSTANTS.MAX_ALTITUDE;
|
650 |
-
this.altitudeWarning = true;
|
651 |
-
if (this.velocity.y > 0) this.velocity.y = 0;
|
652 |
-
} else {
|
653 |
-
this.altitudeWarning = false;
|
654 |
-
}
|
655 |
-
|
656 |
-
// λ§΅ κ²½κ³ μ²λ¦¬
|
657 |
-
const mapLimit = GAME_CONSTANTS.MAP_SIZE / 2;
|
658 |
-
if (this.position.x > mapLimit) this.position.x = -mapLimit;
|
659 |
-
if (this.position.x < -mapLimit) this.position.x = mapLimit;
|
660 |
-
if (this.position.z > mapLimit) this.position.z = -mapLimit;
|
661 |
-
if (this.position.z < -mapLimit) this.position.z = mapLimit;
|
662 |
-
|
663 |
-
// λ©μ μμΉ λ° νμ μ
λ°μ΄νΈ
|
664 |
-
this.mesh.position.copy(this.position);
|
665 |
-
this.mesh.rotation.x = this.rotation.x;
|
666 |
-
this.mesh.rotation.y = this.rotation.y + 3 * Math.PI / 2;
|
667 |
-
this.mesh.rotation.z = this.rotation.z;
|
668 |
-
|
669 |
-
// κ²½κ³ κΉλΉ‘μ νμ΄λ¨Έ
|
670 |
-
this.warningBlinkTimer += deltaTime;
|
671 |
-
if (this.warningBlinkTimer >= 1.0) {
|
672 |
-
this.warningBlinkTimer = 0;
|
673 |
-
this.warningBlinkState = !this.warningBlinkState;
|
674 |
-
}
|
675 |
-
|
676 |
-
// κ³ λ κ³μ°
|
677 |
-
this.altitude = this.position.y;
|
678 |
-
|
679 |
-
// κ²½κ³ μ μ
λ°μ΄νΈ (μμ§ μ리λ κ³μ μ μ§)
|
680 |
-
this.updateWarningAudios();
|
681 |
-
|
682 |
-
// μμ§ μ리 λ³Όλ₯¨μ μ€λ‘νμ μ°λ
|
683 |
-
if (this.warningAudios.normal && !this.warningAudios.normal.paused) {
|
684 |
-
this.warningAudios.normal.volume = 0.3 + this.throttle * 0.4; // 0.3~0.7
|
685 |
-
}
|
686 |
-
} Fν€λ₯Ό λμΌλ©΄ μ§νλ κ°μ
|
687 |
-
this.stallEscapeProgress = Math.max(0, this.stallEscapeProgress - deltaTime * 2);
|
688 |
}
|
689 |
|
690 |
// μλ λ³ν μ μ©
|
@@ -1440,362 +1233,6 @@ class Game {
|
|
1440 |
// κ°μ λ κ΅¬λ¦ μΆκ°
|
1441 |
this.addClouds();
|
1442 |
}
|
1443 |
-
|
1444 |
-
createProceduralTerrain() {
|
1445 |
-
// μ§ν μμ±μ μν μΈκ·Έλ¨ΌνΈ
|
1446 |
-
const segments = 100;
|
1447 |
-
const terrainGeometry = new THREE.PlaneGeometry(
|
1448 |
-
GAME_CONSTANTS.MAP_SIZE,
|
1449 |
-
GAME_CONSTANTS.MAP_SIZE,
|
1450 |
-
segments,
|
1451 |
-
segments
|
1452 |
-
);
|
1453 |
-
|
1454 |
-
// λ²ν
μ€μ λ
Έμ΄μ¦ μΆκ°νμ¬ μΈλ ν¨κ³Ό
|
1455 |
-
const vertices = terrainGeometry.attributes.position.array;
|
1456 |
-
const heightMap = [];
|
1457 |
-
|
1458 |
-
for (let i = 0; i < vertices.length; i += 3) {
|
1459 |
-
const x = vertices[i];
|
1460 |
-
const y = vertices[i + 1];
|
1461 |
-
|
1462 |
-
// λ€μ€ μ€μΌμΌ λ
Έμ΄μ¦λ‘ μμ°μ€λ¬μ΄ μ§ν μμ±
|
1463 |
-
let height = 0;
|
1464 |
-
|
1465 |
-
// ν° μ€μΌμΌ μΈλ (μ£Όμ μ§ν)
|
1466 |
-
height += Math.sin(x * 0.0001) * Math.cos(y * 0.0001) * 300;
|
1467 |
-
|
1468 |
-
// μ€κ° μ€μΌμΌ μΈλ
|
1469 |
-
height += Math.sin(x * 0.0003) * Math.cos(y * 0.0003) * 150;
|
1470 |
-
|
1471 |
-
// μμ μ€μΌμΌ λν
μΌ
|
1472 |
-
height += Math.sin(x * 0.001) * Math.cos(y * 0.001) * 50;
|
1473 |
-
|
1474 |
-
// λλ€ λ
Έμ΄μ¦
|
1475 |
-
height += (Math.random() - 0.5) * 20;
|
1476 |
-
|
1477 |
-
// λ§΅ κ°μ₯μλ¦¬λ‘ κ°μλ‘ λμ΄ κ°μ (κ²½κ³ λΆλλ½κ²)
|
1478 |
-
const distanceFromCenter = Math.sqrt(x * x + y * y);
|
1479 |
-
const maxDistance = GAME_CONSTANTS.MAP_SIZE * 0.4;
|
1480 |
-
if (distanceFromCenter > maxDistance) {
|
1481 |
-
const fade = 1 - (distanceFromCenter - maxDistance) / (GAME_CONSTANTS.MAP_SIZE * 0.1);
|
1482 |
-
height *= Math.max(0, fade);
|
1483 |
-
}
|
1484 |
-
|
1485 |
-
vertices[i + 2] = height;
|
1486 |
-
heightMap.push(height);
|
1487 |
-
}
|
1488 |
-
|
1489 |
-
terrainGeometry.computeVertexNormals();
|
1490 |
-
|
1491 |
-
// λ¨μν μ΄λ‘μ μ§ν λ¨Έν°λ¦¬μΌ
|
1492 |
-
const terrainMaterial = new THREE.MeshLambertMaterial({
|
1493 |
-
color: 0x8FBC8F,
|
1494 |
-
flatShading: false
|
1495 |
-
});
|
1496 |
-
|
1497 |
-
const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
|
1498 |
-
terrain.rotation.x = -Math.PI / 2;
|
1499 |
-
terrain.receiveShadow = true;
|
1500 |
-
|
1501 |
-
// μ§ν μ 보 μ μ₯ (μ΄λͺ© λ°°μΉ λ° μΆ©λ νμ μ©)
|
1502 |
-
this.terrain = terrain;
|
1503 |
-
this.terrainGeometry = terrainGeometry;
|
1504 |
-
this.heightMap = heightMap;
|
1505 |
-
this.terrainSegments = segments;
|
1506 |
-
|
1507 |
-
this.scene.add(terrain);
|
1508 |
-
}
|
1509 |
-
|
1510 |
-
addVegetation() {
|
1511 |
-
if (!this.terrain || !this.heightMap) return;
|
1512 |
-
|
1513 |
-
// κ°λ¨ν λ무 λ°°μΉ
|
1514 |
-
const treeCount = 300;
|
1515 |
-
|
1516 |
-
for (let i = 0; i < treeCount; i++) {
|
1517 |
-
// λλ€ μμΉ μ ν
|
1518 |
-
const x = (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE * 0.8;
|
1519 |
-
const z = (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE * 0.8;
|
1520 |
-
|
1521 |
-
// ν΄λΉ μμΉμ μ§ν λμ΄ κ³μ°
|
1522 |
-
const terrainHeight = this.getTerrainHeightAt(x, z);
|
1523 |
-
|
1524 |
-
// λ무 λμ κ³³μλ λ무λ₯Ό λ°°μΉνμ§ μμ
|
1525 |
-
if (terrainHeight > 200) continue;
|
1526 |
-
|
1527 |
-
// κ°λ¨ν λ무 μμ±
|
1528 |
-
const tree = new THREE.Group();
|
1529 |
-
|
1530 |
-
// λͺΈν΅
|
1531 |
-
const trunkGeometry = new THREE.CylinderGeometry(1, 1.5, 15, 6);
|
1532 |
-
const trunkMaterial = new THREE.MeshLambertMaterial({
|
1533 |
-
color: 0x4a3c28
|
1534 |
-
});
|
1535 |
-
const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
|
1536 |
-
trunk.position.y = 7.5;
|
1537 |
-
trunk.castShadow = true;
|
1538 |
-
trunk.receiveShadow = true;
|
1539 |
-
tree.add(trunk);
|
1540 |
-
|
1541 |
-
// μ
|
1542 |
-
const leavesGeometry = new THREE.SphereGeometry(8, 6, 5);
|
1543 |
-
const leavesMaterial = new THREE.MeshLambertMaterial({
|
1544 |
-
color: 0x2d5016
|
1545 |
-
});
|
1546 |
-
const leaves = new THREE.Mesh(leavesGeometry, leavesMaterial);
|
1547 |
-
leaves.position.y = 20;
|
1548 |
-
leaves.castShadow = true;
|
1549 |
-
leaves.receiveShadow = true;
|
1550 |
-
tree.add(leaves);
|
1551 |
-
|
1552 |
-
// λ무 μμΉ λ° ν¬κΈ° μ€μ
|
1553 |
-
tree.position.set(x, terrainHeight, z);
|
1554 |
-
|
1555 |
-
// ν¬κΈ° λ³ν
|
1556 |
-
const scale = 0.8 + Math.random() * 0.4;
|
1557 |
-
tree.scale.set(scale, scale, scale);
|
1558 |
-
|
1559 |
-
// λλ€ νμ
|
1560 |
-
tree.rotation.y = Math.random() * Math.PI * 2;
|
1561 |
-
|
1562 |
-
this.scene.add(tree);
|
1563 |
-
}
|
1564 |
-
|
1565 |
-
// κ°λ¨ν κ΄λͺ© μΆκ°
|
1566 |
-
const bushCount = 200;
|
1567 |
-
|
1568 |
-
for (let i = 0; i < bushCount; i++) {
|
1569 |
-
const x = (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE * 0.8;
|
1570 |
-
const z = (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE * 0.8;
|
1571 |
-
const terrainHeight = this.getTerrainHeightAt(x, z);
|
1572 |
-
|
1573 |
-
if (terrainHeight > 150) continue;
|
1574 |
-
|
1575 |
-
const bushGeometry = new THREE.SphereGeometry(3, 5, 4);
|
1576 |
-
const bushMaterial = new THREE.MeshLambertMaterial({
|
1577 |
-
color: 0x3a5f3a
|
1578 |
-
});
|
1579 |
-
const bush = new THREE.Mesh(bushGeometry, bushMaterial);
|
1580 |
-
|
1581 |
-
bush.position.set(x, terrainHeight + 1.5, z);
|
1582 |
-
bush.scale.set(
|
1583 |
-
1 + Math.random() * 0.5,
|
1584 |
-
0.6 + Math.random() * 0.3,
|
1585 |
-
1 + Math.random() * 0.5
|
1586 |
-
);
|
1587 |
-
|
1588 |
-
bush.castShadow = true;
|
1589 |
-
bush.receiveShadow = true;
|
1590 |
-
|
1591 |
-
this.scene.add(bush);
|
1592 |
-
}
|
1593 |
-
}
|
1594 |
-
|
1595 |
-
getTerrainHeightAt(x, z) {
|
1596 |
-
if (!this.terrainGeometry || !this.heightMap) return 0;
|
1597 |
-
|
1598 |
-
// μ§ν μ’νλ₯Ό μ κ·ν
|
1599 |
-
const normalizedX = (x + GAME_CONSTANTS.MAP_SIZE / 2) / GAME_CONSTANTS.MAP_SIZE;
|
1600 |
-
const normalizedZ = (z + GAME_CONSTANTS.MAP_SIZE / 2) / GAME_CONSTANTS.MAP_SIZE;
|
1601 |
-
|
1602 |
-
// κ°μ₯ κ°κΉμ΄ λ²ν
μ€ μΈλ±μ€ μ°ΎκΈ°
|
1603 |
-
const segments = this.terrainSegments || 100; // terrainSegmentsκ° μμΌλ©΄ κΈ°λ³Έκ° μ¬μ©
|
1604 |
-
const gridX = Math.floor(normalizedX * (segments - 1));
|
1605 |
-
const gridZ = Math.floor(normalizedZ * (segments - 1));
|
1606 |
-
|
1607 |
-
if (gridX < 0 || gridX >= segments - 1 || gridZ < 0 || gridZ >= segments - 1) {
|
1608 |
-
return 0;
|
1609 |
-
}
|
1610 |
-
|
1611 |
-
const index = gridZ * segments + gridX;
|
1612 |
-
return this.heightMap[index] || 0;
|
1613 |
-
}
|
1614 |
-
|
1615 |
-
addAtmosphericEffects() {
|
1616 |
-
// κ°μ λ μ° μ§ν μμ€ν
|
1617 |
-
this.createMountainRanges();
|
1618 |
-
}
|
1619 |
-
|
1620 |
-
createMountainRanges() {
|
1621 |
-
// μ°λ§₯ μμ±μ μν μ€μ
|
1622 |
-
const mountainRanges = [
|
1623 |
-
{
|
1624 |
-
distance: GAME_CONSTANTS.MAP_SIZE * 0.6,
|
1625 |
-
count: 25,
|
1626 |
-
heightRange: { min: 2000, max: 5000 },
|
1627 |
-
widthRange: { min: 1500, max: 3000 },
|
1628 |
-
color: 0x5a6b7a
|
1629 |
-
},
|
1630 |
-
{
|
1631 |
-
distance: GAME_CONSTANTS.MAP_SIZE * 0.8,
|
1632 |
-
count: 20,
|
1633 |
-
heightRange: { min: 3000, max: 7000 },
|
1634 |
-
widthRange: { min: 2000, max: 4000 },
|
1635 |
-
color: 0x4a5b6a
|
1636 |
-
},
|
1637 |
-
{
|
1638 |
-
distance: GAME_CONSTANTS.MAP_SIZE * 1.0,
|
1639 |
-
count: 15,
|
1640 |
-
heightRange: { min: 4000, max: 9000 },
|
1641 |
-
widthRange: { min: 3000, max: 5000 },
|
1642 |
-
color: 0x3a4b5a
|
1643 |
-
}
|
1644 |
-
];
|
1645 |
-
|
1646 |
-
mountainRanges.forEach(range => {
|
1647 |
-
for (let i = 0; i < range.count; i++) {
|
1648 |
-
const angle = (i / range.count) * Math.PI * 2 + (Math.random() - 0.5) * 0.3;
|
1649 |
-
const distance = range.distance + (Math.random() - 0.5) * 1000;
|
1650 |
-
|
1651 |
-
// μ° κ·Έλ£Ή μμ± (μ¬λ¬ λ΄μ°λ¦¬λ‘ ꡬμ±)
|
1652 |
-
const mountainGroup = new THREE.Group();
|
1653 |
-
|
1654 |
-
// λ©μΈ λ΄μ°λ¦¬
|
1655 |
-
const mainPeakHeight = range.heightRange.min + Math.random() * (range.heightRange.max - range.heightRange.min);
|
1656 |
-
const mainPeakWidth = range.widthRange.min + Math.random() * (range.widthRange.max - range.widthRange.min);
|
1657 |
-
|
1658 |
-
// κ°μ λ μ° νν (νκ°λΏ λμ λ μμ°μ€λ¬μ΄ νν)
|
1659 |
-
const mainPeakGeometry = new THREE.ConeGeometry(mainPeakWidth, mainPeakHeight, 12);
|
1660 |
-
const vertices = mainPeakGeometry.attributes.position.array;
|
1661 |
-
|
1662 |
-
// μ μ λ³νμΌλ‘ λ μμ°μ€λ¬μ΄ μ° νν λ§λ€κΈ°
|
1663 |
-
for (let j = 0; j < vertices.length; j += 3) {
|
1664 |
-
const x = vertices[j];
|
1665 |
-
const y = vertices[j + 1];
|
1666 |
-
const z = vertices[j + 2];
|
1667 |
-
|
1668 |
-
// λμ΄μ λ°λ₯Έ λλ€ λ³ν
|
1669 |
-
const heightFactor = (y + mainPeakHeight * 0.5) / mainPeakHeight;
|
1670 |
-
const noise = (Math.random() - 0.5) * mainPeakWidth * 0.3 * (1 - heightFactor);
|
1671 |
-
|
1672 |
-
vertices[j] += noise;
|
1673 |
-
vertices[j + 2] += noise;
|
1674 |
-
}
|
1675 |
-
|
1676 |
-
mainPeakGeometry.computeVertexNormals();
|
1677 |
-
|
1678 |
-
const mainPeakMaterial = new THREE.MeshLambertMaterial({
|
1679 |
-
color: range.color,
|
1680 |
-
flatShading: true,
|
1681 |
-
fog: true
|
1682 |
-
});
|
1683 |
-
|
1684 |
-
const mainPeak = new THREE.Mesh(mainPeakGeometry, mainPeakMaterial);
|
1685 |
-
mountainGroup.add(mainPeak);
|
1686 |
-
|
1687 |
-
// μ£Όλ³ μμ λ΄μ°λ¦¬λ€ μΆκ°
|
1688 |
-
const subPeakCount = 2 + Math.floor(Math.random() * 3);
|
1689 |
-
for (let j = 0; j < subPeakCount; j++) {
|
1690 |
-
const subPeakHeight = mainPeakHeight * (0.5 + Math.random() * 0.3);
|
1691 |
-
const subPeakWidth = mainPeakWidth * (0.6 + Math.random() * 0.3);
|
1692 |
-
|
1693 |
-
const subPeakGeometry = new THREE.ConeGeometry(subPeakWidth, subPeakHeight, 8);
|
1694 |
-
const subPeak = new THREE.Mesh(subPeakGeometry, mainPeakMaterial);
|
1695 |
-
|
1696 |
-
// λ©μΈ λ΄μ°λ¦¬ μ£Όλ³μ λ°°μΉ
|
1697 |
-
const offsetAngle = (j / subPeakCount) * Math.PI * 2;
|
1698 |
-
const offsetDistance = mainPeakWidth * (0.6 + Math.random() * 0.4);
|
1699 |
-
|
1700 |
-
subPeak.position.set(
|
1701 |
-
Math.cos(offsetAngle) * offsetDistance,
|
1702 |
-
-mainPeakHeight * 0.3,
|
1703 |
-
Math.sin(offsetAngle) * offsetDistance
|
1704 |
-
);
|
1705 |
-
|
1706 |
-
mountainGroup.add(subPeak);
|
1707 |
-
}
|
1708 |
-
|
1709 |
-
// μ° κΈ°λ°λΆ (λ μμ°μ€λ¬μ΄ κ²½μ¬)
|
1710 |
-
const baseGeometry = new THREE.CylinderGeometry(
|
1711 |
-
mainPeakWidth * 1.5,
|
1712 |
-
mainPeakWidth * 2.5,
|
1713 |
-
mainPeakHeight * 0.3,
|
1714 |
-
12
|
1715 |
-
);
|
1716 |
-
const baseMaterial = new THREE.MeshLambertMaterial({
|
1717 |
-
color: new THREE.Color(range.color).multiplyScalar(0.8),
|
1718 |
-
flatShading: true,
|
1719 |
-
fog: true
|
1720 |
-
});
|
1721 |
-
const base = new THREE.Mesh(baseGeometry, baseMaterial);
|
1722 |
-
base.position.y = -mainPeakHeight * 0.35;
|
1723 |
-
mountainGroup.add(base);
|
1724 |
-
|
1725 |
-
// μ° κ·Έλ£Ή μμΉ μ€μ
|
1726 |
-
mountainGroup.position.set(
|
1727 |
-
Math.cos(angle) * distance,
|
1728 |
-
mainPeakHeight * 0.5,
|
1729 |
-
Math.sin(angle) * distance
|
1730 |
-
);
|
1731 |
-
|
1732 |
-
// μ½κ°μ λλ€ νμ
|
1733 |
-
mountainGroup.rotation.y = Math.random() * Math.PI;
|
1734 |
-
|
1735 |
-
// κ±°λ¦¬λ³ μμ: λ©λ¦¬ μλ μ°μΌμλ‘ λ νΈλ₯ΈμμΌλ‘ 보μ΄λλ‘ μ€μ
|
1736 |
-
mountainGroup.layers.set(0);
|
1737 |
-
|
1738 |
-
this.scene.add(mountainGroup);
|
1739 |
-
}
|
1740 |
-
});
|
1741 |
-
|
1742 |
-
// κ°μ₯ λ¨Ό 거리μ κ±°λν μ°λ§₯ μ€λ£¨μ£ μΆκ°
|
1743 |
-
this.createDistantMountainSilhouette();
|
1744 |
-
}
|
1745 |
-
|
1746 |
-
createDistantMountainSilhouette() {
|
1747 |
-
// μ거리 μ°λ§₯ μ€λ£¨μ£ (2D νλ©΄)
|
1748 |
-
const silhouetteDistance = GAME_CONSTANTS.MAP_SIZE * 1.2;
|
1749 |
-
const segments = 50;
|
1750 |
-
|
1751 |
-
for (let side = 0; side < 4; side++) {
|
1752 |
-
const curve = new THREE.CatmullRomCurve3([]);
|
1753 |
-
|
1754 |
-
// μ°λ§₯ μ€λ£¨μ£ 곑μ μμ±
|
1755 |
-
for (let i = 0; i <= segments; i++) {
|
1756 |
-
const t = i / segments;
|
1757 |
-
const x = (t - 0.5) * GAME_CONSTANTS.MAP_SIZE;
|
1758 |
-
const baseHeight = 3000;
|
1759 |
-
const variation = Math.sin(t * Math.PI * 3) * 2000 +
|
1760 |
-
Math.sin(t * Math.PI * 7) * 1000 +
|
1761 |
-
Math.sin(t * Math.PI * 13) * 500;
|
1762 |
-
const y = baseHeight + variation + Math.random() * 500;
|
1763 |
-
|
1764 |
-
curve.points.push(new THREE.Vector3(x, y, 0));
|
1765 |
-
}
|
1766 |
-
|
1767 |
-
// μ€λ£¨μ£ λ©μ μμ±
|
1768 |
-
const points = curve.getPoints(100);
|
1769 |
-
const shape = new THREE.Shape();
|
1770 |
-
|
1771 |
-
shape.moveTo(points[0].x, 0);
|
1772 |
-
points.forEach(p => shape.lineTo(p.x, p.y));
|
1773 |
-
shape.lineTo(points[points.length - 1].x, 0);
|
1774 |
-
shape.lineTo(points[0].x, 0);
|
1775 |
-
|
1776 |
-
const silhouetteGeometry = new THREE.ShapeGeometry(shape);
|
1777 |
-
const silhouetteMaterial = new THREE.MeshBasicMaterial({
|
1778 |
-
color: 0x2a3b4a,
|
1779 |
-
side: THREE.DoubleSide,
|
1780 |
-
fog: true,
|
1781 |
-
transparent: true,
|
1782 |
-
opacity: 0.7
|
1783 |
-
});
|
1784 |
-
|
1785 |
-
const silhouette = new THREE.Mesh(silhouetteGeometry, silhouetteMaterial);
|
1786 |
-
|
1787 |
-
// κ° λ©΄μ λ°°μΉ
|
1788 |
-
const angle = (side / 4) * Math.PI * 2;
|
1789 |
-
silhouette.position.set(
|
1790 |
-
Math.cos(angle) * silhouetteDistance,
|
1791 |
-
0,
|
1792 |
-
Math.sin(angle) * silhouetteDistance
|
1793 |
-
);
|
1794 |
-
silhouette.lookAt(0, 0, 0);
|
1795 |
-
|
1796 |
-
this.scene.add(silhouette);
|
1797 |
-
}
|
1798 |
-
}
|
1799 |
|
1800 |
addClouds() {
|
1801 |
// λ€μΈ΅ κ΅¬λ¦ μμ€ν
|
@@ -2051,11 +1488,11 @@ class Game {
|
|
2051 |
const rollDegrees = this.fighter.rotation.z * (180 / Math.PI);
|
2052 |
hudElement.style.transform = `translate(-50%, -50%) rotate(${-rollDegrees}deg)`;
|
2053 |
|
2054 |
-
// νΌμΉ λλ μ
λ°μ΄νΈ
|
2055 |
const pitchLadder = document.getElementById('pitchLadder');
|
2056 |
if (pitchLadder) {
|
2057 |
const pitchDegrees = this.fighter.rotation.x * (180 / Math.PI);
|
2058 |
-
//
|
2059 |
const pitchOffset = -pitchDegrees * 2;
|
2060 |
pitchLadder.style.transform = `translateY(${pitchOffset}px)`;
|
2061 |
}
|
@@ -2164,7 +1601,6 @@ class Game {
|
|
2164 |
return { x, y };
|
2165 |
}
|
2166 |
|
2167 |
-
// Game ν΄λμ€μ updateWarnings λ©μλ μμ
|
2168 |
updateWarnings() {
|
2169 |
// κΈ°μ‘΄ κ²½κ³ λ©μμ§ μ κ±°
|
2170 |
const existingWarnings = document.querySelectorAll('.warning-message');
|
|
|
461 |
this.stallEscapeProgress = 0; // μ€ν¨ μ§μ
μ μ§νλ μ΄κΈ°ν
|
462 |
}
|
463 |
|
464 |
+
// μ€ν¨ νμΆ μ‘°κ±΄
|
|
|
|
|
|
|
|
|
|
|
|
|
465 |
if (this.stallWarning) {
|
466 |
+
if (this.escapeKeyPressed) {
|
467 |
+
// Fν€λ₯Ό λλ₯΄κ³ μμΌλ©΄ μ§νλ μ¦κ°
|
468 |
+
this.stallEscapeProgress += deltaTime;
|
469 |
+
|
470 |
+
// 2μ΄ μ΄μ λλ₯΄λ©΄ μ€ν¨μμ νμΆ
|
471 |
+
if (this.stallEscapeProgress >= 2.0 && speedKnots >= 350) {
|
472 |
+
this.stallWarning = false;
|
473 |
+
this.stallEscapeProgress = 0;
|
474 |
+
// μ€ν¨μμ λ²μ΄λλ©΄ μμΈλ₯Ό μ½κ° μμ ν
|
475 |
+
this.targetPitch = Math.max(-0.2, Math.min(0.2, this.targetPitch));
|
476 |
+
}
|
477 |
} else {
|
478 |
+
// Fν€λ₯Ό λμΌλ©΄ μ§νλ κ°μ
|
479 |
+
this.stallEscapeProgress = Math.max(0, this.stallEscapeProgress - deltaTime * 2);
|
480 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
481 |
}
|
482 |
|
483 |
// μλ λ³ν μ μ©
|
|
|
1233 |
// κ°μ λ κ΅¬λ¦ μΆκ°
|
1234 |
this.addClouds();
|
1235 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1236 |
|
1237 |
addClouds() {
|
1238 |
// λ€μΈ΅ κ΅¬λ¦ μμ€ν
|
|
|
1488 |
const rollDegrees = this.fighter.rotation.z * (180 / Math.PI);
|
1489 |
hudElement.style.transform = `translate(-50%, -50%) rotate(${-rollDegrees}deg)`;
|
1490 |
|
1491 |
+
// νΌμΉ λλ μ
λ°μ΄νΈ
|
1492 |
const pitchLadder = document.getElementById('pitchLadder');
|
1493 |
if (pitchLadder) {
|
1494 |
const pitchDegrees = this.fighter.rotation.x * (180 / Math.PI);
|
1495 |
+
// μμλ₯Ό κ³±ν΄μ λ°λ λ°©ν₯μΌλ‘, 10λλΉ 20ν½μ
|
1496 |
const pitchOffset = -pitchDegrees * 2;
|
1497 |
pitchLadder.style.transform = `translateY(${pitchOffset}px)`;
|
1498 |
}
|
|
|
1601 |
return { x, y };
|
1602 |
}
|
1603 |
|
|
|
1604 |
updateWarnings() {
|
1605 |
// κΈ°μ‘΄ κ²½κ³ λ©μμ§ μ κ±°
|
1606 |
const existingWarnings = document.querySelectorAll('.warning-message');
|