5.5 플래피버드와 파이프의 충돌 구현하기

이번 절까지 배우면 플래피버드 게임의 핵심기능 구현까지는 완료된다. 얼마 남지않은 코드를 힘내서 함께 구현해보자. 이번 절에는 제목처럼 플래피버드가 파이프와 충돌하는 상황으로, 이 경우는 게임이 종료되는 상황이다. 다만, 부딪히는 그 즉시로 종료되면 게임에 흥미가 떨어지기 때문에, 닿는 순간 플래피버드가 죽은상태의 모습으로 그대로 땅 아래 방향으로 추락하는 에니메이션을 추가하고, 결국 추락하는 플래피버드가 게임화면 하단을 넘어서는 순간 그제서야 첨부터 게임을 다시 시작하게되면 더 자연스러울 것이다. 이 요구사항을 실제 코드로 구현하면 다음 아래의 코드와 같다.

TITLE = 'Flappy Bird'
WIDTH = 400
HEIGHT = 708

GRAVITY = 0.3
drop_speed = 0
GAP = 140
PIPE_SPEED = -3
bird_alive = True

# Actor 객체들
flappy_bird = Actor('bird1', (75, 350))
top_pipe = Actor('top', (350,0))
bottom_pipe = Actor('bottom', (350, top_pipe.height + GAP))

def draw():
    screen.blit('background', (0, 0))
    flappy_bird.draw()
    top_pipe.draw()
    bottom_pipe.draw()

def update():
    global drop_speed, bird_alive
    drop_speed += GRAVITY
    flappy_bird.y += drop_speed
    top_pipe.x += PIPE_SPEED
    bottom_pipe.x += PIPE_SPEED
    
    # 파이프의 무한순환
    if top_pipe.right < 0 or bottom_pipe.right < 0: 
        top_pipe.x = WIDTH
        bottom_pipe.x = WIDTH
        
    # 파이프와의 충돌
    if flappy_bird.colliderect(top_pipe) or flappy_bird.colliderect(bottom_pipe): 
        flappy_bird.image = "birddead"
        bird_alive = False

def on_mouse_down():
    global drop_speed
    if bird_alive == True:
        drop_speed = -6.5

화면의 애니메이션이 일어나는 update 함수 안에서 35번 라인에서 플래피버드가 위 파이프 또는 아래 파이프 둘 중 하나와(이 목적으로 비교문 안에 or 를 사용했음을 주목하자!) 충돌했는지를 지속적으로 체크한 후, 만약 충돌했으면 플래피버드의 이미지를 죽은 모양의 이미지(birddead)로 바꾸고, 추가적으로 bird_alive 라는 플래피버드의 생존유무를 나타내는 global 변수(함수 밖에서 선언)에 값을 죽음을 의미하는 False로 바꾼다.

bird_alive 변수는 왜 필요하게 되었을까? 그것은 41번 라인에 추가된 조건문에서 알 수 있는데, 이미 죽은 상태의 플래피버드를 마치 살아있는 것처럼 하늘로 날아오르는 공중부양을 가능하게 해서는 안되기 때문에 현재 플래피버드가 살아 있는 상태인지 죽어있는 상태인지의 분간이 필요했다.

여기까지만의 코드로 플래피버드는 계획했던데로 파이프에 닿자마자 (외형의 모습까지도)죽은상태가 되서 추락을 시작하게 된다. 이제 남은 건 화면 바닥까지 추락했을 때, 게임을 재시작 해야 하는게 남아있다. 그런데 게임을 재시작하는 상황은 비단 파이프와의 충돌만이 있는게 아닐 수 있다. 왜냐하면 사용자가 의도적으로 플래피버드를 가만히 놔둠으로 중력에 의한 자동추락으로 게임화면 하단을 넘어서게 만들거나, 또는 마우스 버튼을 빠른 시간 안에 계속 클릭함으로써 공중부양으 심하게(?) 시켜 게임화면 상단 위쪽을 벗어나게도 만들 수 있다. 이 경우도 게임을 재시작하게 만들면 좋을 것이다. 결론적으로 이 때는 즉 플래피버드가 살아있던 죽어있던지에 상관없이 화면의 최상단이나 최하단을 넘어서는 상황에 대한 처리를 추가하면 되는 것이고, 이는 사실상 파이프의 무한순환을 구현한 코딩과 크게 다를 바 없다. 이를 코드로 구현하면 다음 아래의 코드와 같다.

TITLE = 'Flappy Bird'
WIDTH = 400
HEIGHT = 708

GRAVITY = 0.3
drop_speed = 0
GAP = 140
PIPE_SPEED = -3
bird_alive = True

# Actor 객체들
flappy_bird = Actor('bird1', (75, 350))
top_pipe = Actor('top', (350,0))
bottom_pipe = Actor('bottom', (350, top_pipe.height + GAP))

def draw():
    screen.blit('background', (0, 0))
    flappy_bird.draw()
    top_pipe.draw()
    bottom_pipe.draw()

def update():
    global drop_speed, bird_alive
    drop_speed += GRAVITY
    flappy_bird.y += drop_speed
    top_pipe.x += PIPE_SPEED
    bottom_pipe.x += PIPE_SPEED
    
    # 파이프의 무한순환
    if top_pipe.right < 0 or bottom_pipe.right < 0: 
        top_pipe.x = WIDTH
        bottom_pipe.x = WIDTH
        
    # 파이프와의 충돌
    if flappy_bird.colliderect(top_pipe) or flappy_bird.colliderect(bottom_pipe):
        flappy_bird.image = "birddead"
        bird_alive = False
        
    # 게임의 재시작
    if flappy_bird.y > HEIGHT or flappy_bird.y < 0: 
        flappy_bird.image = "bird1"
        bird_alive = True
        flappy_bird.center = (75, 350)
        drop_speed = 0
        top_pipe.x = WIDTH
        bottom_pipe.x = WIDTH

def on_mouse_down():
    global drop_speed
    if bird_alive == True:
        drop_speed = -6.5

이로써 플래피버드의 핵심구현 기능구현은 다 끝났다. 추가적으로 게임에 흥미를 더하기 위한 몇 가지 추가 기능요구사항(파이프 위치 램덤화하기, 파이프 무사통과시 점수부여, 게임음악 추가 등등)이 있을 수 있으나, 이처럼 단지, 50라인짜리 코딩으로도 게임하나를 만들 수 있다는 것이 파이게임제로의 가장 큰 장점이라 할 수 있겠다. 추가기능 구현에 대해서는 따로 별도의 챕터에서 기술하도록 하겠다.

지금까지 플래피버드 게임만들기 통해, 파이게임제로에 성공적으로 입문하게 된 여러분들을 다시한번 축하하고, 앞으로 이어질 몇 개의 챕터를 통해 이러한 크고작은 몇 개의 게임제작 프로젝트들을 진행해 보면서, 파이썬 언어를 통한 텍스트코딩의 참 재미를 발견할 수 있길 기대해본다.

Last updated