개발/Python

[Python] 파이썬으로 고전 게임 피하기 게임 만들기 - Dodge Game

일요일좋아하는사람 2025. 4. 22. 19:40
728x90
반응형

고전게임 시리즈

1. 테트리스

https://ilikesunday.tistory.com/31

 

[Python] 파이썬으로 고전 게임 '테트리스' 만들기 – 직접 만들어보며 배운 시행착오의 기록

들어가며예전부터 한 번쯤은 만들어보고 싶었던 게임이 있다면 단연코 테트리스였다. 간단한 구조지만 의외로 중독성이 있고, 적당한 난이도 조절과 함께 구현할 수 있다면 파이썬의 GUI 및 로

ilikesunday.tistory.com

 

2. 스네이크게임

https://ilikesunday.tistory.com/32

 

[Python] 파이썬으로 고전 게임 스네이크 게임 만들기 - 삽질의 연속 ㅠㅠ

들어가며처음에는 파이썬으로 테트리스를 먼저 만들었었다. 다른 고전 게임에도 흥미가 생기기도 하고 시리즈별로 만들고 싶어서 스네이크 게임을 만들고자 했지만... 테트리스 때와 달리 이번

ilikesunday.tistory.com

 

3. 벽돌깨기

https://ilikesunday.tistory.com/33

 

[Python] 파이썬으로 고전 게임 벽돌깨기 만들기

고전게임 시리즈1. 테트리스https://ilikesunday.tistory.com/31 [Python] 파이썬으로 고전 게임 '테트리스' 만들기 – 직접 만들어보며 배운 시행착오의 기록들어가며예전부터 한 번쯤은 만들어보고 싶었던

ilikesunday.tistory.com

 

4. 슈팅게임

https://ilikesunday.tistory.com/34

 

[Python] 파이썬으로 고전 게임 슈팅 게임 만들기: 총알, 적, 그리고 리듬감

고전게임 시리즈1. 테트리스https://ilikesunday.tistory.com/31 [Python] 파이썬으로 고전 게임 '테트리스' 만들기 – 직접 만들어보며 배운 시행착오의 기록들어가며예전부터 한 번쯤은 만들어보고 싶었던

ilikesunday.tistory.com

 

단순한 피하기 게임의 매력

이전까지 우리가 만들어온 게임들은 조작, 공격, 충돌, 점수 같은 요소들이 골고루 들어간 비교적 '화려한' 게임들이었다. 하지만 이번에는 모든 것을 내려놓고 정말 단순한 하나의 목적, 즉 피하는 것만 잘하면 되는 게임을 만들어보려 한다. 우리가 만들 게임은 다음과 같다: 화면 위에서 장애물(예: 운석, 블록, 드론 등)이 떨어지고, 플레이어는 좌우 방향키를 통해 그것들을 피해야 한다. 어떤 공격도 없고, 적도 없다. 그저 살아남는 것이 유일한 목표다. 하지만 이 단순한 구조 속에 시간의 흐름에 따른 속도 증가, 점점 빨라지는 리듬, 화면을 메우는 장애물들까지 들어가면, 생각보다 극도의 집중과 긴장을 요구하는 훌륭한 게임이 된다.

내가 이 게임을 기획하면서 가장 기대한 것은 피지컬한 조작감실패에 대한 즉각적인 피드백이었다. 공격을 하거나 무언가를 파괴하지 않고도 재미를 느낄 수 있을까? 화면에 단순한 도형만 있어도 게임이 될까? 결론부터 말하자면, 아주 훌륭하게 된다. 오히려 이 단순한 구조 덕분에 구현도 빠르고, 테스트도 편하고, 무엇보다 누구나 '한 판만 더!'를 외칠 수 있는 미묘한 중독성이 생긴다. 이번 글에서는 그런 피하기 게임을 처음부터 직접 구현하면서 마주친 작은 문제들, 시행착오, 그리고 코드 한 줄 한 줄이 어떤 역할을 하는지를 낱낱이 정리해보려 한다.

구현을 시작하며 – 뼈대를 만들자

게임을 만들기 위해 첫 번째로 생각한 건 역시 기본적인 화면 구성과 플레이어의 존재였다. 가장 먼저 pygame을 불러오고, 기본적인 창을 열고, while 루프로 게임을 반복하게 만든다. 아래는 그 시작 코드다.

import pygame
import random

pygame.init()

WIDTH, HEIGHT = 500, 700
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Dodge Game")
clock = pygame.time.Clock()

이 코드를 쓰는 데까지는 아무 문제도 없었다. 하지만 이후에 '플레이어 캐릭터를 어떻게 그릴 것인가'에서 잠깐 멈칫했다. 사각형으로 그릴까? 이미지로 불러올까? 초반엔 구현 속도를 위해 단순 사각형을 사용했다. 플레이어는 아래쪽에 위치하고, 좌우로만 움직이기 때문에 위치 좌표만 잘 정하면 된다.

player_width, player_height = 50, 50
player_x = WIDTH // 2 - player_width // 2
player_y = HEIGHT - player_height - 30
player_speed = 5

이 정도만으로도 충분히 '움직이는 사각형'을 만들 수 있었다. 이걸 매 루프마다 그리기 위해 draw.rect()를 사용했다.

pygame.draw.rect(screen, (0, 200, 255), (player_x, player_y, player_width, player_height))

다음 단계에서는 키 입력을 통해 캐릭터를 실제로 움직일 수 있게 만들고, 화면 위에서 장애물이 떨어지도록 구현해보자. 이때 생긴 문제들과 해결 과정도 함께 살펴보겠다.

3. 키 입력으로 캐릭터를 움직이기

플레이어가 가만히 있는 건 더 이상 게임이 아니다. 좌우 방향키로 플레이어가 자유롭게 이동하도록 해보자. 가장 먼저 떠오르는 건 pygame.KEYDOWN 이벤트였다. 하지만 이 방식은 키를 '누른 순간'만 인식되기 때문에, 키를 꾹 누르고 있어도 캐릭터가 계속 움직이지는 않는다. 그래서 pygame.key.get_pressed()라는 메서드를 사용해야 했다. 이 메서드는 누르고 있는 상태를 지속적으로 감지할 수 있어 훨씬 부드럽게 이동할 수 있게 해준다.

keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and player_x > 0:
    player_x -= player_speed
if keys[pygame.K_RIGHT] and player_x < WIDTH - player_width:
    player_x += player_speed

단순히 이 조건만 넣었더니, 생각보다 이동이 부자연스러웠다. 처음엔 player_x < WIDTH까지만 조건을 걸었는데, 그럼 캐릭터 절반이 화면 밖으로 나가는 현상이 생겼다. 그래서 player_width를 고려한 조건으로 수정하자 부드럽게 화면 내에서만 움직이게 되었다. 이런 사소한 좌표 계산이 플레이 감각에 큰 영향을 준다는 걸 깨달았다.

4. 장애물 만들기 – 떨어지는 공포

이제 본격적으로 위에서 떨어지는 장애물을 구현해보자. 장애물은 랜덤한 위치에서 일정 시간 간격으로 나타나고, 일정한 속도로 아래로 떨어진다. 장애물이 하나만 있으면 너무 단순하니까, 여러 개를 리스트로 관리하기로 했다. 처음엔 장애물 하나만 만들어서 테스트했는데, 위치를 고정해두니까 반복이 너무 뻔했다. 그래서 random.randint()로 x 좌표를 랜덤하게 설정하고, y는 항상 0에서 시작해 아래로 떨어지도록 했다.

obstacles = []
obstacle_width, obstacle_height = 50, 50
obstacle_speed = 5

# 장애물 생성
if len(obstacles) < 5:
    x = random.randint(0, WIDTH - obstacle_width)
    rect = pygame.Rect(x, 0, obstacle_width, obstacle_height)
    obstacles.append(rect)

이제 매 루프마다 장애물들을 아래로 이동시키면 된다. 동시에 화면 밖으로 벗어난 장애물은 제거해줘야 계속 리스트가 쌓이지 않는다.

for obstacle in obstacles[:]:
    obstacle.y += obstacle_speed
    if obstacle.y > HEIGHT:
        obstacles.remove(obstacle)

여기서도 리스트를 직접 순회하면서 remove()하면 예외가 발생하니, 반드시 복사본 obstacles[:]을 써야 한다는 걸 다시 한 번 배웠다.

5. 충돌 처리 – 피했는가, 부딪혔는가

이제 장애물이 떨어지고, 플레이어가 움직인다. 다음으로 해야 할 일은 이 둘이 '만났는지'를 확인하는 것이다. 충돌 판정을 위해 pygame.Rect.colliderect()를 사용하면 정말 간단하게 해결할 수 있다. 플레이어를 Rect 객체로 만들고, 각 장애물도 Rect로 되어 있으니 둘이 겹치는지만 보면 된다.

player_rect = pygame.Rect(player_x, player_y, player_width, player_height)
for obstacle in obstacles:
    if player_rect.colliderect(obstacle):
        game_over = True

여기서 game_over 변수를 만들어 True로 바꾸고 루프를 종료하는 방식으로 처리했다. 처음에는 충돌 판정을 너무 늦게 넣어서, 부딪히고도 0.1초쯤 더 살아있는 이상한 현상이 발생했다. 그래서 충돌 감지를 draw()보다 앞에 두고, 충돌 즉시 화면을 멈추도록 했다. 또한 부딪힌 순간을 시각적으로 보여주고 싶어서 'Game Over' 텍스트를 띄우고 1초간 대기 후 종료되도록 만들었다.

6. 점수 시스템 – 살아남은 만큼 보상받기

점수가 없으면 성취감도 없다. 이번에는 시간 기준으로 점수를 올려보기로 했다. 플레이어가 살아있는 프레임 수를 기반으로 점수를 부여하면, 오래 살아남을수록 높은 점수가 나오게 된다. score += 1을 루프 내에 두고, 매 프레임마다 갱신했다. 그리고 pygame.font를 사용해 화면 상단에 점수를 출력했다.

score = 0
font = pygame.font.SysFont(None, 36)

# 게임 루프 안에서
score += 1
text = font.render(f"Score: {score}", True, (255, 255, 255))
screen.blit(text, (10, 10))

점수 표시가 깜빡이거나 흐려질 경우, 텍스트를 출력하기 전에 반드시 배경을 다시 fill()해야 한다. 그리고 점수가 5자리 수로 커질 수 있으니, 폰트 크기와 위치를 충분히 고려해두는 것이 중요하다. 나중에는 최고 점수를 파일로 저장하거나, 게임 오버 후 보여주는 기능도 추가해보고 싶었다.

7. 전체 코드

마지막으로 지금까지 작성한 내용을 모두 합쳐 하나의 완성된 코드로 정리해보자.

import pygame
import random

pygame.init()

WIDTH, HEIGHT = 500, 700
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Dodge Game")
clock = pygame.time.Clock()

player_width, player_height = 50, 50
player_x = WIDTH // 2 - player_width // 2
player_y = HEIGHT - player_height - 30
player_speed = 5

obstacle_width, obstacle_height = 50, 50
obstacle_speed = 5
obstacles = []

score = 0
font = pygame.font.SysFont(None, 36)

game_over = False
running = True

while running:
    clock.tick(60)
    screen.fill((0, 0, 0))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT] and player_x > 0:
        player_x -= player_speed
    if keys[pygame.K_RIGHT] and player_x < WIDTH - player_width:
        player_x += player_speed

    if len(obstacles) < 10:
        x = random.randint(0, WIDTH - obstacle_width)
        rect = pygame.Rect(x, 0, obstacle_width, obstacle_height)
        obstacles.append(rect)

    for obstacle in obstacles[:]:
        obstacle.y += obstacle_speed
        if obstacle.y > HEIGHT:
            obstacles.remove(obstacle)

    player_rect = pygame.Rect(player_x, player_y, player_width, player_height)
    for obstacle in obstacles:
        if player_rect.colliderect(obstacle):
            game_over = True

    for obstacle in obstacles:
        pygame.draw.rect(screen, (255, 50, 50), obstacle)
    pygame.draw.rect(screen, (0, 200, 255), player_rect)

    if not game_over:
        score += 1
    else:
        game_over_text = font.render("GAME OVER", True, (255, 0, 0))
        screen.blit(game_over_text, (WIDTH // 2 - 100, HEIGHT // 2))

    text = font.render(f"Score: {score}", True, (255, 255, 255))
    screen.blit(text, (10, 10))

    pygame.display.flip()

    if game_over:
        pygame.time.delay(2000)
        running = False

pygame.quit()

마무리하며

이 피하기 게임은 굉장히 단순한 구조지만, 짧은 시간 안에 조작 감각, 리듬감, 반응 속도까지 체험할 수 있는 꽤 강력한 게임이 되었다. 구현이 복잡하지 않아서 입문자에게도 적합하고, 직접 만들어보면 pygame의 기본기를 자연스럽게 익힐 수 있다. 특히 충돌 처리, 리스트 순회, 키 입력, 점수 시스템까지 핵심 요소들이 모두 들어 있어 훗날 더 복잡한 게임으로 확장할 수 있는 토대가 되기도 한다. 여러분도 이 코드를 기반으로 장애물의 종류를 늘려보거나, 배경을 움직이게 하거나, 심지어 온라인 순위표까지 붙여보면 어떨까? 게임은 단순할수록 확장성이 넓고, 그만큼 창의력을 자극한다.


티스토리용 태그

#파이썬 #pygame #피하기게임 #게임만들기 #코딩입문 #파이썬예제 #게임프로그래밍 #python #파이게임 #dodgegame

728x90
반응형