๋์์ ์๋น์ค๊ฐ ์ข ๋ฃ๋์ด ํด๋น ์ฝํ ์ธ ๋ฅผ ์ฌ์ํ ์ ์์ต๋๋ค.
์ฌ๋ฌ๋ถ์ ์ค๋ ฅ์ ๋ฝ๋ด๋ณด์ธ์
์ฌ์ฉ ์ฝ๋ 1(์์ ๋ฃ๊ณ ๋ ธ๋ ๋ฝ๋ ํ๋ก๊ทธ๋จ)
import librosa
import numpy as np
import json
import os
WAV_FILE = "fantaisie.wav"
OUTPUT_FILE = "beatmap.json"
def create_beatmap():
if not os.path.exists(WAV_FILE):
print(f"์ค๋ฅ. {WAV_FILE} ํ์ผ์ด ์์ต๋๋ค")
return
print("์์
๋ถ์ ์ค")
y, sr = librosa.load(WAV_FILE, sr=22050)
onset_env = librosa.onset.onset_strength(y=y, sr=sr)
onset_frames = librosa.onset.onset_detect(
onset_envelope=onset_env,
sr=sr,
wait=3,
delta=0.04
)
timestamps = librosa.frames_to_time(onset_frames, sr=sr)
timestamps_list = timestamps.tolist()
with open(OUTPUT_FILE, 'w') as f:
json.dump(timestamps_list, f)
print(f"{len(timestamps_list)}๊ฐ์ ๋
ธํธ๊ฐ ์์ฑ๋์์ต๋๋ค")
print(f"๊ฒฐ๊ณผ๊ฐ {OUTPUT_FILE}์ ์ ์ฅ๋์์ต๋๋ค")
if __name__ == "__main__":
create_beatmap()
์ฌ์ฉ ์ฝ๋ 2(๋ฉ์ธ ์ฝ๋)
import sys
import os
import time
import json
import random
from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QPushButton,
QVBoxLayout, QHBoxLayout, QStackedWidget)
from PyQt5.QtCore import QTimer, Qt, QUrl
from PyQt5.QtGui import QPainter, QColor, QFont, QPen
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 600
NOTE_SPEED = 8
HIT_Y = 500
HIT_RANGE = 70
class RhythmGameWidget(QWidget):
def __init__(self, main_window):
super().__init__()
self.main_window = main_window
self.score = 0
self.notes = []
self.is_playing = False
self.is_game_over = False
self.timer = QTimer()
self.timer.timeout.connect(self.game_loop)
self.score_font = QFont("Arial", 24, QFont.Bold)
self.gameover_font = QFont("Arial", 36, QFont.Bold)
self.player = QMediaPlayer()
self.hit_player = QMediaPlayer()
bgm = QUrl.fromLocalFile(os.path.abspath("fantaisie.wav"))
hit = QUrl.fromLocalFile(os.path.abspath("hit.mp3"))
self.player.setMedia(QMediaContent(bgm))
self.hit_player.setMedia(QMediaContent(hit))
self.map_file = "beatmap.json"
self.note_timestamps = []
if os.path.exists(self.map_file):
with open(self.map_file, 'r') as f:
self.note_timestamps = json.load(f)
self.current_note_index = 0
self.start_time = 0
def start_game(self):
if not self.note_timestamps:
self.main_window.show_status("์ค๋ฅ. beatmap.json ํ์ผ์ด ์์ต๋๋ค")
self.main_window.back_to_form()
return
self.score = 0
self.notes = []
self.current_note_index = 0
self.is_playing = True
self.is_game_over = False
self.start_time = time.time()
self.timer.start(16)
self.setFocus()
self.player.stop()
self.player.play()
def stop_game(self):
if not self.is_playing:
return
self.is_playing = False
self.is_game_over = True
QTimer.singleShot(20, self._finish_stop)
def _finish_stop(self):
self.timer.stop()
self.player.stop()
self.update()
QTimer.singleShot(700, self._safe_return)
def _safe_return(self):
self.main_window.finish_game(self.score)
def play_hit_sound(self):
self.hit_player.stop()
self.hit_player.play()
def game_loop(self):
if not self.is_playing:
return
now = time.time() - self.start_time
while self.current_note_index < len(self.note_timestamps):
t = self.note_timestamps[self.current_note_index]
if t <= now:
x = random.randint(0, WINDOW_WIDTH - 50)
self.notes.append({'x': x, 'y': -20})
self.current_note_index += 1
else:
break
for note in self.notes:
note['y'] += NOTE_SPEED
if note['y'] > WINDOW_HEIGHT:
self.stop_game()
return
self.update()
def paintEvent(self, event):
p = QPainter(self)
p.setRenderHint(QPainter.Antialiasing)
p.fillRect(self.rect(), QColor("#222222"))
if self.is_game_over:
p.setPen(QColor("#FF4444"))
p.setFont(self.gameover_font)
p.drawText(self.rect(), Qt.AlignCenter, "์
๋ ฅ ์๋ฃ")
p.setPen(QColor("white"))
p.setFont(self.score_font)
p.drawText(self.rect().adjusted(0, 90, 0, 0),
Qt.AlignCenter, f"์
๋ ฅ๋ ์ ๋ณด: {self.score}")
return
pen = QPen(QColor(255, 255, 255))
pen.setWidth(2)
p.setPen(pen)
p.drawLine(0, HIT_Y, WINDOW_WIDTH, HIT_Y)
p.setBrush(QColor("#66CCFF"))
p.setPen(Qt.NoPen)
for note in self.notes:
p.drawRect(note['x'], note['y'], 50, 20)
p.setPen(QColor("#66CCFF"))
p.setFont(self.score_font)
p.drawText(self.rect(),
Qt.AlignTop | Qt.AlignHCenter,
f"COMBO: {self.score}")
def keyPressEvent(self, event):
if not self.is_playing:
return
if event.key() == Qt.Key_Space:
for note in self.notes[:]:
if HIT_Y - HIT_RANGE < note['y'] < HIT_Y + HIT_RANGE:
self.notes.remove(note)
self.score += 1
self.play_hit_sound()
break
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Input System")
self.setFixedSize(WINDOW_WIDTH, WINDOW_HEIGHT)
self.setStyleSheet("background-color: #f9f9f9; font-family: 'Malgun Gothic';")
self.stack = QStackedWidget(self)
self.form_widget = QWidget()
self.setup_form_ui()
self.game_widget = RhythmGameWidget(self)
self.stack.addWidget(self.form_widget)
self.stack.addWidget(self.game_widget)
layout = QVBoxLayout(self)
layout.addWidget(self.stack)
layout.setContentsMargins(0, 0, 0, 0)
self.current_target = None
def setup_form_ui(self):
layout = QVBoxLayout()
layout.setAlignment(Qt.AlignCenter)
layout.setSpacing(20)
title = QLabel("์๋
์์ผ ์
๋ ฅ")
title.setStyleSheet("font-size: 24px; font-weight: bold; color: #333;")
title.setAlignment(Qt.AlignCenter)
self.status_label = QLabel("์
๋ ฅ๋ ์ ๋ณด๋ ์ฌ์ฉ ํ ํ๊ธฐ๋ฉ๋๋ค")
self.status_label.setStyleSheet("font-size: 14px; color: #666;")
self.status_label.setAlignment(Qt.AlignCenter)
input_layout = QHBoxLayout()
self.btn_year = self.create_input_btn("YYYY")
self.btn_month = self.create_input_btn("MM")
self.btn_day = self.create_input_btn("DD")
input_layout.addWidget(self.btn_year)
input_layout.addWidget(self.btn_month)
input_layout.addWidget(self.btn_day)
btn_submit = QPushButton("์ ์ถํ๊ธฐ")
btn_submit.setStyleSheet("""
background-color: #333; color: white; border-radius: 8px;
padding: 12px; font-weight: bold;
""")
layout.addStretch()
layout.addWidget(title)
layout.addWidget(self.status_label)
layout.addLayout(input_layout)
layout.addWidget(btn_submit)
layout.addStretch()
self.form_widget.setLayout(layout)
def create_input_btn(self, text):
btn = QPushButton(text)
btn.setFixedSize(90, 60)
btn.setStyleSheet("""
QPushButton {
border: 2px solid #ddd;
border-radius: 12px;
background: white;
font-size: 18px;
color: #555;
}
QPushButton:hover { border-color: #66CCFF; }
""")
btn.clicked.connect(lambda: self.start_game_sequence(btn))
return btn
def start_game_sequence(self, btn):
self.current_target = btn
self.stack.setCurrentWidget(self.game_widget)
self.game_widget.start_game()
def back_to_form(self):
self.stack.setCurrentWidget(self.form_widget)
def show_status(self, msg):
self.status_label.setText(msg)
def finish_game(self, score):
QTimer.singleShot(10, lambda: self._finish(score))
def _finish(self, score):
self.stack.setCurrentWidget(self.form_widget)
if self.current_target:
self.current_target.setText(str(score))
self.current_target.setStyleSheet("""
border: 2px solid #FF4444;
border-radius: 12px;
background: #FFF0F0;
color: #FF4444;
font-size: 20px;
font-weight: bold;
""")
self.status_label.setText("์
๋ ฅ ์๋ฃ")
self.status_label.setStyleSheet(
"font-size: 16px; color: #FF4444; font-weight: bold;"
)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())'๊ณต๋ถ > ์ฝ๋ฉ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [์๊ณ ๋ฆฌ์ฆ ์คํฐ๋] 4์ฃผ์ฐจ ๊ฒฐ์ฐ (0) | 2025.12.12 |
|---|---|
| [์๊ณ ๋ฆฌ์ฆ ์คํฐ๋] 3์ฃผ์ฐจ ๊ฒฐ์ฐ (0) | 2025.12.12 |
| ์๊ณ ๋ฆฌ์ฆ ์คํฐ๋ 2์ฃผ์ฐจ ๊ฒฐ์ฐ (0) | 2025.10.05 |
| [์๊ณ ๋ฆฌ์ฆ ์คํฐ๋] 1์ฃผ์ฐจ ๊ฒฐ์ฐ (0) | 2025.09.27 |
| [C++] ๋ฒกํฐ๋ฅผ ์ง์ ์ ์ํด๋ณด์! (0) | 2025.08.20 |