본문 바로가기
Project

파이썬을 이용한 리듬게임 만들기

by re-hwi 2023. 6. 21.

앞서 종합 아케이드 게임기를 만들며 리듬게임을 만들어 보았다. 사실 그렇게 완성도도 높지 않고 아직 허술하지만 팀원과 함께 만들면서 재미도 있었고, 많은 노력이 들어갔던 만큼 완성했을 때 뿌듯함도 있었다.

 

그런데 노래선택을 할 수 없고, 메인 창이 없어서 되돌아가기도 못하기 때문에 그렇게 완성도 있는 작품이라고 하기엔 조금 허술한 점이 있다.

 

먼저 가장 핵심이 되는 노트가 내려오는 타이밍은 파이썬의 라이브러리인 librosa를 사용했다. 이 라이브러리를 사용하면 음악의 특징을 뽑아내서 템포에 맞춰 노트를 생성하는게 가능하다. 여기서 추가로 음의 변화에 따라 노트를 생성하고 싶었는데 라이브러리에 미숙한 탓인지 실패했다.

 

 

다음은 내가 코드를 작성했던 순서이다.

1. 노트가 내려오는 트랙 그리기

2. 노트 클래스 만들기 > 트랙이 아래로 내려 올 수록 넓어지기 때문에 노트의 높이와 넓이 조절

3. librosa 라이브러리를 이용해 음악의 템포 추출

4. 그 템포에 맞춰 노트 생성

5. 노트가 일정 범위 안에 들어왔을 때 버튼을 누르면 이펙트가 생기며 점수 증가

 

이렇게 5가지로 나누었고 다음은 메인 화면이다.

마지막으로 코드와 실행 영상이다.

import pygame
import sys
import random
import librosa
import time


def Rithomgame_run():

    pygame.init()

    h, w = 600, 900
    screen = pygame.display.set_mode((w, h))
    font = pygame.font.SysFont("Courier", 18, True, True)
    text_color = (255, 255, 255)

    song = "sound/gd.mp3"  # replace with your song file

    pygame.mixer.music.load(song)

    # Load the song using librosa
    y, sr = librosa.load(song, sr=None)
    tempo, beat_frames = librosa.beat.beat_track(y=y, sr=sr)
    beats_timing = librosa.frames_to_time(beat_frames, sr=sr)

    current_beat = 0
    pygame.mixer.music.play()

    class Block:
        # 초기값
        def __init__(self, x, y, dx):
            self.x = x
            self.y = y
            self.dx = dx
            self.width = 50
            self.created_time = time.time()
            self.hit = False

            # 업데이트
        def update(self):
            self.y += 1
            self.x += self.dx
            self.width = int(50 + (150 - 50) * (self.y / h))  # 블록의 크기 업데이트

        def draw(self):
            pygame.draw.rect(screen, (255, 255, 255), (self.x, self.y, self.width, 15))

        def process_note(self, key):
            if not self.hit and 500 < self.y < 650:
                if key == 'left' and self.dx < 0:
                    self.hit = True
                    if 550 < self.y < 650:
                        return 30
                    elif 500 < self.y < 550:
                        return 15
                elif key == 'mid':
                    self.hit = True
                    if 550 < self.y < 650:
                        return 30
                    elif 500 < self.y < 550:
                        return 15
                elif key == 'right' and self.dx > 0:
                    self.hit = True
                    if 550 < self.y < 650:
                        return 30
                    elif 500 < self.y < 550:
                        return 15

            if not self.hit and 450 < self.y < 500:
                if key == 'left' and self.dx < 0:
                    self.hit = True
                    return 10
                elif key == 'mid':
                    self.hit = True
                    return 10
                elif key == 'right' and self.dx > 0:
                    self.hit = True
                    return 10
            return 0

    def lerp_color(color1, color2, factor):
        return (
            int(color1[0] + (color2[0] - color1[0]) * factor),
            int(color1[1] + (color2[1] - color1[1]) * factor),
            int(color1[2] + (color2[2] - color1[2]) * factor)
        )

    blocks = []
    tracks = [(250, 0, -0.24), (300, 0, -0.08), (350, 0, 0.08)]  # 트랙별 시작 위치와 x 이동 속도
    start_time = time.time() - 1  # Add this line to initialize start_time

    # 이펙트 색
    blue_points = [(300, 600), (250, 0), (400, 0), (350, 600)]

    Right_points = [(350, 600), (400, 0), (548, 0), (400, 600)]
    Left_points = [(250, 600), (108, 0), (252, 0), (300, 600)]
    black = (0, 0, 0)
    blue = (0, 0, 255)
    purple = (255, 0, 0)
    rect_width = 2

    Left = False
    Mid = False
    Right = False

    score = 0
    last_block_creation_time = time.time()

    running = True

    # 게임 루프
    while running:
        screen.fill((0, 0, 0))  # 화면을 검은색으로 지우기
        for event in pygame.event.get():
            if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
                running = False

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_DOWN:
                    Mid = True
                    for block in blocks:
                        score += block.process_note('mid')
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_DOWN:
                    Mid = False

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    Left = True
                    for block in blocks:
                        score += block.process_note('left')
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT:
                    Left = False

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    Right = True
                    for block in blocks:
                        score += block.process_note('right')
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_RIGHT:
                    Right = False

            print("점수 : ", score)

        if Mid:
            for i in range(blue_points[1][1], blue_points[0][1], rect_width):
                progress = (i - blue_points[1][1]) / (blue_points[0][1] - blue_points[1][1])
                color = lerp_color(black, blue, progress)
                top_left_x = int(blue_points[0][0] + (blue_points[1][0] - blue_points[0][0]) * progress)
                top_right_x = int(blue_points[3][0] + (blue_points[2][0] - blue_points[3][0]) * progress)

                pygame.draw.rect(screen, color, pygame.Rect(top_left_x, i, top_right_x - top_left_x, rect_width))

        if Right:
            for i in range(Right_points[1][1], Right_points[0][1], rect_width):
                progress = (i - Right_points[1][1]) / (Right_points[0][1] - Right_points[1][1])
                color = lerp_color(black, purple, progress)
                top_left_x = int(Right_points[0][0] + (Right_points[1][0] - Right_points[0][0]) * progress)
                top_right_x = int(Right_points[3][0] + (Right_points[2][0] - Right_points[3][0]) * progress)

                pygame.draw.rect(screen, color, pygame.Rect(top_left_x, i, top_right_x - top_left_x, rect_width))

        if Left:
            for i in range(Left_points[1][1], Left_points[0][1], rect_width):
                progress = (i - Left_points[1][1]) / (Left_points[0][1] - Left_points[1][1])
                color = lerp_color(black, purple, progress)
                top_left_x = int(Left_points[0][0] + (Left_points[1][0] - Left_points[0][0]) * progress)
                top_right_x = int(Left_points[3][0] + (Left_points[2][0] - Left_points[3][0]) * progress)

                pygame.draw.rect(screen, color, pygame.Rect(top_left_x, i, top_right_x - top_left_x, rect_width))

        # 사다리꼴 라인 그리기
        pygame.draw.line(screen, (255, 0, 255), (250, 0), (100, 640), 5)
        pygame.draw.line(screen, (255, 0, 255), (400, 0), (550, 640), 5)
        pygame.draw.line(screen, (1, 85, 255), (300, 0), (250, 640), 3)
        pygame.draw.line(screen, (1, 85, 255), (350, 0), (400, 640), 3)

        for block in blocks:
            block.update()
            block.draw()

        # 스코어 생성
        score_text = font.render(f"Score: {score}", True, text_color)

        screen.blit(score_text, (600, 50))
        pygame.display.flip()

        elapsed_time = time.time() - start_time
        current_time = time.time() + 0.5
        if current_time - last_block_creation_time >= 0.8:
            block_x, _, block_dx = random.choice(tracks)
            blocks.append(Block(block_x, 0, block_dx))
            last_block_creation_time = current_time

    pygame.quit()
    sys.exit()

 

 

반응형

댓글