본문 바로가기

Python/Python 초등 교육

10. 장애물 넘기 게임 - 3부

반응형

이제 장애물을 게임화면에 배치하고 배치된 장애물이 이동되게 만들어 보아요.

장애물 이미지를 찾아서 앞에 공룡이미지를 넣었던 디렉토리에 넣어 두겠습니다.

 

이런 모양이 될거에요. (여러분이 원하는 디렉토리로 하셔도 됩니다. 단! .PracticeGame1 처럼 게임프로젝트 내에 존재해야해요. 이유는 나중에 다시 설명할게요)

 

준비된 이미지를 load 합니다. 

image_tree = pygame.image.load("images/tree.png")
#화면의 제일 우측에 장애물을 위치하게 해준다.
tree_height = image_tree.get_size()[1]
tree_x = MAX_WIDTH - 30
tree_y = MAX_HEIGHT - tree_height

각 라인(줄) 별로 설명을 해볼게요.

첫번째 줄을 공룡이미지 resource를 읽어서 파이썬 게임 객체를 만들었던것 기억하죠? 똑같아요. 

이렇게 이미지 resource를 읽어서 파이썬 게임 객체를 만듭니다.

 

장애물 나무의 높이값을 구해서 장애물이 게임화면에 최초로 위치해야 하는 좌표 (가로, 세로 위치) 를 구하는 부분이에요.

이제 앞에서 공룡이미지를 화면에 나오게했던것처럼 

screen.blit()

를 사용해서 화면에 나타나게 해보아요.

전체 코드는 아래와 같습니다.

import sys

import pygame

pygame.init()
pygame.display.set_caption("데브라쿤 파이썬게임 연습1")
MAX_WIDTH = 800
MAX_HEIGHT = 400

def main():
    # 게임 화면의 사이즈 설정
    screen = pygame.display.set_mode((MAX_WIDTH, MAX_HEIGHT))

    # #공룡 이미지 로드
    image_dino1 = pygame.image.load("images/dino1.png")
    dino_height = image_dino1.get_size()[1]
    # 공룡이 바닥에 붙어 있는 모양을 만들고 싶음
    dino_bottom = MAX_HEIGHT - dino_height

    # 공룡 좌표
    dino_x = 50
    dino_y = dino_bottom

    image_tree = pygame.image.load("images/tree.png")
    # 화면의 제일 우측에 장애물을 위치하게 해준다.
    tree_height = image_tree.get_size()[1]
    tree_x = MAX_WIDTH - 30
    tree_y = MAX_HEIGHT - tree_height

    while True:
        screen.fill((255, 255, 255))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        #공룡이미지를 화면에 나오게 하는 부분 , dino_x , dino_y 좌표에 나오게됨
        screen.blit(image_dino1, (dino_x, dino_y))
        #장애물나무의 위치를 설정해서 화면에 나오게 함.
        screen.blit(image_tree, (tree_x, tree_y))
        pygame.display.update()

if __name__ == '__main__':
    main()

이렇게 코딩을 하고 실행을 해보겠습니다. 실행을 하는 방법을 알고 있으시죠? 

여려분이 만든 파일을 실행해 보아요.

python ./practice-game3.py

저는 파일이름을 pracitce_game3.py라는 이름으로 만들었어요. 여러분은 각자의 파일이름을 입력하면됩니다. 

 

아 이렇게 우리가 추가한 장애물나무가 나타나게 됩니다. 

이제 장애물을 움직여 봅시다. 

 

움직인다는 말을 좀 잘못된 표현이에요. 움직이게 보이게 만든다가 정확한 표현입니다. 

장애물을 움직이게 보이게 하려면 장애물이 조금씩 위치가 왼쪽으로 변하면 마치 움직이는것처럼 보여질거에요.

 

이것을 우리는 코딩으로 표현해야합니다. 이렇게 현실의 있는 현상이나 기능들을 어떻게 하면 자연스럽고 효율적으로 나타낼까를 고민하는것이 프로그래머들이 하는 일이랍니다.

 

장애물을 왼쪽으로 움직이게 하려면 맨처음 장애물의 X 좌표 ( 가로 위치 , 좌표는 정보는 앞에 공룡이미지 설명할때 간단히 설명했죠? )  

가 왼쪽으로 조금씩 바뀌게 되면 마치 장애물이 왼쪽으로 움직이게 되는 모양처럼 보이게 됩니다. 

 

이제 우리가 코등했던 while 문의 대해 자세히 알아봐야 하는 시간이 왔어요. 

while 조건문 : 은 while 옆에 조건이 true이면 안에 내용을 반복하게 되는것입니다. 

아래 우리가 작성했던 코드를 보면서 설명해 보아요.

while True:
    screen.fill((255, 255, 255))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    #공룡이미지를 화면에 나오게 하는 부분 , dino_x , dino_y 좌표에 나오게됨
    screen.blit(image_dino1, (dino_x, dino_y))
    #장애물나무의 위치를 설정해서 화면에 나오게 함.
    screen.blit(image_tree, (tree_x, tree_y))
    pygame.display.update()
  •  while true 
    • 무한 반복이에요 . 보통 while 문앞에서 특정 조건을 체크하고 해당 조건일 충족되면 while을 종료시키는 코드가 들어가게 되요.

이렇게 무한 반복되고 있는 while문은 우리가 게임창의 오른쪽 상탄의 X버튼을 클릭하게 되면 pygame.quit() 되게 되고 게임 프로그램 자체가 종료되게 코딩 되어 있어요. 

 

그리고 아래 공룡이미지와 장애물이미지를 화면에 그리고 , 그려진 이미지들을 화면에 보여지게 pygame.display.update() 하는 부분이 있습니다.

 

그럼 얼마나 자주 이 부분이 반복될까요? 1초에 1번? 10번? 정확히 알수는 없어요 . 컴퓨터가 얼마나 빠르냐 느리냐 하는 컴퓨터 성능에 차이도 있습니다 . 여러분이 게임을 하다가보면 스마트폰 또는 pc 마다 화면이 뚝뚝끊기는 모습을 보거나 반대로 엄청 부드럽게 잘 되는경우가 있죠? 이런 경우가 바로 위에 while문이 얼마나 빠르게 반복하냐에 따라 화면에 그려지고 보여지는 코드가 많이 실행되는지 결정되기 때문이에요. 부드럽고 버벅거리지 않다고 느끼면 while 문의 코드가 많이 실행되었다는 뜻입니다 .

 

사실 while문의 안에 코드는 특별한 처리를 하지 않으면 성능이 느린 컴퓨터에서 아예 게임을 하지 못할 정도로 게임 느려지게 되고 

성능이 빠른 컴퓨터라고 하더라도 불필요하게 과도하게 실행되어 컴퓨터의 성능 저하 시키는 원인이 되기도합니다. 

 

프로그래머는 뭐하는 사람이라구요? 현실의 현상이나 기능을 효율적으로 개발하는 방법을 고민하는 사람입니다. 

그럼 효율적이라면 어떻게 해야할까요? 

 

우리눈은 보통 1초에 화면이 60번 깜빡이거나 또는 120번 정도 깜박이면 충분히 부드럽다고 느끼게 되고 특별한 경우가 아닌 이상 더이상의 부드럽은 사실 불필요한 경우가 많아요. 

 

이말은 while 반복문안에 pygame.display.update()가 1총에 60번 또는 120번 만 실행 되어도 충분하다는것이죠.

 

와 ! 1초 60번이나 실행이 되는거에요? 라고 하지만 우리 컴퓨터는 여러분이 생각하는것 보다 휠씬 많은 일을 할 수 있기 때문에 

아무린 처리를 하지 않으면 휠~~ 씬 더 많이 실행이 되게 됩니다.( 컴퓨터가 힘들어서 멈출때 까지요) 

 

그럼 어떻게 1초에 60번 실행을 시켜요? 라고 생각이 들죠? 

그건 바로.

# 게임 화면의 프레임 설정 ( 1초의 몇번을 깜빡일것인가)
fps = pygame.time.Clock()

이부분을 추가해 주면 됩니다.  pygame에서 제공하는 fps 즉 frame per second ( 초당 프레임수 ) 를 조절할수 있는 기능을 제공해줍니다.

 

어떻게 사용하지 볼까요? 우리가 만든 코드에 fps를 추가해 볼께요.

import sys
import pygame
from datetime import datetime

pygame.init()
pygame.display.set_caption("데브라쿤 파이썬게임 연습1")
MAX_WIDTH = 800
MAX_HEIGHT = 400

def main():
    # 게임 화면의 사이즈 설정
    screen = pygame.display.set_mode((MAX_WIDTH, MAX_HEIGHT))

    # #공룡 이미지 로드
    image_dino1 = pygame.image.load("images/dino1.png")
    dino_height = image_dino1.get_size()[1]
    # 공룡이 바닥에 붙어 있는 모양을 만들고 싶음
    dino_bottom = MAX_HEIGHT - dino_height

    # 공룡 좌표
    dino_x = 50
    dino_y = dino_bottom

    image_tree = pygame.image.load("images/tree.png")
    # 화면의 제일 우측에 장애물을 위치하게 해준다.
    tree_height = image_tree.get_size()[1]
    tree_x = MAX_WIDTH - 30
    tree_y = MAX_HEIGHT - tree_height

    # 게임 화면의 프레임 설정 ( 1초의 몇번을 깜빡일것인가)
    fps = pygame.time.Clock()
    check_time = 0
    while True:
        tick = fps.tick(60)
        check_time = check_time+1
        print("time : ", datetime.now(), "초당 frame 수 :", check_time)
        screen.fill((255, 255, 255))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        #공룡이미지를 화면에 나오게 하는 부분 , dino_x , dino_y 좌표에 나오게됨
        screen.blit(image_dino1, (dino_x, dino_y))
        #장애물나무의 위치를 설정해서 화면에 나오게 함.
        screen.blit(image_tree, (tree_x, tree_y))
        pygame.display.update()

if __name__ == '__main__':
    main()
time :  2022-10-31 19:37:05.742294 초당 frame 수 : 1
time :  2022-10-31 19:37:05.824693 초당 frame 수 : 2
time :  2022-10-31 19:37:05.842034 초당 frame 수 : 3
time :  2022-10-31 19:37:05.857539 초당 frame 수 : 4
time :  2022-10-31 19:37:05.874430 초당 frame 수 : 5
time :  2022-10-31 19:37:05.891896 초당 frame 수 : 6
time :  2022-10-31 19:37:05.908307 초당 frame 수 : 7
time :  2022-10-31 19:37:05.924774 초당 frame 수 : 8
time :  2022-10-31 19:37:05.941098 초당 frame 수 : 9
time :  2022-10-31 19:37:05.957590 초당 frame 수 : 10
time :  2022-10-31 19:37:05.975006 초당 frame 수 : 11
time :  2022-10-31 19:37:05.991142 초당 frame 수 : 12
time :  2022-10-31 19:37:06.008837 초당 frame 수 : 13
time :  2022-10-31 19:37:06.025886 초당 frame 수 : 14
time :  2022-10-31 19:37:06.042291 초당 frame 수 : 15
time :  2022-10-31 19:37:06.059214 초당 frame 수 : 16
time :  2022-10-31 19:37:06.076161 초당 frame 수 : 17
time :  2022-10-31 19:37:06.092863 초당 frame 수 : 18
time :  2022-10-31 19:37:06.108155 초당 frame 수 : 19
time :  2022-10-31 19:37:06.124494 초당 frame 수 : 20
time :  2022-10-31 19:37:06.140787 초당 frame 수 : 21
time :  2022-10-31 19:37:06.157500 초당 frame 수 : 22
time :  2022-10-31 19:37:06.173921 초당 frame 수 : 23
time :  2022-10-31 19:37:06.190783 초당 frame 수 : 24
time :  2022-10-31 19:37:06.207088 초당 frame 수 : 25
time :  2022-10-31 19:37:06.223551 초당 frame 수 : 26
time :  2022-10-31 19:37:06.241016 초당 frame 수 : 27
time :  2022-10-31 19:37:06.257087 초당 frame 수 : 28
time :  2022-10-31 19:37:06.273590 초당 frame 수 : 29
time :  2022-10-31 19:37:06.290936 초당 frame 수 : 30
time :  2022-10-31 19:37:06.307928 초당 frame 수 : 31
time :  2022-10-31 19:37:06.323416 초당 frame 수 : 32
time :  2022-10-31 19:37:06.340318 초당 frame 수 : 33
time :  2022-10-31 19:37:06.357105 초당 frame 수 : 34
time :  2022-10-31 19:37:06.374279 초당 frame 수 : 35
time :  2022-10-31 19:37:06.391662 초당 frame 수 : 36
time :  2022-10-31 19:37:06.408415 초당 frame 수 : 37
time :  2022-10-31 19:37:06.424994 초당 frame 수 : 38
time :  2022-10-31 19:37:06.441459 초당 frame 수 : 39
time :  2022-10-31 19:37:06.458068 초당 frame 수 : 40
time :  2022-10-31 19:37:06.475115 초당 frame 수 : 41
time :  2022-10-31 19:37:06.491623 초당 frame 수 : 42
time :  2022-10-31 19:37:06.507156 초당 frame 수 : 43
time :  2022-10-31 19:37:06.523350 초당 frame 수 : 44
time :  2022-10-31 19:37:06.539123 초당 frame 수 : 45
time :  2022-10-31 19:37:06.555212 초당 frame 수 : 46
time :  2022-10-31 19:37:06.571303 초당 frame 수 : 47
time :  2022-10-31 19:37:06.587356 초당 frame 수 : 48
time :  2022-10-31 19:37:06.603584 초당 frame 수 : 49
time :  2022-10-31 19:37:06.620297 초당 frame 수 : 50
time :  2022-10-31 19:37:06.636935 초당 frame 수 : 51
time :  2022-10-31 19:37:06.652810 초당 frame 수 : 52
time :  2022-10-31 19:37:06.668656 초당 frame 수 : 53
time :  2022-10-31 19:37:06.685272 초당 frame 수 : 54
time :  2022-10-31 19:37:06.702643 초당 frame 수 : 55
time :  2022-10-31 19:37:06.719732 초당 frame 수 : 56
time :  2022-10-31 19:37:06.735971 초당 frame 수 : 57
time :  2022-10-31 19:37:06.751555 초당 frame 수 : 58
time :  2022-10-31 19:37:06.768212 초당 frame 수 : 59
time :  2022-10-31 19:37:06.785005 초당 frame 수 : 60

fps 코드를 추가하고 실제로 1초에 60번이 실행되는지 체크해 보았습니다. 1초에 60번 실행 되었을때 30번 실행 되었을때 또는 120번 실행되었을때 어떤 변화가 생기는지는 아래 실제 장애물이 움직이는 코드를 넣은 다음에 같이 테스트 해 보아요.

 

자 이제 fps의 개념도 생각해 봤어요. 아래는 fps가 없을때 장애물이 위치를 바꿔 보는 코드를 만들어 봤습니다.

import sys
import pygame
from datetime import datetime

pygame.init()
pygame.display.set_caption("데브라쿤 파이썬게임 연습1")
MAX_WIDTH = 800
MAX_HEIGHT = 400

def main():
    # 게임 화면의 사이즈 설정
    screen = pygame.display.set_mode((MAX_WIDTH, MAX_HEIGHT))

    # #공룡 이미지 로드
    image_dino1 = pygame.image.load("images/dino1.png")
    dino_height = image_dino1.get_size()[1]
    # 공룡이 바닥에 붙어 있는 모양을 만들고 싶음
    dino_bottom = MAX_HEIGHT - dino_height

    # 공룡 좌표
    dino_x = 50
    dino_y = dino_bottom

    image_tree = pygame.image.load("images/tree.png")
    # 화면의 제일 우측에 장애물을 위치하게 해준다.
    tree_height = image_tree.get_size()[1]
    tree_x = MAX_WIDTH - 30
    tree_y = MAX_HEIGHT - tree_height

    tree_move_by_tick = 5
    # 게임 화면의 프레임 설정 ( 1초의 몇번을 깜빡일것인가)
    # fps = pygame.time.Clock()
    # check_time = 0
    while True:
        # tick = fps.tick(120)
        # check_time = check_time+1
        # print("time : ", datetime.now(), "초당 frame 수 :", check_time)
        screen.fill((255, 255, 255))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        #공룡이미지를 화면에 나오게 하는 부분 , dino_x , dino_y 좌표에 나오게됨
        screen.blit(image_dino1, (dino_x, dino_y))

        #장애물의 x 위치값을 화면이 한번 깜빡일때 마다 tree_move_by_tick만큼 왼쪽으로 이동
        #왼쪽으로 이동은 x값이 작아지게 만들어야 하니까 원래 장애물의 위치에서 계속 minus를 해준다.
        tree_x = tree_x - tree_move_by_tick
        #장애물나무의 위치를 설정해서 화면에 나오게 함.
        screen.blit(image_tree, (tree_x, tree_y))
        pygame.display.update()

if __name__ == '__main__':
    main()

 

원래 장애물의 위치에서 왼쪽으로 이동하는 값 을 tree_move_by_tick이라는 변수로 설정해 주었습니다.

사실 tree_move_by_tick 의 값은 속도를 나타 냅니다. 시간에 이동가능한 거리 = 속도를 말하는것이죠.

즉 장애물의 현재 속도를 설정한것입니다.

 

이렇게 설정하고 게임 실행하면 장애물이 왼쪽으로 움직이는것이 보여요. 잘움직이죠? 

이제 프레임 설정하는 부분을 넣어서 실행해 보겠습니다. 저는 30으로 설정해 볼게요.

import sys
import pygame
from datetime import datetime

pygame.init()
pygame.display.set_caption("데브라쿤 파이썬게임 연습1")
MAX_WIDTH = 800
MAX_HEIGHT = 400

def main():
    # 게임 화면의 사이즈 설정
    screen = pygame.display.set_mode((MAX_WIDTH, MAX_HEIGHT))

    # #공룡 이미지 로드
    image_dino1 = pygame.image.load("images/dino1.png")
    dino_height = image_dino1.get_size()[1]
    # 공룡이 바닥에 붙어 있는 모양을 만들고 싶음
    dino_bottom = MAX_HEIGHT - dino_height

    # 공룡 좌표
    dino_x = 50
    dino_y = dino_bottom

    image_tree = pygame.image.load("images/tree.png")
    # 화면의 제일 우측에 장애물을 위치하게 해준다.
    tree_height = image_tree.get_size()[1]
    tree_x = MAX_WIDTH - 30
    tree_y = MAX_HEIGHT - tree_height

    tree_move_by_tick = 5
    # 게임 화면의 프레임 설정 ( 1초의 몇번을 깜빡일것인가)
    fps = pygame.time.Clock()
    check_time = 0
    while True:
        tick = fps.tick(30)
        check_time = check_time+1
        print("time : ", datetime.now(), "초당 frame 수 :", check_time)
        screen.fill((255, 255, 255))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        #공룡이미지를 화면에 나오게 하는 부분 , dino_x , dino_y 좌표에 나오게됨
        screen.blit(image_dino1, (dino_x, dino_y))

        #장애물의 x 위치값을 화면이 한번 깜빡일때 마다 tree_move_by_tick만큼 왼쪽으로 이동
        #왼쪽으로 이동은 x값이 작아지게 만들어야 하니까 원래 장애물의 위치에서 계속 minus를 해준다.
        tree_x = tree_x - tree_move_by_tick
        #장애물나무의 위치를 설정해서 화면에 나오게 함.
        screen.blit(image_tree, (tree_x, tree_y))
        pygame.display.update()

if __name__ == '__main__':
    main()

 

처음 보다 느려진것을 확인 할 수 있습니다.  이것은 while문의 코드가 1초에 30번 실행 되었다는 뜻이니까 1초에 60번 실행되는 거 보다 2배 느리게 움직이겠네요. 

 

그런데 이상합니다 프레임의 차이가 있다고 우리가 하는 게임들이 느려지거나 빨라 지면 안되겠죠?

게임을 하는 사람들마다 컴퓨터 사양이 달르고 그마다 1초당 실행할수 있는 프레임수가 전부 다를수 밖에 없어요. 

 

어떤 사람은 빠르게 움직이게 되고 어떤 사람은 느리게 움직이게 되면 느리게 움직이는 사람은 화가 나죠!

프레임은 우리가 얼마나 화면은 부드럽게 볼수 있냐의 차이만 주어져야 합니다. 그런데 이 프레임이 우리가 만든 코드에서는 

게임의 속도에 영향을 줘버렸네요. 그럼 이것을 해결 하기 위해 움직이는 게임 resource들에 대해 속도의 개념을 넣어 주어야합니다. 

 

이 게임 캐릭터의 속도에 대해서는 다음 시간에 또 알아 보아요~