6.3 공의 반사와 블록격파 구현하기

이번 절에서는 이제 공의 반사를 구현할 차례이다. 공 반사의 경우 수는 좌우벽, 상단벽, 공을 반사판을 받아낸 순간, 마지막으로 블록결파된 순간으로 블록은 부서지면서 동시에 반사가 일어난다.

TITLE = 'Breakout'
WIDTH = 800
HEIGHT = 600

GAP_FROM_SCREEN = 50
ball = Actor('ball', (WIDTH / 2, HEIGHT / 2))
bar = Actor('bar', (WIDTH / 2, HEIGHT - GAP_FROM_SCREEN))

# 4행*8열짜리 블록더미 만들기
blocks = []
for block_row in range(4):
    for block_col in range(8):
        block = Actor(
            'block', 
            (block_col * 100, block_row * 32 + GAP_FROM_SCREEN),
            anchor=('left', 'top')
        )
        blocks.append(block)

# 볼의 초기 (방향이 있는)속도값
vx = 5
vy = -5


def draw():
    screen.blit('space', (0, 0))
    ball.draw()
    bar.draw()
    for block in blocks:
        block.draw()
        
def update():
    global vx, vy
    
    # 반사판의 이동을 화면 안에 안에 가두기
    if bar.left < 0:
        bar.left = 0
    if bar.right > WIDTH:
        bar.right = WIDTH
    
    # 각각의 x와 y 방향으로 거리 vx와 vy만큼 공을 이동시키기
    ball.move_ip(vx, vy)

    # 공이 외쪽 또는 오른쪽 벽에 부딪힐 때, x의 방향을 반대로하기
    if ball.left < 0 or ball.right > WIDTH:
        vx = -vx
        sounds.wall.play()
        
    # 공이 위쪽벽 또는 반사판에 부딪힐 때, y의 방향을 반대로하기
    if ball.top < 0 or ball.colliderect(bar) == True:
        vy = -vy
        sounds.wall.play()
        
    # 공이 블록과 부딪 때
    b_index = ball.collidelist(blocks) 
    if b_index != -1:
        vy = -vy
        sounds.block.play()
        blocks.pop(b_index)
        
    # 게임종료
    if ball.bottom > HEIGHT:
        sounds.die.play()
        game.exit()
        
    if not blocks:
        sounds.win.play()
        vx = 0
        vy = 0

def on_mouse_move(pos):
    x, y = pos
    bar.centerx = x

🔢 먼저, 총 4군데, 외쪽(벽)/오른쪽(벽)/위쪽(벽)/반사판(bar)에 의한 반사에 대한 구현을 하겠다. 유사성 있는 2군데씩 짝을 지어 코딩하면 코드량이 줄어든다. 먼저 45라인의 코드에 의해 공이 좌/우벽에 닿았는지 if 조건문으로 지속적으로 검사해 반사를 시키면 되는데 반사시키는 코드는 어렵지 않은게 46번 라인 vx = -vx 의 의미는 속도값에서의 x축 방향(direction)만 반대방향으로 바꾼 것으로, 반대방향으로 바꾸는 건 +(양수)를 -(음수)로, -(음수)를 +(양수)로 값의 부호만 바꾸면 되는 것이므로 본래값에 반대부호화 시키는 의미로 -vx 한 값을 자신에게 재대입시켰다.

이번 게임부터는 처음으로 게임 내의 어떤 상황(공의 반사순간, 블록격파 순간, 게임의 종료)에 대해 효과음을 추가하므로써 게임성을 높여 사용자에게 게임의 흥미를 더하도록 만들자. 절차는 간단한데 먼저 기존에 게임에 사용할 이미지들을 images 라는 정해진 폴더 안에 넣어둔 것처럼, 게임에 사용할 미리 준비된 효과음들을 sounds라는 정해진 이름을 갖는 폴더 안에 저장해 놓으면 된다. 저장하기 쉬운 방법은 뮤 에디터에서 효과음(Sounds)이라는 아이콘을 눌러 해당 폴더를 열고(만약 기존에 폴더가 없을 경우 자동생성됨), 그 안에 넣어놓으면 된다. 참고로 이 게임에 사용되는 효과음은 이곳에서 다운로드 할 수 있다. 또는 당신이 커스텀 뮤 에디터를 사용 중이라면, 뮤 에디터 설치 시 해당 리소스들도 함께 설치되었을 것으므로, (계정명)\mu_code\examples\pygame_zero 폴더 안에서 찾을 수 있다.

효과음을 재생시키는 방법은 간단한데 47번 라인에서 보는 바와 같이 sounds.재생할 효과음파일이름.play() 라는 문법을 통해 재생시킬 수 있다. 상세한 사용방법에 대한 내용은 역시나 이곳 파이게임제로 라이브러리 문서에서 확인할 수 있다.

남은 것은 공이 상부벽에 낳은 경우 또는 우리가 조작하는 반사판에 닿은 경우로 이 경우도 둘이 함께 조건문으로 묶어 코딩할 수 있는데 이유는 속도값에서 y축 방향만 반대방향으로 바꾸면 공을 반사시킬 수 있기 때문이다. 공이 게임화면 위쪽에 닿았는지를 확인(ball.y < 0)에 대한 것은 어려운 것이 없고, 공이 반사판에 닿았는지(충돌했는지)의 여부는 지난 5.5절에서 배운 Actor 객체소속 메소드인 colliderect 함수를 사용하면 되겠다. 이 경우에 공의 입장에서 반사판과 충돌했는지 여부확인용으로 ball.colliderect(bar)로 코딩하던, 반대로 반사판 입장에서 공과 충돌했는지 여부의 확인으로 bar.colliderect(ball) 로 코딩하던 어느쪽이든 상관은 없다.

🔢 이제 공이 블록과 충돌한 상황으로 넘어가자. 충돌을 검사해야 하는데 여러분이 떠오르는 아이디어는 어떤 것인가? 아마도 이런 아이디어가 있을 것이다. for 문을 사용해 블록더미 리스트에서 블록하나씩 꺼내서 그중 어떤 블록과 닿았는가(충돌했는가)를 colliderect 함수로 일일이 확인하면 될 것 같다. 코드로 표현하면 다음과 같을 것이다.

for block in blocks:
    if ball.colliderect(block):

그렇다 좋은 아이디어이고 잘 생각해냈다. 생각한데로 코딩을 해도 잘 동작할 것이다. 그런데, 저 두 줄짜리 코딩에 대해 파이게임에서 이미 한 개의 함수로 만들어 놓은게 있어서, 55번 라인처럼 collidelist 함수를 사용하면 한번에 해결하는 방법이 있다. 이 함수는 파라미터로 List를 입력으로 받아서 그 리스트 안 어떤 아이템에 충돌이 발견되면, 그 충돌된 것은 아이템 리스트 안에 몇번째 위치하는지의 인덱스값을 리턴값으로 알려준다. 만약에 리스트 안에 아이템들 중에 어떠한 충돌도 발견하지 못했다면, -1 의 음수값을 리턴해준다.(참고로 인덱스값은 결코 음수를 가질 수 없기 때문에 의도적으로 그 값을 사용함.) 따라서, 그것을 기반으로 56라인 조건문에서 인덱스값이 -1 인 아닌 경우에만(서로 값이 같지 않은 경우라는 의미의 연산자 != 를 사용), 즉 충돌이 발생했을 때만 조건문 안으로 진입하여 공을 반사시키기위해 속도의 방향을 반전시키고, 충돌효과 소리를 내며, 마지막으로 블록더미 리스트(blocks) 객체의 메소드인 pop 함수를 사용하여 리스트에서 그 충돌한 블록을 없에고 있다.

🔢 마지막은 공의 반사는 아니고, 게임의 종료상황인데 먼저 사용자가 공을 받아내야 하는 미션을 수행하지 못해서 공이 그대로 게임화면 바닥으로 추락해버린 경우, 즉 미션실패로 게임종료된 상황에 대한 간단한 처리를 추가하겠다. 62-64라인처럼 게임종료 상황이니 그에 맞는 효과음 내고, game.exit 메소드를 호출하여 앱을 종료한다. 반대로 미션을 완전히 클리어 해서 블록더미를 완전히 격파한 경우도 승리로서의 게임종료상황 처리가 필요하다. 66-69라인의 코딩처럼 게임승리에 대한 효과음을 냄과 동시에 공을 그자리에서 멈추게 된다.

여기까지 블록격파게임의 핵심은 다 구현했다. 위에 코드를 가지고 게임을 즐겨보면 아마 미흡한 부분이나 특정(?) 버그를 하나 발견할 것이다. 다음 절에 그 부분을 다 해결해서 우리게임의 마무리를 짓도록 하자.

Last updated