유치한 게임

[파이썬 간단한 게임 만들기] 7. 벽돌 부수기

ai-creator 2021. 5. 8. 13:43
반응형

유치한 게임에 오신 것을 환영합니다.

이번에는 벽돌 부수기 게임을 만들어 보겠습니다.

아래와 같은 순서로 배워보겠습니다.

 

1. 목표

2. 사전 준비

3. 소스 코드 (전체)

4. 사전 지식

5. 구현 순서

6. 정리

 

 


1. 목표

이번 장에서는 벽돌 부수기 게임을 만들어 보도록 하겠습니다.

 

이번에 만들 게임은 벽돌 부수기 게임입니다. 벽돌 부수기 게임의 규칙은 다음과 같습니다.

벽돌 부수기는 화면의 벽을 반사하면서 이동하는 공으로 위쪽에 위치한 벽돌을 한 토막 씩 부수는 게임입니다. 이 때 공이 화면 아래쪽으로 떨어지지 않게 바(막대기)를 좌우로 조작하여 튕겨내야 합니다. 공의 개수를 늘리거나 공이 바(막대기)에 맞는 위치 따라 가속도를 주는 방법 등으로 난이도를 조절할 수 있습니다. 벽돌부수기는 화면의 벽을 반사하면서 이동하는 공으로 위쪽에 위치한 벽돌을 한 토막 씩 부수는 게임입니다. 이 때 공이 화면 아래쪽으로 떨어지지 않게 바(막대기)를 좌우로 조작하여 튕겨내야 합니다. 공의 개수를 늘리거나 공이 바(막대기)에 맞는 위치 따라 가속도를 주는 방법 등으로 난이도를 조절할 수 있습니다.

벽돌부수기 게임

이번 장에서는 이 벽돌 부수기 게임을 구현 해보도록 하겠습니다.

 

2. 사전 준비

- 게임판 구상

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import pygame # 1. pygame 선언
 
pygame.init() # 2. pygame 초기화
 
# 3. pygame에 사용되는 전역변수 선언
 
BLACK = (000)
RED = (25500)
GREEN = (02550)
BLUE = (00255)
WHITE = (255255255)
YELLOW = (2552550)
large_font = pygame.font.SysFont(None72)
small_font = pygame.font.SysFont(None36)
screen_width = 600
screen_height = 800
screen = pygame.display.set_mode((screen_width, screen_height)) 
 
clock= pygame.time.Clock()
 
# 4. pygame 무한루프
 
def runGame():
    global done
    while not done:
        clock.tick(10)
        screen.fill(WHITE)
 
runGame()
pygame.quit()
cs

 

게임을 진행할 화면을 다음과 같이 설정하도록 하겠습니다.

7~12번 라인 :  게임에 사용되는 색상 RGB 값

13,14번 라인(게임 폰트) : 게임 내부에서 점수를 표시할 폰트 설정

15,16번 라인(화면 크기) : 넓이(width) - 600, 높이(height) - 800/ 400x300

 

 

3. 소스 코드 (전체)

파일명 :bricksBreak.py

 

drive.google.com/drive/folders/1P7-ibtlJKUEet1lbqkSAg7N8RyxOza_o?usp=sharing

 

ai-creator 공유폴더(유치한게임) - Google Drive

이 폴더에 파일이 없습니다.이 폴더에 파일을 추가하려면 로그인하세요.

drive.google.com

 

4. 사전 지식

1) 벽돌 부수기 게임에 필요한 요소 정의

벽돌 부수기 게임은 이전에 학습한 핑퐁 게임과 비슷한 요소를 가지고 있습니다.

핑퐁 게임의 바가 존재하며 공도 동일하게 존재합니다. 이번에는 벽만을 튕기는 것이 아닌 게임판 상단에 있는 벽돌을 부수고 부수게 되면 마치 벽에 닿은 것과 같이 공이 튕겨야 하기 때문에 약간의 차이점이 존재합니다.

 

먼저 벽돌 부터 살펴보도록 하겠습니다.

벽돌도 pygame의 rect함수를 통해 생성합니다.

 

1
2
3
4
5
6
7
bricks = []
COLUMN_COUNT = 8
ROW_COUNT = 7
for column_index in range(COLUMN_COUNT):
    for row_index in range(ROW_COUNT):
        brick = pygame.Rect(column_index * (60 + 10+ 35, row_index * (16 + 5+ 356016)
        bricks.append(brick) 
cs

 

1번 라인에서 먼저 벽돌들을 담을 리스트를 선언합니다.

2,3번 라인은 벽돌의 총 갯수를 설정해줍니다. 총 7열 8행으로 56개의 벽돌을 생성하도록 합니다.

4~7번 라인은 이중 for문을 통해 56개의 벽돌을 각 열과 행에 맞게 리스트에 더해주도록 합니다.

각 벽돌은 Rect() 함수를 이용해서 생송하며, 각 사이즈와 간격을 조절해줍니다.

 

다음은 공입니다. 

공도 동일하게 Rect() 함수를 통해 생성해줍니다.

 

1
2
3
ball = pygame.Rect(screen_width // 2 - 16 // 2, screen_height // 2 - 16 // 21616)
ball_dx = 5
ball_dy = -5
cs

 

공을 생성 해주도록 하며, 초기 생성 위치는 화면의 정중앙에서 시작하도록 합니다.

또한 기본 이동 속도 dx, dy를 설정하여 공이 x,y좌표로 자유롭게 이동하도록 해줍니다.

 

다음은 패들입니다. 전장의 핑퐁 게임에서는 bar라고 표현했지만 이번 게임에서는 패들(paddle)이라고 표현하도록 하겠습니다.

 

1
2
paddle = pygame.Rect(screen_width // 2 - 80 // 2, screen_height - 168016)
paddle_dx = 0
cs

동일하게 패들을 Rect() 함수를 통해 생성하며, 패들은 좌우로만 이동하기 때문에 

2) 공의 흐름

이번에도 공을 사용하여 게임을 진행하고 동일하게 공은 게임 내부의 원리를 통해 진행 방향이 결정됩니다.

현실 세계에서 공은 물리적인 법칙인 중력과 마찰력에 의해 영향을 받으며 탁구 판에 부딛혀 이동하지만 게임판 내부에서는 조금 더 간단하게 벽이나 채(bar)에 맞았을 때, 입사각과 반사각의 원리가 적용되어 움직이게 됩니다. 

즉, 공이 어떠한 경계점에 도달했을 때, 해당하는 공이 날아온 위치에 기반하여 다음 이동 방향을 설정해주는 것이 필요합니다.

 

[그림 7-2] 공의 입사각과 반사각

 

따라서 공이 위, 아래 벽면 혹은 물체에 부딪힌다면 y축 이동방향을 뒤집어 주고, 좌,우 벽면에 부딪힌다면 x축 이동 뱡향을 뒤집어 마치 공이 튕기는 것처럼 보이도록 합니다.

 

 

 

5. 구현 순서

구현 순서는 다음과 같습니다.

Step1 키보드 이벤트 처리(패들 이동)
Step2 공의 이동 처리
Step3 벽돌 충돌 처리

 

Step1) 키보드 이벤트 처리(패들 이동)

 

먼저 키보드 이벤트 처리를 통해 패들을 이동시켜보도록 하겠습니다.이전 장까지 저희는 두가지 방식의 키보드 이동 처리를 학습하였습니다.1. 버튼을 한번 누를 때 마다 이동 처리2. 버튼을 누르고 있는 동안 지속적인 이동 처리

 

두 가지의 차이는 이동 처리가 상,하,좌,우 로 민감하게 처리되야 하는 게임에서는 첫 번째 방식을 적용하고, 빠른 이동 혹은 횡적, 종적 움직임이 지속적으로 많은 게임은 두 번째 방식을 적용한다는 점에 있습니다.

 

[그림 7-2] 게임에 따라 달라지는 이동 방식

따라서 이번에는 x축을 기준으로한 좌우 지속 이동이 필요함으로 두 번째 방식을 적용하여 구현하도록 하겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
while True
    clock.tick(30)
    screen.fill(BLACK) 
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            break
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                paddle_dx = -5
            elif event.key == pygame.K_RIGHT:
                paddle_dx = 5
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                paddle_dx = 0
            elif event.key == pygame.K_RIGHT:
                paddle_dx = 0   
 
    paddle.left += paddle_dx
cs

 

5번 라인은 이벤트를 처리하는 루프입니다.

8~12번 라인은 키보드의 좌,우 키가 눌러졌을 때, 패들의 이동 속도를 5,-5로 각각 설정하도록 합니다.

13~17번 라인은 눌러졌던 키보드가 다시 떨어졌을 때, 패들의 이동 속도를 0으로 설정하여 키보드가 떨어졌을 때는 더 이상 움직이지 않도록 합니다.

19번 라인은 해당 이동속도를 패들의 위치 값인 left에 적용하여 이후 패들을 화면에 출력할 때, 이동 거리를 움직이도록 합니다.

 

 

Step2) 공의 이동 처리

 

이번에는 앞서 작성한 코드를 기반으로 공의 이동 처리를 해보도록 하겠습니다.

먼저 공이 좌, 우 벽에 닿았을 때와 패들이 아닌 바닥에 닿았을 때 처리를 해보도록 하겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
def runGame():
    score = 0
    missed = 0
    SUCCESS = 1
    FAILURE = 2
    game_over = 0
 
    ## 벽돌 생성 생략
 
    while True
        
        ## 키보드 이벤트 처리 생략
 
        ball.left += ball_dx
        ball.top  += ball_dy
 
        if ball.left <= 0:
            ball.left = 0
            ball_dx = -ball_dx
        elif ball.left >= screen_width - ball.width: 
            ball.left = screen_width - ball.width
            ball_dx = -ball_dx
        if ball.top < 0:
            ball.top = 0
            ball_dy = -ball_dy
        elif ball.top >= screen_height:
            missed += 1
            ball.left = screen_width // 2 - ball.width // 2
            ball.top = screen_height // 2 - ball.width // 2
            ball_dy = -ball_dy 
 
        if missed >= 3:
            game_over = FAILURE 
 
        if paddle.left < 0:
            paddle.left = 0
        elif paddle.left > screen_width - paddle.width:
            paddle.left = screen_width - paddle.width
cs

 

먼저 17번 라인은 공이 좌측 벽면에 닿았을 경우 입니다. 공이 벽면에 닿게 되면 위치값을 다시 0으로 설정해주며 x의 이동 방향을 뒤집어 횡적으로 반대로 이동하도록 합니다.

20번 라인은 공이 오른쪽 벽면에 닿았을 경우 입니다.  공이 벽면에 닿게 되면 위치값을 스크린 넓이에서 공의 폭을 뺀 거리에 설정해주며 이동 방향을 뒤집어 횡적으로 반대로 이동하도록 합니다.23번 라인은 공이 천장에 닿았을 경우입니다. 동일하게 공의 위치를 조정하고 이동 방향을 종적으로 뒤집어 줍니다.26번 라인은 공이 패들이 아닌 바닥에 닿았을 경우이며 이는 공을 놓치게 된 상황입니다. 따라서 missed 값을 추가하주며 공을 다시 최초의 시작지점으로 리셋 하도록 합니다.

32번 라인은 공이 놓치게된 missed 상황이 3번 이상 발생할 경우이며, 이때, game_over 플래그를 수정하여 이후 화면 출력부분에서 game_over 메세지를 출력하도록 준비합니다.

35~38번 라인은 앞서 이동한 패들이 좌우 벽면을 벗어나지 않게 설정하도록 하는 코드입니다.

 

Step3) 벽돌 충돌 처리

 

이번에는 공과 벽돌이 충돌했을 때의 처리를 해보겠습니다.

앞서 벽면과 공의 충돌의 차이점은 여러 벽돌을 감지 해야하며, 벽돌과 충돌 했을 때 해당 벽돌을 삭제해야 한다는 점입니다.

따라서 해당 코드를 작성해보도록 하겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
for brick in bricks:
    if ball.colliderect(brick):
        bricks.remove(brick)
        ball_dy = -ball_dy
        score += 1
        break
 
if ball.colliderect(paddle):
    ball_dy = -ball_dy
 
if ball.centerx <= paddle.left or ball.centerx > paddle.right:
    ball_dx = ball_dx * -1
 
if len(bricks) == 0:
    print('success')
    game_over = SUCCESS
 
#화면 그리기
 
for brick in bricks:
    pygame.draw.rect(screen, GREEN, brick)
 
if game_over == 0:
    pygame.draw.circle(screen, WHITE, (ball.centerx, ball.centery), ball.width // 2)
 
pygame.draw.rect(screen, BLUE, paddle)
 
score_image = small_font.render('Point {}'.format(score), True, YELLOW)
screen.blit(score_image, (1010))
 
missed_image = small_font.render('Missed {}'.format(missed), True, YELLOW)
screen.blit(missed_image, missed_image.get_rect(right=screen_width - 10, top=10))
 
if game_over > 0:
    if game_over == SUCCESS:
        success_image = large_font.render('성공'True, RED)
        screen.blit(success_image, success_image.get_rect(centerx=screen_width // 2, centery=screen_height // 2))
    elif game_over == FAILURE:
        failure_image = large_font.render('실패'True, RED)
        screen.blit(failure_image, failure_image.get_rect(centerx=screen_width // 2, centery=screen_height // 2))
 
pygame.display.update()
cs

 

1~6번 라인은 먼저 공과 벽돌의 충돌을 감지하는 라인입니다.

벽돌이 담인 bricks 리스트를 돌면서 colliderect() 함수를 활용하여 충돌을 감지하고 만약 충돌이 감지 됐을 때는 해당 벽돌을 리스트에서 삭제하고 공의 이동방향을 변경하도록 합니다.

break 포인트를 지정하는 이유는 이미 하나의 벽돌이 충돌이 감지되면 당연히 다른 벽돌은 충돌 감지를 할 필요가 없기 때문입니다.

8,9번 라인은 공이 벽면이 아닌 패들에 충돌했을 때 이동 방향을 뒤집어 주도록하는 코드입니다.

11,12번 라인은 공이 패들의 좌,우측 벽면에 닿았을 경우를 감지하며, 이때는 y 이동 방향을 뒤집는 것이 아닌 x 이동 방향을 뒤집어 주도록 합니다.

14~16번 라인은 남은 벽돌의 수가 0이 되었을 때, 게임 종료를 의미하며 game_over 플래그를 변경하여 플레이어의 승리를 출력하도록 설정해주는 코드입니다.

 

다음은 게임판에 요소드를 출력하는 코드 입니다.

20,21번 라인에서 bricks 리스트들의 모든 벽돌들을 출력하도록 합니다.

23,24번 라인에서는 game_over 플래그가 게임 종료 상황이 아니면(0일때) 공을 화면에 출력해주도록 합니다.

26번 라인에서는 패들을 출력하도록 합니다.

28~32번 라인은 게임판 상단에 스코어와 missed를 출력하도록 하는데 이는 게임 진행동안 설정된 값을 통해 출력됩니다.

34~40번 라인은 앞서 설정한 game_over 플래그가 종료 상황으로 설정 됐을 경우 게임 종료 메세지를 출력하도록 합니다. 만약 플레이어의 승리상황(벽돌을 모두 제거)이면 게임 성공을 출력하며, 반대 상황(missed가 3회 이상)일 경우 게임 실패 메세지를 출력하도록 합니다. 메세지는 앞서 설정한 large_font를 이용해 출력 해줍니다.

 

 

[그림 7-3] 완성 게임 화면

 

 

6. 정리

이번 장에서는 앞서 배운 pygame의 기본 구조와 게임 루프, 이벤트 처리를 기반으로 벽돌 부수기 게임을 구현 해봤습니다.

 

먼저, 게임에 필요한 요소 정의를 하고 공의 흐름 이해를 통해 게임을 진행할 수 있도록 사전학습을 한 뒤 진행을 했으며,

Step 1) 키보드 이벤트 처리(패들 이동)->  게임 상황에 맞는 키보드 처리 선택

Step 2) 공의 이동 처리 -> screen과 공의 x, y좌표를 기반으로 한 접촉 처리 및 이동 방향 전환(입사각, 반사각)

Step 3) 벽돌 충돌 처리 ->  리스트를 이용한 충돌 처리 및 플래그를 활용한 게임 종료 처리

를 배웠습니다.

 

이번 장에는 pygame과 키보드 left, right 이동 처리(상황별) 및 위치별 접촉 처리를 배웠습니다. 해당 게임에서는 앞서 배운 내용을 기반으로  공의 진행 속도 증가하며 각도를 변경하는 등 재미를 더할 요소가 많으니 자유롭게 응용해보면 좋을 듯 합니다^^

 

ㅁ 참고

토닥토닥 파이썬

 

 


도움이 되셨다면, 좋아요 / 구독 버튼 눌러주세요~

 

저작물의 저작권은 작성자에게 있습니다.
공유는 자유롭게 하시되 댓글 남겨주세요~
상업적 용도로는 무단 사용을 금지합니다.
끝까지 읽어주셔서 감사합니다^^

 

반응형