Update index.html
Browse files- index.html +138 -116
index.html
CHANGED
@@ -1,17 +1,4 @@
|
|
1 |
-
|
2 |
-
const maxAllowedPitch = Math.PI / 4.5; // 40๋
|
3 |
-
const maxAllowedRoll = Math.PI * 0.8; // 144๋
|
4 |
-
|
5 |
-
// ํ์ฌ ํ์ ๊ฐ์ด ํ๊ณ๋ฅผ ์ด๊ณผํ๋ฉด ๊ฐ์ ๋ก ๋๋๋ฆฌ๊ธฐ
|
6 |
-
if (Math.abs(this.rotation.x) > maxAllowedPitch) {
|
7 |
-
this.rotation.x = Math.sign(this.rotation.x) * maxAllowedPitch;
|
8 |
-
this.targetPitch = this.rotation.x;
|
9 |
-
}
|
10 |
-
|
11 |
-
if (Math.abs(this.rotation.z) > maxAllowedRoll) {
|
12 |
-
this.rotation.z = Math.sign(this.rotation.z) * maxAllowedRoll;
|
13 |
-
this.targetRoll = this.rotation.z;
|
14 |
-
}<!DOCTYPE html>
|
15 |
<html>
|
16 |
<head>
|
17 |
<meta charset="utf-8">
|
@@ -100,55 +87,55 @@
|
|
100 |
}
|
101 |
|
102 |
#crosshair {
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
}
|
115 |
-
|
116 |
-
/* ๋ฌผ์ฒด๊ฐ ๊ฐ์ง๋์์ ๋์ ์คํ์ผ */
|
117 |
-
#crosshair.target-detected {
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
}
|
122 |
-
|
123 |
-
#crosshair::before,
|
124 |
-
#crosshair::after {
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
}
|
130 |
-
|
131 |
-
#crosshair::before {
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
}
|
138 |
-
|
139 |
-
#crosshair::after {
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
}
|
146 |
-
|
147 |
-
/* ๋ฌผ์ฒด๊ฐ ๊ฐ์ง๋์์ ๋์ ํฌ๋ก์ค ๋ผ์ธ ์คํ์ผ */
|
148 |
-
#crosshair.target-detected::before,
|
149 |
-
#crosshair.target-detected::after {
|
150 |
-
|
151 |
-
}
|
152 |
|
153 |
#healthBar {
|
154 |
position: absolute;
|
@@ -186,7 +173,7 @@
|
|
186 |
|
187 |
#gameTitle {
|
188 |
position: absolute;
|
189 |
-
top: 10px
|
190 |
left: 50%;
|
191 |
transform: translateX(-50%);
|
192 |
color: #0f0;
|
@@ -564,15 +551,19 @@
|
|
564 |
this.lastShootTime = 0;
|
565 |
|
566 |
// ์นด๋ฉ๋ผ ์ค์
|
567 |
-
this.cameraDistance = 300; //
|
568 |
-
this.cameraHeight =
|
569 |
-
this.cameraLag = 0.
|
570 |
|
571 |
// ๊ฒฝ๊ณ ์์คํ
|
572 |
this.altitudeWarning = false;
|
573 |
this.stallWarning = false;
|
574 |
this.warningBlinkTimer = 0;
|
575 |
this.warningBlinkState = false;
|
|
|
|
|
|
|
|
|
576 |
}
|
577 |
|
578 |
async initialize(scene, loader) {
|
@@ -645,30 +636,45 @@
|
|
645 |
}
|
646 |
|
647 |
updateMouseInput(deltaX, deltaY) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
648 |
// ๋ง์ฐ์ค ๊ฐ๋ ์กฐ์
|
649 |
-
const sensitivity = GAME_CONSTANTS.MOUSE_SENSITIVITY * 1.
|
650 |
|
651 |
-
//
|
652 |
-
|
653 |
-
this.
|
654 |
|
655 |
-
//
|
656 |
-
|
|
|
657 |
|
658 |
-
//
|
659 |
-
|
660 |
-
|
661 |
|
662 |
-
//
|
663 |
this.targetPitch = Math.max(-maxPitchAngle, Math.min(maxPitchAngle, this.targetPitch));
|
664 |
this.targetRoll = Math.max(-maxRollAngle, Math.min(maxRollAngle, this.targetRoll));
|
665 |
|
666 |
-
// ๋กค ๊ฐ๋ ์ ๊ทํ
|
667 |
-
if (this.targetRoll > Math.PI
|
668 |
-
this.targetRoll
|
669 |
-
} else if (this.targetRoll < -Math.PI
|
670 |
-
this.targetRoll
|
671 |
}
|
|
|
|
|
672 |
}
|
673 |
|
674 |
updateControls(keys, deltaTime) {
|
@@ -699,12 +705,23 @@
|
|
699 |
updatePhysics(deltaTime) {
|
700 |
if (!this.mesh) return;
|
701 |
|
|
|
|
|
|
|
|
|
702 |
// ๋ถ๋๋ฌ์ด ํ์ ๋ณด๊ฐ
|
703 |
-
const rotationSpeed = deltaTime *
|
704 |
this.rotation.x = THREE.MathUtils.lerp(this.rotation.x, this.targetPitch, rotationSpeed);
|
705 |
this.rotation.y = THREE.MathUtils.lerp(this.rotation.y, this.targetYaw, rotationSpeed);
|
706 |
this.rotation.z = THREE.MathUtils.lerp(this.rotation.z, this.targetRoll, rotationSpeed);
|
707 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
708 |
// ๊ธฐ๋ณธ ์๋ ๊ณ์ฐ
|
709 |
const minSpeed = 100;
|
710 |
const maxSpeed = 300;
|
@@ -856,50 +873,54 @@
|
|
856 |
return this.health <= 0;
|
857 |
}
|
858 |
|
859 |
-
// ๊ฐ์ ๋ ์นด๋ฉ๋ผ ์์คํ
|
860 |
getCameraPosition() {
|
861 |
-
//
|
862 |
-
const
|
863 |
-
|
864 |
-
|
865 |
-
|
866 |
-
|
|
|
|
|
867 |
const backward = new THREE.Vector3(0, 0, -1);
|
868 |
-
backward.applyEuler(
|
869 |
|
870 |
-
//
|
871 |
-
|
872 |
-
|
873 |
-
.add(new THREE.Vector3(0, height, 0));
|
874 |
|
875 |
-
//
|
876 |
-
const
|
877 |
-
|
878 |
-
|
879 |
-
cameraPos.z -= Math.abs(pitchAdjustment) * 0.3;
|
880 |
|
881 |
-
//
|
882 |
-
const
|
883 |
-
|
884 |
-
|
885 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
886 |
|
887 |
-
// ์ต์ ๊ณ ๋ ๋ณด์ฅ
|
888 |
-
|
889 |
|
890 |
-
return
|
891 |
}
|
892 |
|
893 |
getCameraTarget() {
|
894 |
-
// ์ ํฌ๊ธฐ
|
895 |
const forward = new THREE.Vector3(0, 0, 1);
|
896 |
-
forward.applyEuler(
|
897 |
-
|
898 |
-
// ํ๊ฒ ๋์ด๋ ์์ ํ๊ฒ ์ค์
|
899 |
-
const target = this.position.clone().add(forward.multiplyScalar(100));
|
900 |
-
target.y = Math.max(target.y, 10); // ์ต์ ๋์ด ๋ณด์ฅ
|
901 |
|
902 |
-
return
|
903 |
}
|
904 |
}
|
905 |
|
@@ -1091,7 +1112,7 @@
|
|
1091 |
class Game {
|
1092 |
constructor() {
|
1093 |
this.scene = new THREE.Scene();
|
1094 |
-
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1
|
1095 |
this.renderer = new THREE.WebGLRenderer({ antialias: true });
|
1096 |
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
1097 |
this.renderer.shadowMap.enabled = true;
|
@@ -1612,6 +1633,7 @@
|
|
1612 |
|
1613 |
const gameOverDiv = document.createElement('div');
|
1614 |
gameOverDiv.className = 'start-screen';
|
|
|
1615 |
gameOverDiv.innerHTML = `
|
1616 |
<h1 style="color: ${victory ? '#0f0' : '#f00'}; font-size: 48px;">
|
1617 |
${victory ? 'MISSION ACCOMPLISHED!' : 'SHOT DOWN!'}
|
|
|
1 |
+
<!DOCTYPE html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
<html>
|
3 |
<head>
|
4 |
<meta charset="utf-8">
|
|
|
87 |
}
|
88 |
|
89 |
#crosshair {
|
90 |
+
position: fixed;
|
91 |
+
top: 50%;
|
92 |
+
left: 50%;
|
93 |
+
transform: translate(-50%, -50%);
|
94 |
+
width: 40px;
|
95 |
+
height: 40px;
|
96 |
+
border: 2px solid #00ff00;
|
97 |
+
border-radius: 50%;
|
98 |
+
z-index: 1001;
|
99 |
+
pointer-events: none;
|
100 |
+
transition: all 0.2s ease;
|
101 |
+
}
|
102 |
+
|
103 |
+
/* ๋ฌผ์ฒด๊ฐ ๊ฐ์ง๋์์ ๋์ ์คํ์ผ */
|
104 |
+
#crosshair.target-detected {
|
105 |
+
border-color: #ff0000;
|
106 |
+
border-width: 3px;
|
107 |
+
box-shadow: 0 0 10px rgba(255, 0, 0, 0.5);
|
108 |
+
}
|
109 |
+
|
110 |
+
#crosshair::before,
|
111 |
+
#crosshair::after {
|
112 |
+
content: '';
|
113 |
+
position: absolute;
|
114 |
+
background: #00ff00;
|
115 |
+
transition: all 0.2s ease;
|
116 |
+
}
|
117 |
+
|
118 |
+
#crosshair::before {
|
119 |
+
top: 50%;
|
120 |
+
left: -10px;
|
121 |
+
right: -10px;
|
122 |
+
height: 2px;
|
123 |
+
transform: translateY(-50%);
|
124 |
+
}
|
125 |
+
|
126 |
+
#crosshair::after {
|
127 |
+
left: 50%;
|
128 |
+
top: -10px;
|
129 |
+
bottom: -10px;
|
130 |
+
width: 2px;
|
131 |
+
transform: translateX(-50%);
|
132 |
+
}
|
133 |
+
|
134 |
+
/* ๋ฌผ์ฒด๊ฐ ๊ฐ์ง๋์์ ๋์ ํฌ๋ก์ค ๋ผ์ธ ์คํ์ผ */
|
135 |
+
#crosshair.target-detected::before,
|
136 |
+
#crosshair.target-detected::after {
|
137 |
+
background: #ff0000;
|
138 |
+
}
|
139 |
|
140 |
#healthBar {
|
141 |
position: absolute;
|
|
|
173 |
|
174 |
#gameTitle {
|
175 |
position: absolute;
|
176 |
+
top: 60px; /* 10px์์ 60px๋ก ๋ณ๊ฒฝ - ํ๋ฉด ์๋ฆผ ๋ฌธ์ ํด๊ฒฐ */
|
177 |
left: 50%;
|
178 |
transform: translateX(-50%);
|
179 |
color: #0f0;
|
|
|
551 |
this.lastShootTime = 0;
|
552 |
|
553 |
// ์นด๋ฉ๋ผ ์ค์
|
554 |
+
this.cameraDistance = 300; // ์ ์ ํ ๊ฑฐ๋ฆฌ
|
555 |
+
this.cameraHeight = 80; // ๋์ด ์ฆ๊ฐ
|
556 |
+
this.cameraLag = 0.08; // ๋ถ๋๋ฌ์ด ์ถ์
|
557 |
|
558 |
// ๊ฒฝ๊ณ ์์คํ
|
559 |
this.altitudeWarning = false;
|
560 |
this.stallWarning = false;
|
561 |
this.warningBlinkTimer = 0;
|
562 |
this.warningBlinkState = false;
|
563 |
+
|
564 |
+
// ๋ง์ฐ์ค ์
๋ ฅ ๋์ ๋ฐฉ์ง๋ฅผ ์ํ ๋ณ์ ์ถ๊ฐ
|
565 |
+
this.mouseInputBuffer = { x: 0, y: 0 };
|
566 |
+
this.lastMouseUpdate = 0;
|
567 |
}
|
568 |
|
569 |
async initialize(scene, loader) {
|
|
|
636 |
}
|
637 |
|
638 |
updateMouseInput(deltaX, deltaY) {
|
639 |
+
const currentTime = Date.now();
|
640 |
+
|
641 |
+
// ๋ง์ฐ์ค ์
๋ ฅ ๋์ ๋ฒํผ ์ด๊ธฐํ (๋งค ํ๋ ์๋ง๋ค)
|
642 |
+
if (currentTime - this.lastMouseUpdate > 16) { // ์ฝ 60fps
|
643 |
+
this.mouseInputBuffer.x = 0;
|
644 |
+
this.mouseInputBuffer.y = 0;
|
645 |
+
}
|
646 |
+
|
647 |
+
// ํ์ฌ ํ๋ ์์ ๋ง์ฐ์ค ์
๋ ฅ ๋์
|
648 |
+
this.mouseInputBuffer.x += deltaX;
|
649 |
+
this.mouseInputBuffer.y += deltaY;
|
650 |
+
|
651 |
// ๋ง์ฐ์ค ๊ฐ๋ ์กฐ์
|
652 |
+
const sensitivity = GAME_CONSTANTS.MOUSE_SENSITIVITY * 1.5;
|
653 |
|
654 |
+
// ๋์ ๋ ์
๋ ฅ์ ์ฌ์ฉํ์ฌ ๋ชฉํ ํ์ ๊ฐ ์ค์
|
655 |
+
const rollChange = this.mouseInputBuffer.x * sensitivity;
|
656 |
+
const pitchChange = -this.mouseInputBuffer.y * sensitivity;
|
657 |
|
658 |
+
// ๋์นญ์ ์ธ ๊ฐ๋ ์ ํ
|
659 |
+
const maxPitchAngle = Math.PI / 3; // 60๋๋ก ์ฆ๊ฐ
|
660 |
+
const maxRollAngle = Math.PI; // 180๋ ์์ ํ์ฉ
|
661 |
|
662 |
+
// ๋ชฉํ๊ฐ ์
๋ฐ์ดํธ (์ ๋๊ฐ์ด ์๋ ๋ณํ๋์ผ๋ก)
|
663 |
+
this.targetRoll += rollChange;
|
664 |
+
this.targetPitch += pitchChange;
|
665 |
|
666 |
+
// ์๊ฒฉํ ์ ํ ์ ์ฉ
|
667 |
this.targetPitch = Math.max(-maxPitchAngle, Math.min(maxPitchAngle, this.targetPitch));
|
668 |
this.targetRoll = Math.max(-maxRollAngle, Math.min(maxRollAngle, this.targetRoll));
|
669 |
|
670 |
+
// ๋กค ๊ฐ๋ ์ ๊ทํ
|
671 |
+
if (this.targetRoll > Math.PI) {
|
672 |
+
this.targetRoll -= 2 * Math.PI;
|
673 |
+
} else if (this.targetRoll < -Math.PI) {
|
674 |
+
this.targetRoll += 2 * Math.PI;
|
675 |
}
|
676 |
+
|
677 |
+
this.lastMouseUpdate = currentTime;
|
678 |
}
|
679 |
|
680 |
updateControls(keys, deltaTime) {
|
|
|
705 |
updatePhysics(deltaTime) {
|
706 |
if (!this.mesh) return;
|
707 |
|
708 |
+
// ๋ง์ฐ์ค ์
๋ ฅ ๋ฒํผ ๋ฆฌ์
(๋ฌผ๋ฆฌ ์
๋ฐ์ดํธ ํ)
|
709 |
+
this.mouseInputBuffer.x *= 0.9; // ์์ํ ๊ฐ์
|
710 |
+
this.mouseInputBuffer.y *= 0.9;
|
711 |
+
|
712 |
// ๋ถ๋๋ฌ์ด ํ์ ๋ณด๊ฐ
|
713 |
+
const rotationSpeed = deltaTime * 2.5; // ์ฝ๊ฐ ๋๋ฆฌ๊ฒ
|
714 |
this.rotation.x = THREE.MathUtils.lerp(this.rotation.x, this.targetPitch, rotationSpeed);
|
715 |
this.rotation.y = THREE.MathUtils.lerp(this.rotation.y, this.targetYaw, rotationSpeed);
|
716 |
this.rotation.z = THREE.MathUtils.lerp(this.rotation.z, this.targetRoll, rotationSpeed);
|
717 |
|
718 |
+
// ์ถ๊ฐ ์์ ์ฅ์น: ๋ฌผ๋ฆฌ ์
๋ฐ์ดํธ ์ค์๋ ๊ฐ๋ ์ ํ
|
719 |
+
const maxPitchAngle = Math.PI / 3;
|
720 |
+
const maxRollAngle = Math.PI;
|
721 |
+
|
722 |
+
this.rotation.x = Math.max(-maxPitchAngle, Math.min(maxPitchAngle, this.rotation.x));
|
723 |
+
this.rotation.z = Math.max(-maxRollAngle, Math.min(maxRollAngle, this.rotation.z));
|
724 |
+
|
725 |
// ๊ธฐ๋ณธ ์๋ ๊ณ์ฐ
|
726 |
const minSpeed = 100;
|
727 |
const maxSpeed = 300;
|
|
|
873 |
return this.health <= 0;
|
874 |
}
|
875 |
|
876 |
+
// ๊ฐ์ ๋ ์นด๋ฉ๋ผ ์์คํ
- ์ ํํ ๊ผฌ๋ฆฌ ์ถ์
|
877 |
getCameraPosition() {
|
878 |
+
// ์ ํฌ๊ธฐ์ ํ์ฌ ํ์ ์ ๊ธฐ๋ฐ์ผ๋ก ํ ๋ฐฉํฅ ๋ฒกํฐ
|
879 |
+
const fighterRotation = new THREE.Euler(
|
880 |
+
this.rotation.x,
|
881 |
+
this.rotation.y,
|
882 |
+
this.rotation.z
|
883 |
+
);
|
884 |
+
|
885 |
+
// ๋ค์ชฝ ๋ฐฉํฅ ๋ฒกํฐ (์ ํฌ๊ธฐ ๊ธฐ์ค)
|
886 |
const backward = new THREE.Vector3(0, 0, -1);
|
887 |
+
backward.applyEuler(fighterRotation);
|
888 |
|
889 |
+
// ์์ชฝ ๋ฐฉํฅ ๋ฒกํฐ (์ ํฌ๊ธฐ ๊ธฐ์ค)
|
890 |
+
const up = new THREE.Vector3(0, 1, 0);
|
891 |
+
up.applyEuler(fighterRotation);
|
|
|
892 |
|
893 |
+
// ์นด๋ฉ๋ผ ์์น ๊ณ์ฐ - ์ ํฌ๊ธฐ ๋ค์ชฝ๊ณผ ์์ชฝ
|
894 |
+
const cameraPosition = this.position.clone()
|
895 |
+
.add(backward.multiplyScalar(this.cameraDistance))
|
896 |
+
.add(up.multiplyScalar(this.cameraHeight));
|
|
|
897 |
|
898 |
+
// ๊ทน๋จ์ ์ธ ๊ธฐ๋ ์ ์นด๋ฉ๋ผ ์์ ํ
|
899 |
+
const pitchAngle = Math.abs(this.rotation.x);
|
900 |
+
if (pitchAngle > Math.PI / 3) { // 60๋ ์ด์
|
901 |
+
// ์๋ ์
๋ฒกํฐ์ ๋ธ๋ ๋ฉํ์ฌ ์์ ํ
|
902 |
+
const worldUp = new THREE.Vector3(0, 1, 0);
|
903 |
+
const stabilizeFactor = (pitchAngle - Math.PI / 3) / (Math.PI / 6);
|
904 |
+
const stabilizedUp = up.lerp(worldUp, Math.min(stabilizeFactor, 0.7));
|
905 |
+
|
906 |
+
// ์์ ํ๋ ์์น๋ก ์ฌ๊ณ์ฐ
|
907 |
+
cameraPosition.copy(this.position)
|
908 |
+
.add(backward.multiplyScalar(this.cameraDistance))
|
909 |
+
.add(stabilizedUp.multiplyScalar(this.cameraHeight * 1.2));
|
910 |
+
}
|
911 |
|
912 |
+
// ์ต์ ์นด๋ฉ๋ผ ๊ณ ๋ ๋ณด์ฅ
|
913 |
+
cameraPosition.y = Math.max(cameraPosition.y, this.position.y - 50);
|
914 |
|
915 |
+
return cameraPosition;
|
916 |
}
|
917 |
|
918 |
getCameraTarget() {
|
919 |
+
// ์ ํฌ๊ธฐ ์ค์ฌ์ ์ฝ๊ฐ ์์ชฝ์ผ๋ก ์ด๋ํ ์ง์
|
920 |
const forward = new THREE.Vector3(0, 0, 1);
|
921 |
+
forward.applyEuler(this.rotation);
|
|
|
|
|
|
|
|
|
922 |
|
923 |
+
return this.position.clone().add(forward.multiplyScalar(50));
|
924 |
}
|
925 |
}
|
926 |
|
|
|
1112 |
class Game {
|
1113 |
constructor() {
|
1114 |
this.scene = new THREE.Scene();
|
1115 |
+
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 50000);
|
1116 |
this.renderer = new THREE.WebGLRenderer({ antialias: true });
|
1117 |
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
1118 |
this.renderer.shadowMap.enabled = true;
|
|
|
1633 |
|
1634 |
const gameOverDiv = document.createElement('div');
|
1635 |
gameOverDiv.className = 'start-screen';
|
1636 |
+
gameOverDiv.style.display = 'flex'; // ๊ฒ์ ์ค๋ฒ ํ๋ฉด์ ๋ฐ๋ก ํ์
|
1637 |
gameOverDiv.innerHTML = `
|
1638 |
<h1 style="color: ${victory ? '#0f0' : '#f00'}; font-size: 48px;">
|
1639 |
${victory ? 'MISSION ACCOMPLISHED!' : 'SHOT DOWN!'}
|