Spaces:
Running
Running
<html lang="ko"> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>JET FIGHT SIMULATER - FPS Mode</title> | |
<style> | |
body { | |
margin: 0; | |
overflow: hidden; | |
background: #000; | |
font-family: 'Courier New', monospace; | |
} | |
#loading { | |
position: fixed; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
background: rgba(0,0,0,0.8); | |
padding: 20px; | |
border-radius: 10px; | |
z-index: 2000; | |
text-align: center; | |
} | |
.loading-spinner { | |
width: 50px; | |
height: 50px; | |
border: 5px solid #0f0; | |
border-top: 5px solid transparent; | |
border-radius: 50%; | |
animation: spin 1s linear infinite; | |
margin: 0 auto 20px; | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.loading-text { | |
color: #0f0; | |
font-size: 24px; | |
text-align: center; | |
} | |
#gameContainer { | |
position: relative; | |
width: 100vw; | |
height: 100vh; | |
cursor: none; | |
} | |
/* HUD ์ ์ฒด ์ปจํ ์ด๋ */ | |
#hudContainer { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
pointer-events: none; | |
z-index: 1000; | |
} | |
/* ์ค์ HUD */ | |
#hudCrosshair { | |
position: fixed; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
width: 400px; | |
height: 400px; | |
} | |
/* ์กฐ์ค ์ */ | |
.hud-aiming-circle { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
width: 150px; | |
height: 150px; | |
border: 2px solid rgba(0, 255, 0, 0.5); | |
border-radius: 50%; | |
} | |
/* ์ค์์ */ | |
.hud-center-dot { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
width: 4px; | |
height: 4px; | |
background: #00ff00; | |
border-radius: 50%; | |
} | |
/* ์ํ์ */ | |
.hud-horizon-line { | |
position: absolute; | |
top: 50%; | |
left: 20%; | |
right: 20%; | |
height: 1px; | |
background: rgba(0, 255, 0, 0.4); | |
} | |
/* ํผ์น ๋๋ ์ปจํ ์ด๋ */ | |
.hud-pitch-ladder { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
width: 200px; | |
height: 400px; | |
} | |
.pitch-line { | |
position: absolute; | |
width: 100%; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
} | |
.pitch-line-bar { | |
width: 60px; | |
height: 1px; | |
background: rgba(0, 255, 0, 0.4); | |
} | |
.pitch-line-text { | |
position: absolute; | |
color: rgba(0, 255, 0, 0.6); | |
font-size: 10px; | |
left: -25px; | |
} | |
/* ์ค์ HUD ์ ๋ณด ํ์ - ์๋ก์ด ์คํ์ผ */ | |
.hud-central-info { | |
position: absolute; | |
color: #00ff00; | |
font-size: 11px; | |
font-family: 'Courier New', monospace; | |
text-shadow: 0 0 3px rgba(0, 0, 0, 0.8); | |
background: rgba(0, 0, 0, 0.3); | |
padding: 2px 5px; | |
border-radius: 2px; | |
} | |
/* ์ค์ HUD ๋ด ์๋ ํ์ */ | |
#hudSpeedCentral { | |
left: -120px; | |
top: 50%; | |
transform: translateY(-50%); | |
} | |
/* ์ค์ HUD ๋ด ๊ณ ๋ ํ์ */ | |
#hudAltitudeCentral { | |
right: -120px; | |
top: 50%; | |
transform: translateY(-50%); | |
} | |
/* ์ค์ HUD ๋ด ํค๋ฉ ํ์ */ | |
#hudHeadingCentral { | |
top: -30px; | |
left: 50%; | |
transform: translateX(-50%); | |
} | |
/* ์ค์ HUD ๋ด G-Force ํ์ */ | |
#hudGForceCentral { | |
bottom: -30px; | |
left: 50%; | |
transform: translateX(-50%); | |
} | |
/* ์ค์ HUD ๋ด ์ค๋กํ ํ์ */ | |
#hudThrottleCentral { | |
left: -120px; | |
top: calc(50% + 25px); | |
} | |
/* ํ๊ฒ ๋ง์ปค */ | |
.target-marker { | |
position: absolute; | |
width: 30px; | |
height: 30px; | |
border: 2px solid transparent; | |
transform: translate(-50%, -50%); | |
} | |
.target-marker.in-crosshair { | |
border: 2px solid #ffff00; | |
animation: target-pulse 0.5s infinite; | |
} | |
.target-marker.locked { | |
border: 2px solid #ff0000; | |
box-shadow: 0 0 10px #ff0000; | |
} | |
.target-marker .target-box { | |
position: absolute; | |
top: -5px; | |
left: -5px; | |
right: -5px; | |
bottom: -5px; | |
border: 1px solid currentColor; | |
} | |
@keyframes target-pulse { | |
0%, 100% { opacity: 1; } | |
50% { opacity: 0.5; } | |
} | |
.target-info { | |
position: absolute; | |
top: 100%; | |
left: 50%; | |
transform: translateX(-50%); | |
color: #00ff00; | |
font-size: 10px; | |
white-space: nowrap; | |
margin-top: 5px; | |
} | |
#healthBar { | |
position: absolute; | |
bottom: 20px; | |
left: 20px; | |
width: 200px; | |
height: 20px; | |
background: rgba(0,20,0,0.7); | |
border: 2px solid #0f0; | |
z-index: 1001; | |
border-radius: 10px; | |
overflow: hidden; | |
} | |
#health { | |
width: 100%; | |
height: 100%; | |
background: linear-gradient(90deg, #0f0, #00ff00); | |
transition: width 0.3s; | |
} | |
#gameTitle { | |
position: absolute; | |
top: 60px; | |
left: 50%; | |
transform: translateX(-50%); | |
color: #0f0; | |
background: rgba(0,20,0,0.7); | |
padding: 10px 20px; | |
font-size: 20px; | |
z-index: 1001; | |
border: 1px solid #0f0; | |
border-radius: 5px; | |
text-transform: uppercase; | |
letter-spacing: 2px; | |
} | |
#weaponDisplay { | |
position: absolute; | |
bottom: 20px; | |
right: 20px; | |
background: rgba(0,20,0,0.7); | |
padding: 10px; | |
z-index: 1001; | |
border: 1px solid #0f0; | |
border-radius: 5px; | |
} | |
#ammoDisplay { | |
color: #0f0; | |
font-size: 20px; | |
} | |
#radar { | |
position: absolute; | |
bottom: 60px; | |
left: 20px; | |
width: 200px; | |
height: 200px; | |
background: rgba(30, 30, 30, 0.9); /* ๋ ์ด๋์ด ํ์ ๋ฐฐ๊ฒฝ */ | |
border: 2px solid #0f0; | |
border-radius: 50%; | |
z-index: 1001; | |
overflow: hidden; | |
} | |
/* RWR ๋์คํ๋ ์ด ์คํ์ผ */ | |
.rwr-display { | |
position: relative; | |
width: 100%; | |
height: 100%; | |
} | |
/* RWR ์ค์ ํญ๊ณต๊ธฐ ์ฌ๋ณผ */ | |
.rwr-center { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
width: 15px; /* ๋ ์๊ฒ */ | |
height: 15px; /* ๋ ์๊ฒ */ | |
z-index: 10; | |
} | |
.rwr-aircraft-symbol { | |
width: 100%; | |
height: 100%; | |
position: relative; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
} | |
.rwr-aircraft-symbol img { | |
width: 100%; | |
height: 100%; | |
object-fit: contain; | |
filter: drop-shadow(0 0 2px #00ff00); /* ์ด๋ก์ ๋น๋๋ ํจ๊ณผ */ | |
} | |
/* RWR ๊ฑฐ๋ฆฌ ๋ง */ | |
.rwr-range-ring { | |
position: absolute; | |
border: 1px solid rgba(0, 255, 0, 0.4); | |
border-radius: 50%; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
} | |
.rwr-ring-inner { | |
width: 60px; | |
height: 60px; | |
} | |
.rwr-ring-middle { | |
width: 120px; | |
height: 120px; | |
} | |
.rwr-ring-outer { | |
width: 180px; | |
height: 180px; | |
} | |
/* RWR ์ค์์ ์์ ์ ์ถ๊ฐ */ | |
.rwr-center-dot { | |
position: absolute; | |
top: calc(50% + 4px); /* 4px ์๋๋ก ์ด๋ */ | |
left: 50%; | |
transform: translate(-50%, -50%); | |
width: 4px; | |
height: 4px; | |
background: #00ff00; | |
border-radius: 50%; | |
z-index: 11; | |
} | |
/* RWR ์ํ ํ์ */ | |
.rwr-threat { | |
position: absolute; | |
width: 15px; | |
height: 15px; | |
transform: translate(-50%, -50%); | |
text-align: center; | |
font-size: 10px; | |
font-weight: bold; | |
z-index: 5; | |
} | |
/* RWR ๋ฝ์จ ๋ฐฉํฅ์ */ | |
.rwr-lock-line { | |
position: absolute; | |
background: #ff0000; | |
height: 2px; | |
transform-origin: left center; | |
z-index: 4; | |
animation: lock-pulse 0.5s infinite; | |
} | |
@keyframes lock-pulse { | |
0%, 100% { opacity: 1; } | |
50% { opacity: 0.3; } | |
} | |
/* ๋ฏธ์ฌ์ผ ๊ฒฝ๊ณ ํจ๋ */ | |
.missile-warning-panel { | |
position: fixed; | |
top: 50%; | |
right: 20px; | |
transform: translateY(-50%); | |
background: rgba(255, 0, 0, 0.2); | |
border: 2px solid #ff0000; | |
border-radius: 5px; | |
padding: 10px; | |
z-index: 1500; | |
min-width: 200px; | |
} | |
.missile-warning-item { | |
color: #ff0000; | |
font-size: 16px; | |
font-weight: bold; | |
margin: 5px 0; | |
text-shadow: 0 0 5px #ff0000; | |
animation: warning-blink 0.3s infinite; | |
} | |
@keyframes warning-blink { | |
0%, 50% { opacity: 1; } | |
51%, 100% { opacity: 0.7; } | |
} | |
/* ๋ฝ์จ ๊ฒฝ๊ณ ํ์ดํ */ | |
.lock-warning-arrow { | |
position: fixed; | |
width: 40px; | |
height: 40px; | |
z-index: 1400; | |
pointer-events: none; | |
} | |
.lock-warning-arrow::before { | |
content: 'โฒ'; | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
font-size: 30px; | |
color: #ffff00; | |
text-shadow: 0 0 10px #ffff00, 0 0 20px #ffff00; | |
animation: arrow-blink 0.5s infinite; | |
} | |
@keyframes arrow-blink { | |
0%, 50% { opacity: 1; } | |
51%, 100% { opacity: 0.3; } | |
} | |
.rwr-direction-text { | |
position: absolute; | |
color: #0f0; | |
font-size: 12px; | |
font-weight: bold; | |
transform: translate(-50%, -50%); | |
text-shadow: 0 0 2px #0f0; | |
} | |
.rwr-north { | |
top: 15px; | |
left: 50%; | |
} | |
.rwr-east { | |
top: 50%; | |
right: 15px; | |
left: auto; | |
transform: translateY(-50%); | |
} | |
.rwr-south { | |
bottom: 15px; | |
left: 50%; | |
top: auto; | |
transform: translateX(-50%); | |
} | |
.rwr-west { | |
top: 50%; | |
left: 15px; | |
} | |
/* RWR ์ํ ํ์ */ | |
.rwr-threat { | |
position: absolute; | |
width: 15px; | |
height: 15px; | |
transform: translate(-50%, -50%); | |
text-align: center; | |
font-size: 10px; | |
font-weight: bold; | |
z-index: 5; | |
} | |
/* ์ํ ๋ ๋ฒจ๋ณ ์์ */ | |
.rwr-threat.level-low { | |
color: #ffff00; /* ๋ ธ๋์ */ | |
text-shadow: 0 0 3px #ffff00; | |
} | |
.rwr-threat.level-medium { | |
color: #ff8800; /* ์ฃผํฉ์ */ | |
text-shadow: 0 0 3px #ff8800; | |
} | |
.rwr-threat.level-high { | |
color: #ff0000; /* ๋นจ๊ฐ์ */ | |
text-shadow: 0 0 3px #ff0000; | |
} | |
.rwr-threat.missile-lock { | |
animation: rwr-flash 0.3s infinite; | |
} | |
@keyframes rwr-flash { | |
0%, 50% { opacity: 1; } | |
51%, 100% { opacity: 0.3; } | |
} | |
#mission { | |
position: absolute; | |
top: 10px; | |
left: 10px; | |
color: #0f0; | |
background: rgba(0,20,0,0.7); | |
padding: 10px; | |
font-size: 16px; | |
z-index: 1001; | |
border: 1px solid #0f0; | |
border-radius: 5px; | |
} | |
#radarLine { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
width: 50%; | |
height: 2px; | |
background: #0f0; | |
transform-origin: left center; | |
animation: radar-sweep 4s infinite linear; | |
} | |
.enemy-dot { | |
position: absolute; | |
width: 6px; | |
height: 6px; | |
background: #ff0000; | |
border-radius: 50%; | |
transform: translate(-50%, -50%); | |
} | |
@keyframes radar-sweep { | |
from { | |
transform: rotate(0deg); | |
} | |
to { | |
transform: rotate(360deg); | |
} | |
} | |
#gameStats { | |
position: absolute; | |
top: 10px; | |
right: 20px; | |
color: #0f0; | |
background: rgba(0,20,0,0.7); | |
padding: 10px; | |
font-size: 16px; | |
z-index: 1001; | |
border: 1px solid #0f0; | |
border-radius: 5px; | |
text-align: right; | |
} | |
.start-screen { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background: rgba(0,0,0,0.8); | |
display: none; | |
justify-content: center; | |
align-items: center; | |
flex-direction: column; | |
z-index: 2000; | |
} | |
.start-button { | |
padding: 15px 30px; | |
font-size: 24px; | |
background: #0f0; | |
color: #000; | |
border: none; | |
border-radius: 5px; | |
cursor: pointer; | |
margin-top: 20px; | |
transition: transform 0.2s; | |
} | |
.start-button:hover { | |
transform: scale(1.1); | |
} | |
/* ๊ธฐ์กด ๊ฐ๋ณ HUD ์ ๋ณด๋ค์ ์จ๊น */ | |
#hudSpeed, #hudAltitude, #hudHeading, #hudPitch, #hudRoll, #hudTurnRate { | |
display: none; | |
} | |
</style> | |
</head> | |
<body> | |
<!-- ๋ก๋ฉ ํ๋ฉด --> | |
<div id="loading"> | |
<div class="loading-spinner"></div> | |
<div class="loading-text">Loading Fighter Resources...</div> | |
<div style="color: #0f0; font-size: 16px; margin-top: 10px;"> | |
<p>Loading Aircraft Models...</p> | |
<p>Loading Audio Assets...</p> | |
<p>Preparing Game Environment...</p> | |
</div> | |
</div> | |
<!-- ๊ฒ์ ์์ ํ๋ฉด --> | |
<div class="start-screen" id="startScreen"> | |
<h1 style="color: #0f0; font-size: 48px; margin-bottom: 20px;">JET FIGHT SIMULATER</h1> | |
<button class="start-button" onclick="startGame()">Start Game</button> | |
<div style="color: #0f0; margin-top: 20px; text-align: center;"> | |
<p>Controls:</p> | |
<p>W/S - Throttle Control</p> | |
<p>A/D - Rudder Control</p> | |
<p>Mouse - Aircraft Control</p> | |
<p>Left Click - Fire</p> | |
<p>F - Deploy Flares</p> | |
<p>G - Escape Stall</p> | |
</div> | |
</div> | |
<!-- ๊ฒ์ ํ๋ฉด --> | |
<div id="gameContainer"> | |
<!-- ๊ฒ์ ํ์ดํ (๊ฒ์ ์์ ์ ์จ๊ฒจ์ง) --> | |
<div id="gameTitle" style="display: none;">JET FIGHT SIMULATER</div> | |
<div id="mission">MISSION: DESTROY ENEMY JET</div> | |
<div id="gameStats"> | |
<div id="score">Score: 0</div> | |
<div id="time">Time: 180s</div> | |
</div> | |
<!-- HUD ์ปจํ ์ด๋ --> | |
<div id="hudContainer"> | |
<!-- ์ค์ HUD --> | |
<div id="hudCrosshair"> | |
<div class="hud-aiming-circle"></div> | |
<div class="hud-center-dot"></div> | |
<div class="hud-horizon-line"></div> | |
<!-- ์ค์ HUD ์ ๋ณด ํ์ --> | |
<div class="hud-central-info" id="hudSpeedCentral">SPD: 0 KT</div> | |
<div class="hud-central-info" id="hudAltitudeCentral">ALT: 0 M</div> | |
<div class="hud-central-info" id="hudHeadingCentral">HDG: 000ยฐ</div> | |
<div class="hud-central-info" id="hudGForceCentral">G: 1.0</div> | |
<div class="hud-central-info" id="hudThrottleCentral">THR: 60%</div> | |
<!-- ํผ์น ๋๋ --> | |
<div class="hud-pitch-ladder" id="pitchLadder"> | |
<!-- 10๋๋น 20ํฝ์ ๊ฐ๊ฒฉ --> | |
<div class="pitch-line" style="top: calc(50% - 80px);"> | |
<div class="pitch-line-bar"></div> | |
<span class="pitch-line-text">40</span> | |
</div> | |
<div class="pitch-line" style="top: calc(50% - 60px);"> | |
<div class="pitch-line-bar"></div> | |
<span class="pitch-line-text">30</span> | |
</div> | |
<div class="pitch-line" style="top: calc(50% - 40px);"> | |
<div class="pitch-line-bar"></div> | |
<span class="pitch-line-text">20</span> | |
</div> | |
<div class="pitch-line" style="top: calc(50% - 20px);"> | |
<div class="pitch-line-bar"></div> | |
<span class="pitch-line-text">10</span> | |
</div> | |
<!-- 0๋ ๋ผ์ธ - ์ ํํ ์ค์ --> | |
<div class="pitch-line" style="top: 50%;"> | |
<div class="pitch-line-bar" style="width: 100px; height: 2px; background: rgba(0, 255, 0, 0.8);"></div> | |
<span class="pitch-line-text" style="color: rgba(0, 255, 0, 0.8); font-weight: bold;">0</span> | |
</div> | |
<div class="pitch-line" style="top: calc(50% + 20px);"> | |
<div class="pitch-line-bar"></div> | |
<span class="pitch-line-text">-10</span> | |
</div> | |
<div class="pitch-line" style="top: calc(50% + 40px);"> | |
<div class="pitch-line-bar"></div> | |
<span class="pitch-line-text">-20</span> | |
</div> | |
<div class="pitch-line" style="top: calc(50% + 60px);"> | |
<div class="pitch-line-bar"></div> | |
<span class="pitch-line-text">-30</span> | |
</div> | |
<div class="pitch-line" style="top: calc(50% + 80px);"> | |
<div class="pitch-line-bar"></div> | |
<span class="pitch-line-text">-40</span> | |
</div> | |
</div> | |
<!-- ๋กค ์ธ๋์ผ์ดํฐ (์ข/์ฐ ๊ธฐ์ธ๊ธฐ) - ํฌ๋ก์คํค์ด ๋ฐ๋ก ์ --> | |
<div class="hud-roll-indicator" id="rollIndicator" style=" | |
position: absolute; | |
top: -90px; | |
left: 50%; | |
transform: translateX(-50%); | |
width: 300px; | |
height: 35px; | |
"> | |
<!-- ๋กค ๊ฐ๋ ํ์ --> | |
<div style="position: relative; width: 100%; height: 100%;"> | |
<!-- ์ค์ ๋ง์ปค --> | |
<div style=" | |
position: absolute; | |
top: 0; | |
left: 50%; | |
transform: translateX(-50%); | |
width: 0; | |
height: 0; | |
border-left: 12px solid transparent; | |
border-right: 12px solid transparent; | |
border-top: 18px solid #00ff00; | |
z-index: 2; | |
"></div> | |
<!-- ๋กค ์ค์ผ์ผ ๋ฐฐ๊ฒฝ --> | |
<div style=" | |
position: absolute; | |
bottom: 5px; | |
left: 0; | |
right: 0; | |
height: 15px; | |
background: rgba(0, 0, 0, 0.3); | |
border: 1px solid rgba(0, 255, 0, 0.3); | |
border-radius: 3px; | |
"></div> | |
<!-- ์ํ ๊ตฌ์ญ ํ์ (ยฑ30๋ ์ด์) --> | |
<div style=" | |
position: absolute; | |
bottom: 5px; | |
left: 0; | |
width: 25%; | |
height: 15px; | |
background: rgba(255, 0, 0, 0.2); | |
border-radius: 3px 0 0 3px; | |
"></div> | |
<div style=" | |
position: absolute; | |
bottom: 5px; | |
right: 0; | |
width: 25%; | |
height: 15px; | |
background: rgba(255, 0, 0, 0.2); | |
border-radius: 0 3px 3px 0; | |
"></div> | |
<!-- ๋กค ์ค์ผ์ผ --> | |
<div style=" | |
position: absolute; | |
bottom: 22px; | |
width: 100%; | |
display: flex; | |
justify-content: space-between; | |
align-items: flex-end; | |
padding: 0 10px; | |
box-sizing: border-box; | |
"> | |
<div style="color: rgba(255, 0, 0, 0.7); font-size: 10px; font-weight: bold;">-60</div> | |
<div style="position: absolute; left: 25%; transform: translateX(-50%); width: 1px; height: 10px; background: rgba(255, 255, 0, 0.6);"></div> | |
<div style="position: absolute; left: 25%; transform: translateX(-50%); color: rgba(255, 255, 0, 0.7); font-size: 10px; top: -15px;">-30</div> | |
<div style="position: absolute; left: 50%; transform: translateX(-50%); width: 2px; height: 12px; background: rgba(0, 255, 0, 0.8);"></div> | |
<div style="position: absolute; left: 50%; transform: translateX(-50%); color: rgba(0, 255, 0, 1); font-size: 11px; font-weight: bold; top: -15px;">0</div> | |
<div style="position: absolute; left: 75%; transform: translateX(-50%); width: 1px; height: 10px; background: rgba(255, 255, 0, 0.6);"></div> | |
<div style="position: absolute; left: 75%; transform: translateX(-50%); color: rgba(255, 255, 0, 0.7); font-size: 10px; top: -15px;">30</div> | |
<div style="color: rgba(255, 0, 0, 0.7); font-size: 10px; font-weight: bold;">60</div> | |
</div> | |
<!-- ํ์ฌ ๋กค ํ์ --> | |
<div id="currentRollMarker" style=" | |
position: absolute; | |
bottom: 5px; | |
left: 50%; | |
transform: translateX(-50%); | |
width: 3px; | |
height: 15px; | |
background: #00ff00; | |
transition: left 0.1s; | |
box-shadow: 0 0 5px #00ff00; | |
"></div> | |
</div> | |
</div> | |
</div> | |
<!-- ๊ธฐ์กด ๋นํ ์ ๋ณด (์จ๊น) --> | |
<div class="hud-info" id="hudSpeed">SPD: 0 KT</div> | |
<div class="hud-info" id="hudAltitude">ALT: 0 M</div> | |
<div class="hud-info" id="hudHeading">HDG: 000ยฐ</div> | |
<div class="hud-info" id="hudPitch">PITCH: 0ยฐ</div> | |
<div class="hud-info" id="hudRoll">ROLL: 0ยฐ</div> | |
<div class="hud-info" id="hudTurnRate">TURN: 0ยฐ/s</div> | |
<!-- ํ๊ฒ ๋ง์ปค ๋ ์ด์ด --> | |
<div id="targetMarkers"></div> | |
</div> | |
<!-- ์ฒด๋ ฅ๋ฐ --> | |
<div id="healthBar"> | |
<div id="health"></div> | |
</div> | |
<!-- ๋ฌด์ฅ ํ์ --> | |
<div id="weaponDisplay"> | |
<div style="color: #0f0; font-size: 14px; margin-bottom: 5px;">R: WEAPON CHANGE</div> | |
<div id="ammoDisplay">20MM MG: 940</div> | |
</div> | |
<!-- ๋ ์ด๋ (RWR) --> | |
<div id="radar"> | |
<div class="rwr-display"> | |
<!-- RWR ๊ฑฐ๋ฆฌ ๋ง --> | |
<div class="rwr-range-ring rwr-ring-inner"></div> | |
<div class="rwr-range-ring rwr-ring-middle"></div> | |
<div class="rwr-range-ring rwr-ring-outer"></div> | |
<!-- ๋ฐฉํฅ ํ์ --> | |
<div class="rwr-direction-marks"> | |
<div class="rwr-direction-text rwr-north">N</div> | |
<div class="rwr-direction-text rwr-east">E</div> | |
<div class="rwr-direction-text rwr-south">S</div> | |
<div class="rwr-direction-text rwr-west">W</div> | |
</div> | |
<!-- ์ค์ ํญ๊ณต๊ธฐ ์ฌ๋ณผ --> | |
<div class="rwr-center"> | |
<div class="rwr-aircraft-symbol" id="rwrSymbol"> | |
<!-- ์ด๋ฏธ์ง๋ JavaScript๋ก ๋ก๋ --> | |
</div> | |
</div> | |
<!-- ์ค์ ์ --> | |
<div class="rwr-center-dot"></div> | |
<!-- ์ํ ํ์ ์์ญ --> | |
<div id="rwrThreats"></div> | |
</div> | |
<!-- ๊ธฐ์กด ๋ ์ด๋ ์์ (์จ๊น) --> | |
<div id="radarLine" style="display: none;"></div> | |
</div> | |
</div> | |
<script type="importmap"> | |
{ | |
"imports": { | |
"three": "https://unpkg.com/three@0.157.0/build/three.module.js", | |
"three/addons/": "https://unpkg.com/three@0.157.0/examples/jsm/" | |
} | |
} | |
</script> | |
<!-- ํผ์น ๋๋ ๋ฐ HUD ์ ๋ฐ์ดํธ ์คํฌ๋ฆฝํธ --> | |
<script> | |
// ์ ์ญ ๋ณ์ | |
let gameStarted = false; | |
// ๊ฒ์ ์ค๋น ์๋ฃ ์ HUD ์ ๋ฐ์ดํธ ์์ | |
window.addEventListener('gameReady', function() { | |
console.log('Game ready - starting HUD updates'); | |
// RWR ์ฌ๋ณผ ์ด๋ฏธ์ง ๋ก๋ | |
const rwrSymbol = document.getElementById('rwrSymbol'); | |
if (rwrSymbol) { | |
// ๋ฐฉ๋ฒ 1: img ํ๊ทธ๋ก ์๋ | |
const img = new Image(); | |
img.src = 'effects/symbol.png'; | |
img.style.width = '100%'; | |
img.style.height = '100%'; | |
img.style.objectFit = 'contain'; | |
img.style.filter = 'drop-shadow(0 0 2px #00ff00)'; // ์ด๋ก์์ผ๋ก ๋ณ๊ฒฝ | |
img.onload = function() { | |
console.log('RWR symbol image loaded successfully'); | |
rwrSymbol.innerHTML = ''; | |
rwrSymbol.appendChild(img); | |
}; | |
img.onerror = function() { | |
console.log('RWR symbol image failed to load, using fallback'); | |
// ์ด๋ฏธ์ง ๋ก๋ ์คํจ์ ์์ ์ด๋ก์ ์ ์ผ๋ก ๋์ฒด | |
rwrSymbol.innerHTML = ` | |
<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;"> | |
<div style="width: 8px; height: 8px; background: #00ff00; border-radius: 50%; box-shadow: 0 0 4px #00ff00;"></div> | |
</div> | |
`; | |
}; | |
} | |
// ํผ์น ๋๋ ์ด๊ธฐ ์์น ๊ฐ์ ์ค์ | |
setTimeout(function() { | |
const pitchLadder = document.getElementById('pitchLadder'); | |
if (pitchLadder) { | |
// CSS๋ก ์ง์ ์์น ์กฐ์ | |
pitchLadder.style.position = 'absolute'; | |
pitchLadder.style.top = 'calc(50% - 200px)'; // 200px ์๋ก ์ฌ๋ฆผ | |
pitchLadder.style.left = '50%'; | |
pitchLadder.style.transform = 'translate(-50%, 0)'; | |
console.log('Pitch ladder initial position set'); | |
} | |
}, 100); | |
// ์ค์ HUD ์ ๋ณด ์ ๋ฐ์ดํธ๋ฅผ ์ํ ์ธํฐ๋ฒ | |
setInterval(function() { | |
if (window.gameInstance && window.gameInstance.fighter && window.gameInstance.fighter.isLoaded) { | |
const fighter = window.gameInstance.fighter; | |
// ๋นํ ์ ๋ณด ๊ณ์ฐ | |
const speedKnots = Math.round(fighter.speed * 1.94384); | |
const altitudeMeters = Math.round(fighter.altitude); | |
const throttlePercent = Math.round(fighter.throttle * 100); | |
const gForce = fighter.gForce.toFixed(1); | |
const headingDegrees = Math.round(fighter.getHeadingDegrees()); | |
// ์ค์ HUD ์ ๋ณด ์ ๋ฐ์ดํธ | |
const hudSpeedCentral = document.getElementById('hudSpeedCentral'); | |
const hudAltitudeCentral = document.getElementById('hudAltitudeCentral'); | |
const hudHeadingCentral = document.getElementById('hudHeadingCentral'); | |
const hudGForceCentral = document.getElementById('hudGForceCentral'); | |
const hudThrottleCentral = document.getElementById('hudThrottleCentral'); | |
if (hudSpeedCentral) hudSpeedCentral.textContent = `SPD: ${speedKnots} KT`; | |
if (hudAltitudeCentral) hudAltitudeCentral.textContent = `ALT: ${altitudeMeters} M`; | |
if (hudHeadingCentral) hudHeadingCentral.textContent = `HDG: ${String(headingDegrees).padStart(3, '0')}ยฐ`; | |
if (hudGForceCentral) { | |
hudGForceCentral.textContent = `G: ${gForce}`; | |
// G-Force์ ๋ฐ๋ฅธ ์์ ๋ณ๊ฒฝ | |
if (fighter.overG) { | |
hudGForceCentral.style.color = '#ff0000'; | |
} else if (parseFloat(gForce) > 7) { | |
hudGForceCentral.style.color = '#ffff00'; | |
} else { | |
hudGForceCentral.style.color = '#00ff00'; | |
} | |
} | |
if (hudThrottleCentral) hudThrottleCentral.textContent = `THR: ${throttlePercent}%`; | |
// ํผ์น ๋๋ ๋์ ์ ๋ฐ์ดํธ - ๊ธฐ๋ณธ ์์น์์ ํผ์น ๊ฐ๋๋งํผ ์ด๋ | |
const pitchLadder = document.getElementById('pitchLadder'); | |
if (pitchLadder) { | |
const pitchDegrees = fighter.rotation.x * (180 / Math.PI); | |
// 10๋๋น 20ํฝ์ , ์์๋ก ๋ฐ๋ ๋ฐฉํฅ | |
const pitchOffset = -200 + (-pitchDegrees * 2); | |
pitchLadder.style.top = `calc(50% + ${pitchOffset}px)`; | |
} | |
// HUD ํฌ๋ก์คํค์ด ํ์ (๋กค ๊ฐ๋์ ๋ฐ๋ผ) | |
const hudCrosshair = document.getElementById('hudCrosshair'); | |
if (hudCrosshair && fighter.rotation) { | |
const rollDegrees = fighter.rotation.z * (180 / Math.PI); | |
hudCrosshair.style.transform = `translate(-50%, -50%) rotate(${-rollDegrees}deg)`; | |
} | |
// ๋กค ์ธ๋์ผ์ดํฐ ์ ๋ฐ์ดํธ | |
const currentRollMarker = document.getElementById('currentRollMarker'); | |
const rollIndicator = document.getElementById('rollIndicator'); | |
if (currentRollMarker && fighter.rotation) { | |
const rollDegrees = fighter.rotation.z * (180 / Math.PI); | |
// -60๋์์ +60๋ ๋ฒ์๋ฅผ ์ ์ฒด ๋๋น์ ๋ฐฑ๋ถ์จ๋ก ๋งคํ | |
// 300px ๋๋น์์ ์์ชฝ 10px ํจ๋ฉ์ ๋บ 280px ์ฌ์ฉ | |
const normalizedRoll = (rollDegrees + 60) / 120; // 0~1 ๋ฒ์๋ก ์ ๊ทํ | |
const markerPosition = 10 + (normalizedRoll * 280); // 10px ~ 290px | |
currentRollMarker.style.left = `${markerPosition}px`; | |
currentRollMarker.style.transform = 'translateX(-50%)'; | |
// ๋กค์ด ยฑ30๋๋ฅผ ์ด๊ณผํ๋ฉด ์์ ๋ณ๊ฒฝ | |
if (Math.abs(rollDegrees) > 30) { | |
currentRollMarker.style.background = '#ff0000'; | |
currentRollMarker.style.boxShadow = '0 0 8px #ff0000'; | |
currentRollMarker.style.width = '4px'; | |
} else { | |
currentRollMarker.style.background = '#00ff00'; | |
currentRollMarker.style.boxShadow = '0 0 5px #00ff00'; | |
currentRollMarker.style.width = '3px'; | |
} | |
} | |
// RWR ์ ๋ฐ์ดํธ | |
updateRWR(fighter); | |
} | |
}, 16); // ์ฝ 60fps | |
}); | |
// RWR ์ ๋ฐ์ดํธ ํจ์ | |
function updateRWR(fighter) { | |
const rwrThreats = document.getElementById('rwrThreats'); | |
if (!rwrThreats || !window.gameInstance) return; | |
// ๊ธฐ์กด ์ํ ํ์ ์ ๊ฑฐ | |
rwrThreats.innerHTML = ''; | |
// ์ ํญ๊ณต๊ธฐ ํ์ | |
if (window.gameInstance.enemies) { | |
window.gameInstance.enemies.forEach((enemy, index) => { | |
if (!enemy.mesh || !enemy.isLoaded) return; | |
const distance = fighter.position.distanceTo(enemy.position); | |
// 10km ์ด๋ด์ ์ ๋ง RWR์ ํ์ | |
if (distance <= 10000) { | |
// ์๋ ์์น ๊ณ์ฐ | |
const relativePos = enemy.position.clone().sub(fighter.position); | |
// ์ ํฌ๊ธฐ์ heading์ ๊ณ ๋ คํ ๊ฐ๋ ๊ณ์ฐ | |
const angle = Math.atan2(relativePos.x, relativePos.z) - fighter.rotation.y; | |
// RWR ์์ ์์น ๊ณ์ฐ (์ต๋ ๋ฐ๊ฒฝ 90px) | |
const maxRadius = 90; | |
const relativeDistance = Math.min(distance / 10000, 1) * maxRadius; | |
const x = Math.sin(angle) * relativeDistance + 100; // ์ค์์ด 100px | |
const y = -Math.cos(angle) * relativeDistance + 100; | |
// ์ํ ์ฌ๋ณผ ์์ฑ | |
const threat = document.createElement('div'); | |
threat.className = 'rwr-threat'; | |
// ๊ฑฐ๋ฆฌ์ ๋ฐ๋ฅธ ์ํ ๋ ๋ฒจ (์ ํญ๊ณต๊ธฐ๋ง ํ์) | |
if (distance < 2000) { | |
threat.classList.add('level-high'); | |
threat.classList.add('missile-lock'); | |
threat.textContent = 'โ'; | |
} else if (distance < 5000) { | |
threat.classList.add('level-medium'); | |
threat.textContent = 'โ'; | |
} else { | |
threat.classList.add('level-low'); | |
threat.textContent = 'โ'; | |
} | |
threat.style.left = `${x}px`; | |
threat.style.top = `${y}px`; | |
rwrThreats.appendChild(threat); | |
// ๋ฝ์จ ์ค์ธ ์ ์ด๋ฉด RWR์ ๋ฐฉํฅ์ ํ์ | |
if (enemy.isLocking && enemy.lockTarget === fighter) { | |
const lockLine = document.createElement('div'); | |
lockLine.className = 'rwr-lock-line'; | |
// ์ค์์์ ์ ๊น์ง์ ์ | |
const lineLength = Math.sqrt(Math.pow(x - 100, 2) + Math.pow(y - 100, 2)); | |
const lineAngle = Math.atan2(y - 100, x - 100); | |
lockLine.style.width = `${lineLength}px`; | |
lockLine.style.left = '100px'; | |
lockLine.style.top = '100px'; | |
lockLine.style.transform = `rotate(${lineAngle}rad)`; | |
rwrThreats.appendChild(lockLine); | |
} | |
} | |
}); | |
} | |
// ๋ฝ์จ ๊ฒฝ๊ณ ํ์ดํ ์ ๋ฐ์ดํธ | |
updateLockWarningArrows(fighter); | |
// ๋ฏธ์ฌ์ผ ๊ฑฐ๋ฆฌ ๊ฒฝ๊ณ ์ ๋ฐ์ดํธ | |
updateMissileWarnings(fighter); | |
} | |
// ๋ฝ์จ ๊ฒฝ๊ณ ํ์ดํ ํ์ | |
function updateLockWarningArrows(fighter) { | |
// ๊ธฐ์กด ํ์ดํ ์ ๊ฑฐ | |
const existingArrows = document.querySelectorAll('.lock-warning-arrow'); | |
existingArrows.forEach(arrow => arrow.remove()); | |
if (!fighter.beingLockedBy || fighter.beingLockedBy.length === 0) return; | |
fighter.beingLockedBy.forEach(enemy => { | |
if (!enemy.position) return; | |
// ํ๋ฉด ๋ฐ์ ์๋ ์ ๋ง ํ์ดํ ํ์ | |
const screenPos = getScreenPosition(enemy.position); | |
if (screenPos) return; // ํ๋ฉด ์์ ์์ผ๋ฉด ํ์ดํ ๋ถํ์ | |
// ์๋ ์์น ๊ณ์ฐ | |
const relativePos = enemy.position.clone().sub(fighter.position); | |
const angle = Math.atan2(relativePos.x, relativePos.z) - fighter.rotation.y; | |
const arrow = document.createElement('div'); | |
arrow.className = 'lock-warning-arrow'; | |
// ํ๋ฉด ๊ฐ์ฅ์๋ฆฌ์ ํ์ดํ ๋ฐฐ์น | |
const screenCenterX = window.innerWidth / 2; | |
const screenCenterY = window.innerHeight / 2; | |
const edgeDistance = Math.min(screenCenterX - 100, screenCenterY - 100); | |
const arrowX = screenCenterX + Math.sin(angle) * edgeDistance; | |
const arrowY = screenCenterY - Math.cos(angle) * edgeDistance; | |
arrow.style.left = `${arrowX}px`; | |
arrow.style.top = `${arrowY}px`; | |
// ํ์ดํ ํ์ | |
const rotationAngle = angle * (180 / Math.PI); | |
arrow.style.transform = `translate(-50%, -50%) rotate(${rotationAngle}deg)`; | |
document.body.appendChild(arrow); | |
}); | |
} | |
// ๋ฏธ์ฌ์ผ ๊ฑฐ๋ฆฌ ๊ฒฝ๊ณ ํ์ | |
function updateMissileWarnings(fighter) { | |
// ๊ธฐ์กด ๊ฒฝ๊ณ ํจ๋ ์ ๊ฑฐ | |
const existingPanel = document.querySelector('.missile-warning-panel'); | |
if (existingPanel) existingPanel.remove(); | |
if (!fighter.incomingMissiles || fighter.incomingMissiles.length === 0) return; | |
const panel = document.createElement('div'); | |
panel.className = 'missile-warning-panel'; | |
fighter.incomingMissiles.forEach((missile, index) => { | |
if (!missile.position) return; | |
const distance = Math.round(fighter.position.distanceTo(missile.position)); | |
const warningItem = document.createElement('div'); | |
warningItem.className = 'missile-warning-item'; | |
warningItem.textContent = `MISSILE ${index + 1}: ${distance}m`; | |
// ๊ฑฐ๋ฆฌ์ ๋ฐ๋ฅธ ์์ ๊ฐ์กฐ | |
if (distance < 500) { | |
warningItem.style.fontSize = '20px'; | |
warningItem.style.color = '#ff0000'; | |
warningItem.style.textShadow = '0 0 10px #ff0000, 0 0 20px #ff0000'; | |
} else if (distance < 1000) { | |
warningItem.style.fontSize = '18px'; | |
} | |
panel.appendChild(warningItem); | |
}); | |
document.body.appendChild(panel); | |
} | |
// 3D ์์น๋ฅผ ํ๋ฉด ์ขํ๋ก ๋ณํํ๋ ํฌํผ ํจ์ | |
function getScreenPosition(worldPosition) { | |
if (!window.gameInstance || !window.gameInstance.camera) return null; | |
const vector = worldPosition.clone(); | |
vector.project(window.gameInstance.camera); | |
// ์นด๋ฉ๋ผ ๋ค์ ์๋ ๊ฐ์ฒด๋ null ๋ฐํ | |
if (vector.z > 1) return null; | |
const x = (vector.x * 0.5 + 0.5) * window.innerWidth; | |
const y = (-vector.y * 0.5 + 0.5) * window.innerHeight; | |
// ํ๋ฉด ๋ฐ์ ์์ผ๋ฉด null ๋ฐํ | |
if (x < 0 || x > window.innerWidth || y < 0 || y > window.innerHeight) { | |
return null; | |
} | |
return { x, y }; | |
} | |
// startGame ํจ์ ์ ์ | |
window.startGame = function() { | |
if (!window.gameInstance || !window.gameInstance.isLoaded || !window.gameInstance.isBGMReady) { | |
console.log('๊ฒ์์ด ์์ง ์ค๋น๋์ง ์์์ต๋๋ค...'); | |
return; | |
} | |
gameStarted = true; | |
document.getElementById('startScreen').style.display = 'none'; | |
// ๊ฒ์ ํ์ดํ ์จ๊ธฐ๊ธฐ | |
const gameTitle = document.getElementById('gameTitle'); | |
if (gameTitle) { | |
gameTitle.style.display = 'none'; | |
} | |
document.body.requestPointerLock(); | |
window.gameInstance.startBGM(); | |
window.gameInstance.startGame(); | |
} | |
</script> | |
<script src="game.js" type="module"></script> | |
</body> | |
</html> |