파이썬으로 타일드롭 게임 만들기: 초보자를 위한 종합 가이드

파이썬으로 타일드롭 게임 만들기: 초보자를 위한 종합 가이드 | 지식에 대한 탐구

🚀 코딩 초보자도 쉽게 따라할 수 있는 파이썬 게임 개발의 세계로 여러분을 초대합니다! 이 가이드를 통해 재미있는 타일드롭 게임을 만들면서 프로그래밍의 기초를 배워보세요.

안녕하세요, 코딩 enthusiasts! 오늘은 파이썬을 사용하여 클래식한 블록 퍼즐 게임인 타일드롭을 만드는 방법을 단계별로 자세히 알아보겠습니다. 이 튜토리얼은 코딩을 처음 접하는 분들도 쉽게 따라할 수 있도록 구성되었습니다. 그럼 시작해볼까요?

 

목차

  1. 개발 환경 설정
  2. 게임 기본 구조 만들기
  3. 게임 보드 및 블록 설정
  4. 게임 로직 구현
  5. 그래픽 인터페이스 구현
  6. 사용자 입력 처리
  7. 게임 실행 및 테스트
  8. 추가 기능 및 개선 사항
  9. 코드 최적화 및 리팩토링
  10. 버전 관리 시스템 사용하기
  11. 다른 사람들과 공유하기
  12. 지속적인 학습과 개선

 

1. 개발 환경 설정

 

1.1 Python 설치

먼저, 컴퓨터에 Python이 설치되어 있어야 합니다.

  1. Python 공식 웹사이트에서 최신 버전의 Python을 다운로드하세요.
  2. 설치 프로그램을 실행하고 “Add Python to PATH” 옵션을 체크하세요.
  3. 설치가 완료되면 명령 프롬프트(Windows) 또는 터미널(Mac/Linux)을 열고 다음 명령어를 입력하여 Python이 제대로 설치되었는지 확인하세요:
python --version

 

1.2 Pygame 설치

Pygame은 Python으로 게임을 만들기 위한 라이브러리입니다.

  1. 명령 프롬프트 또는 터미널에서 다음 명령어를 실행하세요:
pip install pygame
  1. 설치가 완료되면 다음 명령어로 Pygame이 제대로 설치되었는지 확인하세요:
python -c "import pygame; print(pygame.ver)"

 

1.3 코드 에디터 설치

코드를 작성하기 위한 에디터가 필요합니다. 초보자에게는 Visual Studio Code를 추천합니다.

  1. Visual Studio Code 웹사이트에서 다운로드하고 설치하세요.
  2. VS Code를 실행하고 왼쪽 사이드바에서 확장(Extensions) 메뉴를 클릭하세요.
  3. 검색창에 “Python”을 입력하고 Microsoft에서 제공하는 Python 확장을 설치하세요.

 

2. 게임 기본 구조 만들기

이제 게임의 기본 구조를 만들어 보겠습니다.

  1. VS Code를 열고 새 폴더를 만드세요 (예: tiledrop_game).
  2. 이 폴더 내에 tiledrop.py 파일을 생성하세요.
  3. tiledrop.py 파일을 열고 다음 코드를 입력하세요:
# 필요한 라이브러리 임포트
import pygame
import random

# 게임 초기화
pygame.init()

# 게임 설정
BLOCK_SIZE = 30  # 블록 크기
BOARD_WIDTH = 10  # 게임 보드 너비
BOARD_HEIGHT = 20  # 게임 보드 높이
SCREEN_WIDTH = BLOCK_SIZE * (BOARD_WIDTH + 6)  # 화면 너비
SCREEN_HEIGHT = BLOCK_SIZE * BOARD_HEIGHT  # 화면 높이

# 색상 정의
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
CYAN = (0, 255, 255)
YELLOW = (255, 255, 0)
MAGENTA = (255, 0, 255)
ORANGE = (255, 165, 0)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)

# 화면 설정
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("TileDrop")

# 메인 게임 루프
def main():
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
        
        screen.fill(BLACK)
        pygame.display.flip()

if __name__ == "__main__":
    main()
    pygame.quit()

이 코드는 게임의 기본 구조를 설정합니다. Pygame을 초기화하고, 게임 창을 생성하며, 간단한 게임 루프를 구현합니다.

 







 

3. 게임 보드 및 블록 설정

다음으로 게임 보드와 블록을 설정해 보겠습니다.

  1. tiledrop.py 파일에 다음 코드를 추가하세요:
# 테트로미노 모양
SHAPES = [
    [[1, 1, 1, 1]],
    [[1, 1], [1, 1]],
    [[1, 1, 1], [0, 1, 0]],
    [[1, 1, 1], [1, 0, 0]],
    [[1, 1, 1], [0, 0, 1]],
    [[1, 1, 0], [0, 1, 1]],
    [[0, 1, 1], [1, 1, 0]]
]

COLORS = [CYAN, YELLOW, MAGENTA, ORANGE, BLUE, RED, GREEN]

class TileDrop:
    def __init__(self):
        self.board = [[0 for _ in range(BOARD_WIDTH)] for _ in range(BOARD_HEIGHT)]
        self.current_piece = self.new_piece()
        self.game_over = False
        self.score = 0

    def new_piece(self):
        shape = random.choice(SHAPES)
        color = random.choice(COLORS)
        return {
            'shape': shape,
            'color': color,
            'x': BOARD_WIDTH // 2 - len(shape[0]) // 2,
            'y': 0
        }

이 코드는 게임에서 사용할 블록(테트로미노)의 모양과 색상을 정의하고, TileDrop 클래스를 생성하여 게임 보드와 현재 블록을 초기화합니다.

 

4. 게임 로직 구현

이제 게임의 핵심 로직을 구현해 보겠습니다.

  1. TileDrop 클래스에 다음 메서드들을 추가하
    1. TileDrop 클래스에 다음 메서드들을 추가하세요:
    def valid_move(self, shape, x, y):
        # 이동이 유효한지 확인하는 메서드
        for i, row in enumerate(shape):
            for j, cell in enumerate(row):
                if cell:
                    if (x + j < 0 or x + j >= BOARD_WIDTH or
                        y + i >= BOARD_HEIGHT or
                        (y + i >= 0 and self.board[y + i][x + j])):
                        return False
        return True
    
    def place_piece(self):
        # 현재 조각을 보드에 배치하는 메서드
        for i, row in enumerate(self.current_piece['shape']):
            for j, cell in enumerate(row):
                if cell:
                    self.board[self.current_piece['y'] + i][self.current_piece['x'] + j] = self.current_piece['color']
        self.clear_lines()
        self.current_piece = self.new_piece()
        if not self.valid_move(self.current_piece['shape'], self.current_piece['x'], self.current_piece['y']):
            self.game_over = True
    
    def clear_lines(self):
        # 완성된 줄을 제거하고 점수를 업데이트하는 메서드
        lines_cleared = 0
        for i in range(BOARD_HEIGHT - 1, -1, -1):
            if all(self.board[i]):
                del self.board[i]
                self.board.insert(0, [0 for _ in range(BOARD_WIDTH)])
                lines_cleared += 1
        self.score += lines_cleared ** 2 * 100
    
    def move(self, dx, dy):
        # 조각을 이동시키는 메서드
        if self.valid_move(self.current_piece['shape'], self.current_piece['x'] + dx, self.current_piece['y'] + dy):
            self.current_piece['x'] += dx
            self.current_piece['y'] += dy
            return True
        return False
    
    def rotate(self):
        # 조각을 회전시키는 메서드
        rotated = list(zip(*self.current_piece['shape'][::-1]))
        if self.valid_move(rotated, self.current_piece['x'], self.current_piece['y']):
            self.current_piece['shape'] = rotated

    이 코드는 블록의 이동, 회전, 배치, 라인 제거 등의 게임 로직을 구현합니다.

     

    5. 그래픽 인터페이스 구현

    게임의 시각적 요소를 구현해 봅시다.

    1. TileDrop 클래스에 draw 메서드를 추가하세요:
    def draw(self):
        # 게임 보드와 현재 조각을 화면에 그리는 메서드
        screen.fill(BLACK)
        for y, row in enumerate(self.board):
            for x, cell in enumerate(row):
                if cell:
                    pygame.draw.rect(screen, cell, (x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1))
    
        if self.current_piece:
            for i, row in enumerate(self.current_piece['shape']):
                for j, cell in enumerate(row):
                    if cell:
                        pygame.draw.rect(screen, self.current_piece['color'],
                                         ((self.current_piece['x'] + j) * BLOCK_SIZE,
                                          (self.current_piece['y'] + i) * BLOCK_SIZE,
                                          BLOCK_SIZE - 1, BLOCK_SIZE - 1))
    
        font = pygame.font.Font(None, 36)
        score_text = font.render(f"Score: {self.score}", True, WHITE)
        screen.blit(score_text, (BOARD_WIDTH * BLOCK_SIZE + 10, 10))
    
        if self.game_over:
            game_over_text = font.render("GAME OVER", True, RED)
            screen.blit(game_over_text, (SCREEN_WIDTH // 2 - 70, SCREEN_HEIGHT // 2 - 30))
    
        pygame.display.flip()

    이 메서드는 게임 보드, 현재 블록, 점수, 그리고 게임 오버 메시지를 화면에 그립니다.

     

    6. 사용자 입력 처리

    이제 사용자의 키보드 입력을 처리하는 로직을 구현해 봅시다.

    1. main 함수를 다음과 같이 수정하세요:
    def main():
        game = TileDrop()
        clock = pygame.time.Clock()
        fall_time = 0
        fall_speed = 0.5  # 초당 한 칸씩 떨어짐
    
        while True:
            fall_time += clock.get_rawtime()
            clock.tick()
    
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_LEFT:
                        game.move(-1, 0)
                    elif event.key == pygame.K_RIGHT:
                        game.move(1, 0)
                    elif event.key == pygame.K_DOWN:
                        game.move(0, 1)
                    elif event.key == pygame.K_UP:
                        game.rotate()
    
            if fall_time / 1000 > fall_speed:
                fall_time = 0
                if not game.move(0, 1):
                    game.place_piece()
                    if game.game_over:
                        game = TileDrop()
    
            game.draw()

    이 코드는 키보드 이벤트를 처리하고, 블록이 자동으로 떨어지는 로직을 구현합니다.

     

    7. 게임 실행 및 테스트

    모든 코드를 작성했다면 이제 게임을 실행해 볼 차례입니다!

    1. 터미널이나 명령 프롬프트에서 게임 파일이 있는 디렉토리로 이동하세요.
    2. 다음 명령어를 실행하세요:
    python tiledrop.py
    1. 게임 창이 열리면 다음과 같이 조작할 수 있습니다:
      • 왼쪽 화살표: 블록을 왼쪽으로 이동
      • 오른쪽 화살표: 블록을 오른쪽으로 이동
      • 아래쪽 화살표: 블록을 아래로 빠르게 이동
      • 위쪽 화살표: 블록을 회전

     

    8. 추가 기능 및 개선 사항

    기본적인 게임이 완성되었습니다! 하지만 여기서 멈추지 말고 더 재미있는 게임을 만들어 보는 것은 어떨까요? 다음과 같은 기능들을 추가해 보세요:

    1. 레벨 시스템: 점수에 따라 블록이 떨어지는 속도를 증가시킵니다.
    2. 다음 블록 미리보기: 다음에 나올 블록을 화면 한쪽에 표시합니다.
    3. 블록 홀드 기능: 현재 블록을 저장하고 나중에 사용할 수 있는 기능을 추가합니다.
    4. 사운드 효과: 블록 이동, 회전, 라인 제거 등의 동작에 사운드 효과를 추가합니다.
    5. 하이스코어 시스템: 최고 점수를 저장하고 표시합니다.
    6. 일시정지 기능: 게임을 잠시 멈출 수 있는 기능을 추가합니다.
    7. 시작 화면과 게임 오버 화면: 더 세련된 UI를 만들어 게임의 시작과 끝을 표현합니다.

     

    9. 코드 최적화 및 리팩토링

    게임이 완성되고 추가 기능을 구현한 후에는 코드를 최적화하고 리팩토링하는 것이 좋습니다. 이는 코드의 가독성을 높이고 성능을 개선하는 데 도움이 됩니다.

    1. 함수 분리: 큰 함수들을 더 작고 특정한 기능을 하는 함수들로 분리합니다.
    2. 상수 사용: 매직 넘버(코드 내의 직접적인 숫자값)를 의미 있는 이름의 상수로 대체합니다.
    3. 주석 추가: 복잡한 로직에 주석을 달아 코드의 의도를 명확히 합니다.
    4. 명명 규칙 준수: 파이썬의 PEP 8 스타일 가이드를 따라 변수와 함수 이름을 지정합니다.

     







     

    10. 버전 관리 시스템 사용하기

    프로젝트가 커지면 버전 관리의 필요성이 생깁니다. Git을 사용하여 코드의 변경 사항을 추적하고 관리해보세요.

    1. Git 설치
    2. 프로젝트 폴더에서 Git 저장소 초기화:
    git init
    1. 변경 사항을 스테이징하고 커밋하기:
    git add .
    git commit -m "Initial commit: Basic TileDrop game implementation"

    이렇게 하면 코드의 변경 사항을 추적하고 필요할 때 이전 버전으로 돌아갈 수 있습니다.

     

    11. 다른 사람들과 공유하기

    만든 게임을 다른 사람들과 공유해보세요! GitHub와 같은 플랫폼을 사용하면 쉽게 코드를 공유하고 다른 개발자들의 피드백을 받을 수 있습니다.

    1. GitHub에 계정을 만듭니다.
    2. 새 저장소를 생성합니다.
    3. 로컬 저장소를 GitHub 저장소에 연결하고 푸시합니다:
    git remote add origin <your-github-repo-url>
    git push -u origin master

     

    12. 지속적인 학습과 개선

    프로그래밍 실력 향상은 끊임없는 학습과 연습의 결과입니다. 다음과 같은 방법으로 계속해서 실력을 키워나갈 수 있습니다:

    1. 다른 파이썬 프로젝트 탐구하기
    2. 파이썬 및 Pygame 관련 문서 읽기
    3. 코딩 챌린지 참여하기 (예: LeetCode, HackerRank)
    4. 프로그래밍 커뮤니티 참여하기 (예: Stack Overflow, Reddit r/learnpython)

     

    축하합니다! 여러분은 이제 파이썬과 Pygame을 사용하여 기본적인 타일드롭 게임을 만들고, 추가 기능을 구현하고,

    코드를 최적화하는 방법을 배웠습니다. 이는 게임 개발과 프로그래밍의 세계로 들어가는 첫 걸음일 뿐입니다.

    계속해서 코딩을 연습하고, 새로운 것을 배우고, 자신만의 아이디어를 실현해 보세요. 프로그래밍의 여정은 끝이 없으며, 항상 새로운 도전과 배울 것들이 기다리고 있습니다.

    마지막으로, 코딩을 즐기세요! 프로그래밍은 단순한 기술 이상의 것입니다. 그것은 창의성을 표현하고, 문제를 해결하고, 세상을 조금씩 바꿀 수 있는 강력한 도구입니다. 여러분의 코딩 여정에 행운이 있기를 바랍니다! 🚀👨‍💻👩‍💻

    전체 코드

    다음은 tiledrop.py 파일의 전체 코드입니다:

    import pygame
    import random
    
    # 게임 초기화
    pygame.init()
    
    # 게임 설정
    BLOCK_SIZE = 30
    BOARD_WIDTH = 10
    BOARD_HEIGHT = 20
    SCREEN_WIDTH = BLOCK_SIZE * (BOARD_WIDTH + 6)
    SCREEN_HEIGHT = BLOCK_SIZE * BOARD_HEIGHT
    
    # 색상 정의
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    CYAN = (0, 255, 255)
    YELLOW = (255, 255, 0)
    MAGENTA = (255, 0, 255)
    ORANGE = (255, 165, 0)
    BLUE = (0, 0, 255)
    RED = (255, 0, 0)
    GREEN = (0, 255, 0)
    
    # 화면 설정
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption("TileDrop")
    
    # 테트로미노 모양
    SHAPES = [
        [[1, 1, 1, 1]],
        [[1, 1], [1, 1]],
        [[1, 1, 1], [0, 1, 0]],
        [[1, 1, 1], [1, 0, 0]],
        [[1, 1, 1], [0, 0, 1]],
        [[1, 1, 0], [0, 1, 1]],
        [[0, 1, 1], [1, 1, 0]]
    ]
    
    COLORS = [CYAN, YELLOW, MAGENTA, ORANGE, BLUE, RED, GREEN]
    
    class TileDrop:
        def __init__(self):
            self.board = [[0 for _ in range(BOARD_WIDTH)] for _ in range(BOARD_HEIGHT)]
            self.current_piece = self.new_piece()
            self.game_over = False
            self.score = 0
    
        def new_piece(self):
            # 새로운 테트로미노 생성
            shape = random.choice(SHAPES)
            color = random.choice(COLORS)
            return {
                'shape': shape,
                'color': color,
                'x': BOARD_WIDTH // 2 - len(shape[0]) // 2,
                'y': 0
            }
    
        def valid_move(self, shape, x, y):
            # 이동이 유효한지 확인
            for i, row in enumerate(shape):
                for j, cell in enumerate(row):
                    if cell:
                        if (x + j < 0 or x + j >= BOARD_WIDTH or
                            y + i >= BOARD_HEIGHT or
                            (y + i >= 0 and self.board[y + i][x + j])):
                            return False
            return True
    
        def place_piece(self):
            # 현재 조각을 보드에 배치
            for i, row in enumerate(self.current_piece['shape']):
                for j, cell in enumerate(row):
                    if cell:
                        self.board[self.current_piece['y'] + i][self.current_piece['x'] + j] = self.current_piece['color']
            self.clear_lines()
            self.current_piece = self.new_piece()
            if not self.valid_move(self.current_piece['shape'], self.current_piece['x'], self.current_piece['y']):
                self.game_over = True
    
        def clear_lines(self):
            # 완성된 줄 제거 및 점수 계산
            lines_cleared = 0
            for i in range(BOARD_HEIGHT - 1, -1, -1):
                if all(self.board[i]):
                    del self.board[i]
                    self.board.insert(0, [0 for _ in range(BOARD_WIDTH)])
                    lines_cleared += 1
            self.score += lines_cleared ** 2 * 100
    
        def move(self, dx, dy):
            # 조각 이동
            if self.valid_move(self.current_piece['shape'], self.current_piece['x'] + dx, self.current_piece['y'] + dy):
                self.current_piece['x'] += dx
                self.current_piece['y'] += dy
                return True
            return False
    
        def rotate(self):
            # 조각 회전
            rotated = list(zip(*self.current_piece['shape'][::-1]))
            if self.valid_move(rotated, self.current_piece['x'], self.current_piece['y']):
                self.current_piece['shape'] = rotated
    
        def draw(self):
            # 게임 화면 그리기
            screen.fill(BLACK)
            for y, row in enumerate(self.board):
                for x, cell in enumerate(row):
                    if cell:
                        pygame.draw.rect(screen, cell, (x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1))
    
            if self.current_piece:
                for i, row in enumerate(self.current_piece['shape']):
                    for j, cell in enumerate(row):
                        if cell:
                            pygame.draw.rect(screen, self.current_piece['color'],
                                             ((self.current_piece['x'] + j) * BLOCK_SIZE,
                                              (self.current_piece['y'] + i) * BLOCK_SIZE,
                                              BLOCK_SIZE - 1, BLOCK_SIZE - 1))
    
            font = pygame.font.Font(None, 36)
            score_text = font.render(f"Score: {self.score}", True, WHITE)
            screen.blit(score_text, (BOARD_WIDTH * BLOCK_SIZE + 10, 10))
    
            if self.game_over:
                game_over_text = font.render("GAME OVER", True, RED)
                screen.blit(game_over_text, (SCREEN_WIDTH // 2 - 70, SCREEN_HEIGHT // 2 - 30))
    
            pygame.display.flip()
    
    def main():
        game = TileDrop()
        clock = pygame.time.Clock()
        fall_time = 0
        fall_speed = 0.5  # 초당 한 칸씩 떨어짐
    
        while True:
            fall_time += clock.get_rawtime()
            clock.tick()
    
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_LEFT:
                        game.move(-1, 0)
                    elif event.key == pygame.K_RIGHT:
                        game.move(1, 0)
                    elif event.key == pygame.K_DOWN:
                        game.move(0, 1)
                    elif event.key == pygame.K_UP:
                        game.rotate()
    
            if fall_time / 1000 > fall_speed:
                fall_time = 0
                if not game.move(0, 1):
                    game.place_piece()
                    if game.game_over:
                        game = TileDrop()
    
            game.draw()
    
    if __name__ == "__main__":
        main()
        pygame.quit()