파이썬으로 시작하는 게임 개발의 세계 🐍🎮
안녕하세요! Python Game Dev 블로그의 다섯 번째 포스팅입니다. 이전에는 숫자 맞추기 게임, 퀴즈 게임, 행맨 게임, 그리고 틱택토 게임을 만들어보았는데요, 오늘은 많은 분들이 한 번쯤 해보셨을 클래식 게임인 스네이크(Snake) 게임을 만들어보겠습니다.
오늘의 게임: 스네이크 게임 🐍🍎
스네이크 게임은 플레이어가 뱀을 조종하여 먹이를 먹으면서 몸을 키워나가는 게임입니다. 게임의 난이도는 뱀의 몸이 길어질수록 증가하며, 벽이나 자신의 몸에 부딪히면 게임이 종료됩니다. 이번 포스팅에서는 키보드 방향키로 조작하는 스네이크 게임을 만들어보겠습니다.
게임의 규칙 📜
- 뱀은 처음에 화면 중앙에서 시작합니다.
- 방향키(↑, ↓, ←, →)를 사용하여 뱀을 조종합니다.
- 뱀은 계속 움직이며 멈출 수 없습니다.
- 화면에 랜덤하게 생성되는 먹이(🍎)를 먹으면 뱀의 길이가 증가합니다.
- 뱀이 벽이나 자신의 몸에 부딪히면 게임이 종료됩니다.
- 먹이를 먹을 때마다 점수가 증가합니다.
전체 코드 💻
import pygame
import random
import time
import sys
# 초기화
pygame.init()
# 색상 정의
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
DARK_GREEN = (0, 200, 0)
# 게임 설정
WIDTH, HEIGHT = 600, 600
GRID_SIZE = 20
GRID_WIDTH = WIDTH // GRID_SIZE
GRID_HEIGHT = HEIGHT // GRID_SIZE
SPEED = 10
# 방향 설정
UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)
# 창 설정
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Python Snake Game')
clock = pygame.time.Clock()
# 폰트 설정
font = pygame.font.SysFont('malgungothic', 25)
big_font = pygame.font.SysFont('malgungothic', 50)
def draw_grid():
"""그리드 라인 그리기"""
for x in range(0, WIDTH, GRID_SIZE):
pygame.draw.line(screen, (40, 40, 40), (x, 0), (x, HEIGHT))
for y in range(0, HEIGHT, GRID_SIZE):
pygame.draw.line(screen, (40, 40, 40), (0, y), (WIDTH, y))
def draw_snake(snake_body):
"""뱀 그리기"""
for i, segment in enumerate(snake_body):
# 머리는 더 진한 녹색으로 표시
color = DARK_GREEN if i == 0 else GREEN
pygame.draw.rect(screen, color,
(segment[0] * GRID_SIZE, segment[1] * GRID_SIZE,
GRID_SIZE, GRID_SIZE))
# 눈 그리기 (머리일 경우)
if i == 0:
eye_size = GRID_SIZE // 5
# 왼쪽 눈
pygame.draw.rect(screen, BLACK,
(segment[0] * GRID_SIZE + GRID_SIZE // 4 - eye_size // 2,
segment[1] * GRID_SIZE + GRID_SIZE // 3 - eye_size // 2,
eye_size, eye_size))
# 오른쪽 눈
pygame.draw.rect(screen, BLACK,
(segment[0] * GRID_SIZE + 3 * GRID_SIZE // 4 - eye_size // 2,
segment[1] * GRID_SIZE + GRID_SIZE // 3 - eye_size // 2,
eye_size, eye_size))
def draw_food(food_position):
"""먹이 그리기"""
# 사과 몸체
pygame.draw.rect(screen, RED,
(food_position[0] * GRID_SIZE, food_position[1] * GRID_SIZE,
GRID_SIZE, GRID_SIZE))
# 사과 꼭지
pygame.draw.rect(screen, DARK_GREEN,
(food_position[0] * GRID_SIZE + GRID_SIZE // 2 - 2,
food_position[1] * GRID_SIZE - 3,
4, 6))
def generate_food(snake_body):
"""새로운 먹이 위치 생성"""
while True:
x = random.randint(0, GRID_WIDTH - 1)
y = random.randint(0, GRID_HEIGHT - 1)
food_position = (x, y)
# 뱀의 몸과 겹치지 않는 위치 선택
if food_position not in snake_body:
return food_position
def game_over(score):
"""게임 오버 화면"""
game_over_text = big_font.render("GAME OVER", True, RED)
score_text = font.render(f"최종 점수: {score}", True, WHITE)
restart_text = font.render("다시 시작하려면 SPACE를 누르세요", True, WHITE)
exit_text = font.render("종료하려면 ESC를 누르세요", True, WHITE)
screen.blit(game_over_text,
(WIDTH // 2 - game_over_text.get_width() // 2,
HEIGHT // 3 - game_over_text.get_height() // 2))
screen.blit(score_text,
(WIDTH // 2 - score_text.get_width() // 2,
HEIGHT // 2 - score_text.get_height() // 2))
screen.blit(restart_text,
(WIDTH // 2 - restart_text.get_width() // 2,
HEIGHT // 2 + 40))
screen.blit(exit_text,
(WIDTH // 2 - exit_text.get_width() // 2,
HEIGHT // 2 + 80))
pygame.display.update()
waiting = True
while waiting:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
waiting = False
main()
elif event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
clock.tick(5)
def show_score(score):
"""점수 표시"""
score_text = font.render(f"점수: {score}", True, WHITE)
screen.blit(score_text, (10, 10))
def main():
"""게임 메인 함수"""
# 뱀 초기화
snake_body = [(GRID_WIDTH // 2, GRID_HEIGHT // 2)]
snake_direction = RIGHT
# 먹이 초기화
food_position = generate_food(snake_body)
# 점수 초기화
score = 0
# 뱀의 이전 방향 저장 (급격한 방향 전환 방지)
last_direction = snake_direction
# 게임 루프
running = True
while running:
# 이벤트 처리
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
# 방향키 입력 처리
if event.key == pygame.K_UP and last_direction != DOWN:
snake_direction = UP
elif event.key == pygame.K_DOWN and last_direction != UP:
snake_direction = DOWN
elif event.key == pygame.K_LEFT and last_direction != RIGHT:
snake_direction = LEFT
elif event.key == pygame.K_RIGHT and last_direction != LEFT:
snake_direction = RIGHT
# 뱀 머리 이동
head_x, head_y = snake_body[0]
dir_x, dir_y = snake_direction
new_head = (head_x + dir_x, head_y + dir_y)
# 벽에 부딪혔는지 확인
if (new_head[0] < 0 or new_head[0] >= GRID_WIDTH or
new_head[1] < 0 or new_head[1] >= GRID_HEIGHT):
game_over(score)
return
# 자기 몸에 부딪혔는지 확인
if new_head in snake_body:
game_over(score)
return
# 뱀 머리 추가
snake_body.insert(0, new_head)
# 먹이를 먹었는지 확인
if new_head == food_position:
# 먹이를 먹은 경우 새로운 먹이 생성 및 점수 증가
food_position = generate_food(snake_body)
score += 10
else:
# 먹이를 먹지 않은 경우 꼬리 제거
snake_body.pop()
# 마지막 방향 갱신
last_direction = snake_direction
# 화면 그리기
screen.fill(BLACK)
draw_grid()
draw_snake(snake_body)
draw_food(food_position)
show_score(score)
pygame.display.update()
clock.tick(SPEED)
def start_screen():
"""시작 화면"""
screen.fill(BLACK)
title_text = big_font.render("SNAKE GAME", True, GREEN)
start_text = font.render("시작하려면 SPACE를 누르세요", True, WHITE)
exit_text = font.render("종료하려면 ESC를 누르세요", True, WHITE)
screen.blit(title_text,
(WIDTH // 2 - title_text.get_width() // 2,
HEIGHT // 3 - title_text.get_height() // 2))
screen.blit(start_text,
(WIDTH // 2 - start_text.get_width() // 2,
HEIGHT // 2 + 20))
screen.blit(exit_text,
(WIDTH // 2 - exit_text.get_width() // 2,
HEIGHT // 2 + 60))
pygame.display.update()
waiting = True
while waiting:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
waiting = False
main()
elif event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
clock.tick(5)
# 게임 시작
if __name__ == "__main__":
start_screen()
게임 화면 📸
게임을 실행하면 다음과 같은 화면이 나타납니다:
- 시작 화면 
- 게임 진행 화면 
- 게임 오버 화면 
코드 설명 📝
1. 필요한 모듈 가져오기
import pygame
import random
import time
import sys
- pygame: 게임 개발을 위한 파이썬 라이브러리입니다.
- random: 랜덤한 위치에 먹이를 생성하기 위해 사용합니다.
- time: 시간 지연을 위해 사용합니다.
- sys: 프로그램 종료 등의 시스템 함수를 위해 사용합니다.
2. 게임 초기화 및 설정
# 초기화
pygame.init()
# 색상 정의
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
DARK_GREEN = (0, 200, 0)
# 게임 설정
WIDTH, HEIGHT = 600, 600
GRID_SIZE = 20
GRID_WIDTH = WIDTH // GRID_SIZE
GRID_HEIGHT = HEIGHT // GRID_SIZE
SPEED = 10
# 방향 설정
UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)
게임에서 사용할 색상, 화면 크기, 그리드 크기, 속도, 방향 등의 기본 설정을 정의합니다.
3. 그래픽 표현 함수
def draw_snake(snake_body):
"""뱀 그리기"""
for i, segment in enumerate(snake_body):
# 머리는 더 진한 녹색으로 표시
color = DARK_GREEN if i == 0 else GREEN
pygame.draw.rect(screen, color,
(segment[0] * GRID_SIZE, segment[1] * GRID_SIZE,
GRID_SIZE, GRID_SIZE))
# 눈 그리기 (머리일 경우)
if i == 0:
eye_size = GRID_SIZE // 5
# 왼쪽 눈
pygame.draw.rect(screen, BLACK,
(segment[0] * GRID_SIZE + GRID_SIZE // 4 - eye_size // 2,
segment[1] * GRID_SIZE + GRID_SIZE // 3 - eye_size // 2,
eye_size, eye_size))
# 오른쪽 눈
pygame.draw.rect(screen, BLACK,
(segment[0] * GRID_SIZE + 3 * GRID_SIZE // 4 - eye_size // 2,
segment[1] * GRID_SIZE + GRID_SIZE // 3 - eye_size // 2,
eye_size, eye_size))
def draw_food(food_position):
"""먹이 그리기"""
# 사과 몸체
pygame.draw.rect(screen, RED,
(food_position[0] * GRID_SIZE, food_position[1] * GRID_SIZE,
GRID_SIZE, GRID_SIZE))
# 사과 꼭지
pygame.draw.rect(screen, DARK_GREEN,
(food_position[0] * GRID_SIZE + GRID_SIZE // 2 - 2,
food_position[1] * GRID_SIZE - 3,
4, 6))
뱀과 먹이를 그리는 함수입니다. 눈과 사과 꼭지 같은 세부 사항을 추가하여 게임의 비주얼을 향상시켰습니다.
4. 게임 로직 함수
def generate_food(snake_body):
"""새로운 먹이 위치 생성"""
while True:
x = random.randint(0, GRID_WIDTH - 1)
y = random.randint(0, GRID_HEIGHT - 1)
food_position = (x, y)
# 뱀의 몸과 겹치지 않는 위치 선택
if food_position not in snake_body:
return food_position
뱀의 몸과 겹치지 않는 위치에 랜덤하게 먹이를 생성하는 함수입니다.
5. 게임 메인 함수
def main():
"""게임 메인 함수"""
# 뱀 초기화
snake_body = [(GRID_WIDTH // 2, GRID_HEIGHT // 2)]
snake_direction = RIGHT
# 먹이 초기화
food_position = generate_food(snake_body)
# 점수 초기화
score = 0
# 뱀의 이전 방향 저장 (급격한 방향 전환 방지)
last_direction = snake_direction
# 게임 루프
running = True
while running:
# 이벤트 처리
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
# 방향키 입력 처리
if event.key == pygame.K_UP and last_direction != DOWN:
snake_direction = UP
elif event.key == pygame.K_DOWN and last_direction != UP:
snake_direction = DOWN
elif event.key == pygame.K_LEFT and last_direction != RIGHT:
snake_direction = LEFT
elif event.key == pygame.K_RIGHT and last_direction != LEFT:
snake_direction = RIGHT
# 뱀 머리 이동
head_x, head_y = snake_body[0]
dir_x, dir_y = snake_direction
new_head = (head_x + dir_x, head_y + dir_y)
# 벽에 부딪혔는지 확인
if (new_head[0] < 0 or new_head[0] >= GRID_WIDTH or
new_head[1] < 0 or new_head[1] >= GRID_HEIGHT):
game_over(score)
return
# 자기 몸에 부딪혔는지 확인
if new_head in snake_body:
game_over(score)
return
# 뱀 머리 추가
snake_body.insert(0, new_head)
# 먹이를 먹었는지 확인
if new_head == food_position:
# 먹이를 먹은 경우 새로운 먹이 생성 및 점수 증가
food_position = generate_food(snake_body)
score += 10
else:
# 먹이를 먹지 않은 경우 꼬리 제거
snake_body.pop()
게임의 핵심 로직을 담당하는 메인 함수입니다. 키보드 입력 처리, 뱀의 이동, 충돌 검사, 점수 관리 등을 수행합니다.
게임 실행 방법 🚀
- 필요한 라이브러리 설치
pip install pygame
- 위의 코드를 복사하여 텍스트 에디터에 붙여넣기
- 파일을 snake_game.py와 같은 이름으로 저장
- 파이썬이 설치된 환경에서 터미널(명령 프롬프트)을 열고 파일을 저장한 디렉토리로 이동
- python snake_game.py 명령어를 입력하여 게임 실행
확장 가능성 🌱
이 스네이크 게임을 다음과 같은 방향으로 확장해볼 수 있습니다:
- 난이도 설정 추가
- 쉬움: 느린 속도, 벽 통과 가능
- 보통: 현재 구현된 수준
- 어려움: 빠른 속도, 장애물 추가
- 추가 기능
- 특수 먹이 추가 (점수 2배, 속도 변경 등)
- 장애물 추가
- 레벨 시스템 구현
- 시각적 개선
- 배경 이미지 추가
- 애니메이션 효과 추가
- 스프라이트 사용하여 더 나은 그래픽 구현
- 사운드 추가
- 배경 음악
- 효과음 (먹이 먹기, 충돌 등)
- 멀티플레이어 모드
- 로컬 2인 대전 모드 구현
- 네트워크 플레이 구현
게임에서 사용된 프로그래밍 개념 📚
이번 게임을 통해 다음과 같은 파이썬 개념들을 활용해보았습니다:
- Pygame 라이브러리 활용
- 그래픽 처리, 입력 처리, 게임 루프 관리
- 그리드 기반 게임 설계
- 2차원 그리드에서의 객체 이동 및 충돌 감지
- 이벤트 기반 프로그래밍
- 키보드 이벤트 처리를 통한 게임 제어
- 충돌 감지 알고리즘
- 뱀이 벽이나 자신의 몸과 충돌했는지 감지
- 상태 관리
- 게임의 다양한 상태(시작, 진행 중, 게임 오버) 관리
알고리즘 설명 🧮
스네이크 게임의 핵심 알고리즘들을 살펴보겠습니다:
뱀의 이동 알고리즘
- 현재 뱀의 머리 위치와 방향을 기반으로 새로운 머리 위치 계산
- 새로운 위치를 뱀의 몸 리스트 맨 앞에 추가
- 먹이를 먹지 않은 경우 꼬리(마지막 요소) 제거
- 결과적으로 뱀이 한 칸 이동한 효과 구현
충돌 감지 알고리즘
- 벽과의 충돌: 새로운 머리 위치가 그리드 범위를 벗어났는지 확인
- 자기 몸과의 충돌: 새로운 머리 위치가 현재 뱀의 몸 리스트에 이미 존재하는지 확인
먹이 생성 알고리즘
- 그리드 내의 무작위 위치 생성
- 생성된 위치가 뱀의 몸과 겹치는지 확인
- 겹치지 않는 경우 해당 위치 반환, 겹치는 경우 다시 시도
다음 포스팅 예고 🔮
다음 포스팅에서는 클래식 아케이드 게임인 "브레이크아웃(Breakout)" 게임을 만들어 볼 예정입니다. 물리 시뮬레이션과 충돌 처리를 중심으로 게임 개발에 대해 더 깊이 배워보겠습니다.
텍스트 기반 게임에서 그래픽 게임으로 발전해 나가는 여정, 계속 지켜봐 주세요! 궁금한 점이나 제안사항이 있으시면 댓글로 남겨주세요. 행복한 코딩 되세요! 🐍✨
'파이썬 기초문법 > 파이썬 게임 만들기' 카테고리의 다른 글
파이썬 게임 만들기 - 퐁(Pong) 게임 🏓🏓 (0) | 2025.03.09 |
---|---|
파이썬 게임 만들기 - 브레이크아웃 게임 🧱🏓 (1) | 2025.03.08 |
파이썬 게임 만들기 - 틱택토 게임 ⭕❌ (0) | 2025.03.08 |
파이썬 게임 만들기 - 행맨 게임 👨🎨 (2) | 2025.03.08 |
파이썬 게임 만들기 - 퀴즈 게임 🧠 (2) | 2025.03.08 |