7.3 충돌처리 및 기타정보(점수 및 게임종료) 표기

이제 남은 두 개의 함수의 구현체를 만들도록 하겠다. 제일 먼저, 충돌검사(chek_collision) 함수에 대한 부분이다. 여기서 말하는 충돌이란, 주인공이 쏜 총알에 적 비행기가 맞았을 때, 또는 반대로 적 비행기의 총알에 주인공이 맞았을 때, 마지막으로 적 비행기와 주인공이 직접 충돌했을 때 이렇게 총 3가지가 있을 수 있겠다.

각각의 충돌에 대한 코드를 살펴보자. 적의 총알은 단 1개가 아니므로 총알객체의 모음인 리스트 형태로 존재하고, 따라서, 리스트 안에 하나하나의 총알들과 개별 충돌검사를 for 구문을 통해 하게되고, 충돌이 발견될 경우, 총알 자체도 없에고(8번 라인에서 메모리에서 해제), 적 비행기도 없에면(15번 라인) 되는데 단순하게 그냥 사라지면 흥미가 떨어지니 게임성을 위해 폭발장면을 애니메이션화 하였다. 참고로 적 비행기를 없에기 위한 방법으로 del 명령어 문법이 새롭게 등장했는데, 이는 객체를 제거(메모리에서 해제)하는 한 방법 중에 하나이다. 우리는 enemies 리스트에 enemy 객체들이 담겨있다는 것을 알고, enemy_index를 통해 그 안에 지우고자 하는 객체의 위치를 특정해 지우는 것이다. 그밖에 리스트 안에 특정 값을 제거하는 방법으로 pop remove 내장메소드를 사용해봤었는데, 이 메소드들을 사용해서 지울 수 있지만, 편리한 새로운 문법을 배우는 기회로 삼으면 되겠다.

폭발 장면 2장의 이미지를 객체화한 후 에니매이션 처리를 하였는데 주의깊게 봐야할 부분은 폭발장면 애니메이션을 시간지연을 주면서 충분히 보여주는 처리가 필요하였다(13번, 21~23번 라인). 이 부분이 왜 필요한지는 이 부분의 코드를 제거하고 실행해보면 금방 이해가 되는데, 이전 절에서 update 콜백함수의 빠른 호출에 의한 동일한 사유로 애니메이션을 충분히 보여주기도 전에 폭발객체 자체를 삭제해버리는 것을 방지하기 위한 목적이다.

그밖에 score와 game_over의 두 글로벌 변수에 대해서는 적 비행기를 제거해 점수가 증가되는 상황(16번 라인)과 주인공이 죽어 게임이 종료되는 상황(27번 라인)에 대해 변수값 변경을 적용하고 있다.

def check_collision():
    global score, game_over

    # 적이 총알에 맞았을 때
    for bullet in bullets:
        enemy_index = bullet.collidelist(enemies)
        if enemy_index != -1:
            bullets.remove(bullet)
            explosion = Actor("explosion1")
            explosion.pos = enemies[enemy_index].pos
            explosion.images = ["explosion1", "explosion2"]
            explosion.fps = 8
            explosion.duration = 15
            explosions.append(explosion)
            del enemies[enemy_index]
            score += 1

    # 적 비행기 폭발 애니메이션
    for explosion in explosions:
        explosion.animate()
        explosion.duration -= 1
        if explosion.duration == 0:
            explosions.remove(explosion)
    
    # 적 총알에 주인공이 맞거나, 적과 직접 충돌했을 때
    if player.collidelist(enemy_bullets) != -1 or player.collidelist(enemies) != -1:
        game_over = True

이제 마지막으로 남은 화면에 정보표시용 draw_text 함수를 구현하는 것으로 모든 사용자 함수의 구현은 끝이나게 된다. draw_text 함수는 간단한데, 아래와 같이 점수와 게임종료 상황에 대해 단순 화면 표시이다.

def draw_text():
    screen.draw.text("Score " + str(score), (50, 0), color="black", fontsize=30)
    if game_over:
        screen.draw.text(
            "Game over", midbottom=(WIDTH / 2, HEIGHT / 2), color="blue", fontsize=100
        )

정말 마지막으로 처리해야 할 것이 하나 남았는데 그것은 게임이 종료되었음에도 불구하고 계속 게임을 할 수 있는 상황을 막아놓는 것이다. 크게 어려운 것은 아니고, 아래와 같이 update 콜백함수에서 게임종료가 아닐 때만, 주인공의 조작이 가능하게 조건문을 넣으면 되는 것이다(8번 라인).

def update():
    for background in backgrounds:
        background.y += 3
        if background.top > HEIGHT:
            background.y = (HEIGHT / 2) - HEIGHT

    create_enemies()
    if game_over == False:
        move_player()
        shoot_bullets()
    check_collision()

이것으로 이번 트윈비 게임의 모든 구현이 완료되었다. 마지막으로 전체 소스코드를 첨부하는 것으로 이 절을 마무리 하겠다.

from pgzhelper import *
import random

WIDTH = 800
HEIGHT = 600

backgrounds = []
background1 = Actor("background1", (WIDTH / 2, HEIGHT / 2))
backgrounds.append(background1)
background2 = Actor("background2", (WIDTH / 2, (HEIGHT / 2) - HEIGHT))
backgrounds.append(background2)
player = Actor("player", (400, 500))

MAX_BULLETS = 3
bullets = []

enemies = []
enemy_bullets = []
explosions = []

score = 0
game_over = False

music.play('main_theme')


def move_player():
    if keyboard.right:
        player.x += 5
    if keyboard.left:
        player.x -= 5
    if keyboard.up:
        player.y -= 5
    if keyboard.down:
        player.y += 5
    if player.right > WIDTH:
        player.right = WIDTH
    if player.left < 0:
        player.left = 0
    if player.bottom > HEIGHT:
        player.bottom = HEIGHT
    if player.top < 0:
        player.top = 0


def shoot_bullets():
    if keyboard.space and len(bullets) < MAX_BULLETS:
        sounds.sfx_sounds_interaction25.play()
        bullet_delay = 5
        bullet = Actor("player_bullet")
        bullet.pos = player.pos
        bullet.angle = 90
        bullets.append(bullet)

    for bullet in bullets:
        bullet.move_forward(15)
        if bullet.y < 0:
            bullets.remove(bullet)


def create_enemies():
    if random.randint(0, 1000) > 980:
        enemy = Actor("enemy1_1")
        enemy.images = ["enemy1_1", "enemy1_2"]
        enemy.fps = 5
        enemy.y = -50
        enemy.x = random.randint(100, WIDTH - 100)
        enemy.direction = random.randint(-100, -80)
        enemies.append(enemy)

    for enemy in enemies:
        enemy.move_in_direction(4)
        enemy.animate()
        if enemy.top > HEIGHT:
            enemies.remove(enemy)
        if random.randint(0, 1000) > 990:
            bullet = Actor("enemy_bullet")
            bullet.pos = enemy.pos
            bullet.angle = random.randint(0, 359)
            enemy_bullets.append(bullet)

    for bullet in enemy_bullets:
        bullet.move_forward(5)
        if bullet.x < 0 or bullet.x > WIDTH or bullet.y < 0 or bullet.y > HEIGHT:
            enemy_bullets.remove(bullet)



def check_collision():
    global score, game_over

    for bullet in bullets:
        enemy_index = bullet.collidelist(enemies)
        if enemy_index != -1:
            bullets.remove(bullet)
            explosion = Actor("explosion1")
            explosion.pos = enemies[enemy_index].pos
            explosion.images = ["explosion1", "explosion2"]
            explosion.fps = 8
            explosion.duration = 15
            explosions.append(explosion)
            del enemies[enemy_index]
            score += 1

    for explosion in explosions:
        explosion.animate()
        explosion.duration -= 1
        if explosion.duration == 0:
            explosions.remove(explosion)

    if player.collidelist(enemy_bullets) != -1 or player.collidelist(enemies) != -1:
        game_over = True


def draw_text():
    screen.draw.text("Score " + str(score), (50, 0), color="black", fontsize=30)
    if game_over:
        screen.draw.text(
            "Game over", midbottom=(WIDTH / 2, HEIGHT / 2), color="blue", fontsize=100
        )


def draw():
    for background in backgrounds:
        background.draw()
    player.draw()
    for enemy in enemies:
        enemy.draw()
    for bullet in bullets:
        bullet.draw()
    for explosion in explosions:
        explosion.draw()
    for bullet in enemy_bullets:
        bullet.draw()
    draw_text()


def update():
    for background in backgrounds:
        background.y += 3
        if background.top > HEIGHT:
            background.y = (HEIGHT / 2) - HEIGHT

    create_enemies()
    if game_over == False:
        move_player()
        shoot_bullets()
    check_collision()

Last updated