<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Space Rhythm Game</title>
<style>
/* สไตล์กราฟิกธีมอวกาศ/ดิสโก้ */
body {
margin: 0;
background: #0b0c10; /* พื้นหลังสีเข้ม */
color: #fff;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
}
#game-container {
position: relative;
width: 400px;
height: 600px;
background: radial-gradient(circle at center, #1f2833, #020202);
border: 2px solid #66fcf1;
box-shadow: 0 0 30px rgba(102, 252, 241, 0.4);
overflow: hidden;
border-radius: 10px;
}
/* เลนทั้ง 4 */
.lane {
position: absolute;
top: 0;
bottom: 0;
width: 100px;
border-right: 1px dashed rgba(102, 252, 241, 0.2);
box-sizing: border-box;
transition: background-color 0.1s;
}
.lane:nth-child(1) { left: 0; }
.lane:nth-child(2) { left: 100px; }
.lane:nth-child(3) { left: 200px; }
.lane:nth-child(4) { left: 300px; border-right: none; }
/* เอฟเฟกต์ไฟกะพริบตอนกดปุ่ม */
.flash {
background: linear-gradient(to top, rgba(102, 252, 241, 0.6), transparent) !important;
box-shadow: inset 0 -20px 20px rgba(102, 252, 241, 0.8);
}
/* เส้นเป้าหมาย */
.target-line {
position: absolute;
bottom: 60px;
left: 0;
width: 100%;
height: 10px;
background: rgba(255, 255, 255, 0.8);
box-shadow: 0 0 15px #ff007f, 0 0 30px #ff007f; /* แสงนีออนสีชมพู */
z-index: 5;
}
/* ตัวอักษรบอกปุ่ม */
.key-label {
position: absolute;
bottom: 15px;
width: 100%;
text-align: center;
font-size: 24px;
font-weight: bold;
color: #66fcf1;
text-shadow: 0 0 10px #66fcf1;
z-index: 10;
}
/* ตัวโน้ต */
.note {
position: absolute;
width: 60px;
height: 60px;
background: #ff007f;
border-radius: 50%;
left: 20px;
box-shadow: 0 0 15px #ff007f, inset 0 0 10px #fff;
z-index: 4;
}
/* ส่วนแสดงคะแนนและคำตัดสิน */
#ui {
position: absolute;
top: 20px;
width: 100%;
text-align: center;
z-index: 10;
pointer-events: none;
}
#score { font-size: 28px; font-weight: bold; text-shadow: 0 0 5px #fff; }
#judgment {
font-size: 40px;
font-weight: bold;
margin-top: 15px;
min-height: 50px;
text-transform: uppercase;
letter-spacing: 2px;
}
/* หน้าจอเริ่มเกม */
#start-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(11, 12, 16, 0.9);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 20;
}
h1 { color: #66fcf1; text-shadow: 0 0 20px #66fcf1; font-size: 36px; margin-bottom: 10px; }
p { color: #c5c6c7; margin-bottom: 30px; }
button {
padding: 15px 40px;
font-size: 22px;
background: transparent;
color: #66fcf1;
border: 2px solid #66fcf1;
cursor: pointer;
border-radius: 30px;
font-weight: bold;
box-shadow: 0 0 15px rgba(102, 252, 241, 0.5);
transition: all 0.3s ease;
}
button:hover { background: #66fcf1; color: #0b0c10; box-shadow: 0 0 25px #66fcf1; }
</style>
</head>
<body>
<div id="game-container">
<div id="ui">
<div id="score">Score: 0</div>
<div id="judgment"></div>
</div>
<div class="target-line"></div>
<div class="lane" id="lane-0"><div class="key-label">D</div></div>
<div class="lane" id="lane-1"><div class="key-label">F</div></div>
<div class="lane" id="lane-2"><div class="key-label">J</div></div>
<div class="lane" id="lane-3"><div class="key-label">K</div></div>
<div id="start-screen">
<h1>SPACE RHYTHM</h1>
<p>กดปุ่ม D, F, J, K ให้ตรงจังหวะ</p>
<button id="start-btn">PLAY NOW</button>
</div>
</div>
<script>
// การตั้งค่าเริ่มต้น
const lanes = [
{ key: 'd', element: document.getElementById('lane-0'), notes: [] },
{ key: 'f', element: document.getElementById('lane-1'), notes: [] },
{ key: 'j', element: document.getElementById('lane-2'), notes: [] },
{ key: 'k', element: document.getElementById('lane-3'), notes: [] }
];
const scoreDisplay = document.getElementById('score');
const judgmentDisplay = document.getElementById('judgment');
let score = 0;
let isPlaying = false;
let spawnInterval;
const speed = 7; // ความเร็วของตัวโน้ตที่ตกลงมา
const targetY = 480; // ตำแหน่ง Y ที่เหมาะสม (ตัวโน้ตทับเส้นเป้าหมาย)
// --- ระบบเสียง (สังเคราะห์จังหวะกลองด้วย Web Audio API) ---
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
function playKickDrum() {
if (audioCtx.state === 'suspended') audioCtx.resume();
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.connect(gain);
gain.connect(audioCtx.destination);
// สร้างเสียงกระเดื่อง (Kick Drum) สไตล์ดิสโก้/อิเล็กทรอนิกส์
osc.frequency.setValueAtTime(150, audioCtx.currentTime);
osc.frequency.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.5);
gain.gain.setValueAtTime(1, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.5);
osc.start();
osc.stop(audioCtx.currentTime + 0.5);
}
let beatTimer;
function startMusic() {
// เล่นจังหวะทุกๆ 400 มิลลิวินาที (~150 BPM)
beatTimer = setInterval(() => {
playKickDrum();
}, 400);
}
// --- ระบบเกมเพลย์ ---
// สร้างตัวโน้ตใหม่
function spawnNote() {
const laneIndex = Math.floor(Math.random() * 4); // สุ่มเลน 0-3
const lane = lanes[laneIndex];
const noteEl = document.createElement('div');
noteEl.className = 'note';
noteEl.style.top = '-60px'; // เริ่มต้นนอกจอด้านบน
lane.element.appendChild(noteEl);
lane.notes.push({ el: noteEl, y: -60 });
}
// อัปเดตตำแหน่งโน้ตทุกๆ เฟรม
function updateGame() {
if (!isPlaying) return;
lanes.forEach(lane => {
for (let i = 0; i < lane.notes.length; i++) {
let note = lane.notes[i];
note.y += speed; // ขยับลงมา
note.el.style.top = note.y + 'px';
// หากโน้ตหลุดหน้าจอ (Miss)
if (note.y > 600) {
note.el.remove();
lane.notes.splice(i, 1);
i--; // ถอยอินเด็กซ์เพราะอาร์เรย์ถูกตัด
showJudgment("Miss", "#ff4c4c", "0 0 15px #ff4c4c");
}
}
});
requestAnimationFrame(updateGame);
}
// ตรวจจับการกดคีย์บอร์ด
document.addEventListener('keydown', (e) => {
if (!isPlaying) return;
const key = e.key.toLowerCase();
const laneIndex = lanes.findIndex(l => l.key === key);
if (laneIndex !== -1) {
const lane = lanes[laneIndex];
// เอฟเฟกต์ไฟกะพริบที่เลน
lane.element.classList.add('flash');
setTimeout(() => lane.element.classList.remove('flash'), 100);
// ตรวจสอบความแม่นยำเมื่อกด
if (lane.notes.length > 0) {
// ตรวจโน้ตตัวที่อยู่ต่ำสุดในเลนนั้น
const note = lane.notes[0];
const distance = Math.abs(note.y - targetY); // คำนวณระยะห่างจากเส้นเป้าหมาย
if (distance < 30) {
// กดตรงเป๊ะ
score += 100;
showJudgment("Perfect!", "#66fcf1", "0 0 20px #66fcf1");
removeNote(lane, 0);
} else if (distance < 70) {
// กดเกือบตรง
score += 50;
showJudgment("Good", "#ffd700", "0 0 20px #ffd700");
removeNote(lane, 0);
}
}
}
});
// ลบโน้ตที่กดโดน
function removeNote(lane, index) {
lane.notes[index].el.remove();
lane.notes.splice(index, 1);
scoreDisplay.innerText = "Score: " + score;
}
// แสดงคำตัดสิน (Perfect, Good, Miss)
let judgmentTimeout;
function showJudgment(text, color, shadow) {
judgmentDisplay.innerText = text;
judgmentDisplay.style.color = color;
judgmentDisplay.style.textShadow = shadow;
clearTimeout(judgmentTimeout);
judgmentTimeout = setTimeout(() => {
judgmentDisplay.innerText = "";
}, 400); // ข้อความจะหายไปใน 0.4 วินาที
}
// เริ่มเกม
document.getElementById('start-btn').addEventListener('click', () => {
document.getElementById('start-screen').style.display = 'none';
isPlaying = true;
score = 0;
scoreDisplay.innerText = "Score: 0";
audioCtx.resume(); // เริ่มระบบเสียง
startMusic(); // เริ่มเปิดจังหวะ
spawnInterval = setInterval(spawnNote, 600); // สร้างโน้ตใหม่ทุกๆ 0.6 วินาที
updateGame(); // เริ่ม Loop เกม
});
</script>
</body>
</html>