본문 바로가기

❤25기/25기 세미나

[25기 세미나] Pygame 모듈로 눈송이게임 만들기

 

    Pygame은 파이썬에서 작성할 수 있는 게임과 같은 멀티미디어 표현을 위한 라이브러리입니다. 오픈 소스이자 무료이니, 파이썬을 이용할 수 있는 플랫폼이라면 언제 어디서든 실행할 수 있습니다. 이번 세미나에서는 pygame으로 간단한 아래의 이미지와 같은 슈팅 게임을 만들어 보도록 하겠습니다. 우리는 눈송이가 되어서 교수님이 던지는 C학점을 피할 거에요!

 

 

① pygame 설치 하기


① pygame 설치 하기

     먼저pycharm을 실행해 프로젝트를 생성한 후, pygame모듈을 사용하기 위한 준비를 하겠습니다.

이전에 사용했던 방법과 마찬가지로, File – Settings에 들어가신 후, project interpreter를 찾아 눌러주세요.

그 뒤,오른쪽에 있는+버튼을 눌러pygame을 검색하고, install하시면 됩니다.

 

이와 같은 과정을 마쳤다면 이제 우리는 import 명령어를 통해 pygame을 불러와 사용할 수 있게 되었습니다! 다음 장에서는 화면에 게임판을 생성해 보도록 하겠습니다.

 

①-2 idle로 모듈 설치하기


파이참을 사용하여 pygame 모듈 설치에 성공하셨다면 해당 글은 넘어가셔도 됩니다!

만약 파이참으로 pygame 모듈을 설치하지 않고, idle을 이용하여 라이브러리를 설치하여 이용하고 싶다면 해당 글을 읽어주세요.


우선은 하단의 링크에 접속하여, 자신의 운영체제에 맞게 다운로드 받으시면 됩니다.

https://www.python.org/downloads/

Mac OS 또한 상단의 링크로 접속하신 뒤, 


 

 

해당 부분을 클릭하시고 설치하시면 됩니다.

 

라이브러리 설치법은 다음과 같습니다.

1) cmd창 켜기

2)pip install pygame 입력

3)설치 완료

(저는 이미 설치되어있기 때문에, 업그레이드를 하라고 뜨네요.)

 

pip은 현재까지 세미나를 들으셨다면 설치되어있을것이기 때문에 별도로 설명하지 않습니다.

(pip 설치 참고: https://m.blog.naver.com/lee95292/221205091279)

(Mac OS pip 설치 참고: https://nangkyeong.tistory.com/entry/macOS-Catalina%EC%97%90-python-3-pip-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0)

 

 

그 뒤 idle을 실행하시고 File->New File을 선택하신 뒤 세미나를 따라오시면 됩니다.

 

② 게임판 생성하기


<준비단계>

게임판을 생성하기 전,아래 링크의 파일을 다운받아 압축을  풀어주세요!게임에 이용될 이미지 파일들입니다.

생성해놓은 프로젝트 파일과 같은 폴더에 넣어 두시면 됩니다!앞으로 설명할 때 같은 폴더에 그림 파일이 존재하는 경우를 기준으로 설명할 것이므로,

다른 위치에 저장해 놓았을 경우 그림 파일을 불러오는 코드를 알아서 잘 작성해 주셔야 합니다.그럼 이제 정말로 게임판을 만들어 보겠습니다!

세미나 필요 파일 다운로드

 


게임판 생성하기

이번 장에서는 아래 화면과 같은 게임판을 만들어 보겠습니다.

# 1 배경만들기
import pygame

WHITE = (255, 255, 255)
pad_width = 740
pad_height = 370


def runGame():
    global gamepad, clock

    crashed = False
    while not crashed:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                crashed = True
        gamepad.fill(WHITE)
        pygame.display.update()
        clock.tick(60)

    pygame.quit()


def initGame():
    global gamepad, clock

    pygame.init()
    gamepad = pygame.display.set_mode((pad_width, pad_height))
    pygame.display.set_caption('PyFlying')

    clock = pygame.time.Clock()
    runGame()


initGame()

배경판을 만드는 데 필요한 소스코드입니다.하나하나 천천히 살펴보도록 할게요.

import pygame

>>파이썬에서 게임을 만드는 데 필요한pygame모듈을 불러와 사용할 수 있게 합니다.

WHITE = (255, 255, 255)
pad_width = 740
pad_height = 370

>> WHITE, pad_width, pad_height 3개의 변수를 선언하고,초기화합니다. WHITE의 경우()안에 들어가 있는3개의 숫자가 각각RGB코드 값을 의미합니다.

흰색의RGB는255, 255, 255이므로 조금 뒤 게임판이 색상을 지정할 때WHITE변수가 사용되어 흰색 게임판을 생성할 것입니다.

pad_width와pad_height는 각각 게임판의 너비와 높이로 설정할 것입니다.

def runGame():
    global gamepad, clock

    crashed = False
    while not crashed:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                crashed = True

        gamepad.fill(WHITE)
        pygame.display.update()
        clock.tick(60)

    pygame.quit()

>>게임을 실행하는 함수입니다. runGame함수를 벗어나는 것은 게임을 종료하는 것을 의미합니다.

gamepad와clock을 전역별수로 선언하고, crashed라는 변수를 선언하여,눈송이가 충돌했는지 여부를Boolean형태로 나타냅니다.

while not crashed :비행기가 충돌한 상태가 아닐 때, while문을 반복하여 게임을 실행합니다.

pygame.event.get은 게임판에서 발생하는 여러 이벤트들을 리턴합니다.이벤트 타입이pygame.QUIT,즉 창을 단는 것이면crashed가TRUE가 되어while문을 빠져나와 게임을 종료합니다.

그렇지 않다면 게임판을WHITE(흰색)으로 채우고,게임판을 다시 그립니다. clock.tick(60)은 게임의FPS값을60으로 설정한다는 의미입니다.


※FPS란? 프레임 레이트(Frame rate)란 디스플레이 장치가 하나의 데이터를 표시하는 속도

※Boolean이란? 컴퓨터 과학에서불리언(boolean) 자료형은 논리 자료형이라고도 하며, 참과 거짓을 나타내는 데 쓰인다. 주로 참은 1, 거짓은 0에 대응하나 언어마다 차이가 있다.

def initGame():
    global gamepad, clock

    pygame.init()
    gamepad = pygame.display.set_mode((pad_width, pad_height))
    pygame.display.set_caption('PyFlying')

    clock = pygame.time.Clock()
    runGame()

>>게임을 본격적으로 시작하기 전,필요한 파일들을 불러 오고,창을 구성하여 게임을 초기화하는 함수입니다.

runGame함수와 마찬가지로 전역변수gamepad와clock를 선언합니다.

pygame.init()으로pygame라이브러리를 초기화합니다. pygame으로 게임을 만들 때 필수적으로 호출하는 함수이기도 합니다.

gamepad변수를 선언하여 게임판의 크기를740 * 370로 설정합니다.

display.set_caption을PyFlying으로 설정하여 게임판을 실행할 때 창의 왼쪽 위에 나오는 타이틀을pyFlying으로 지정합니다.

clock은 게임의 초당 프레임을 설정하기 위해 생성하는 변수입니다.

초기화하는 과정을 모두 거쳤으므로runGame함수를 호출하여 실제로 게임을 구동합니다.

 

위의 코드를 실행하면 처음에 보았던 하얀 게임판이 잘 나오나요? 하얀 게임판이 잘 나온다면 다음 장에서는 하얀 게임판에 눈송이를 배치해보도록 하겠습니다!

 

③ 눈송이 배치하기


③ 눈송이 배치하기

이번 장에서는 조금 전 다운받았던 눈송이 이미지 파일을 불러와 게임판에 배치해보도록 하겠습니다.

결과 화면은 아래와 같을 예정이고,아래 화면에서 키보드 위,아래 방향 키를 누르면 눈송이가 위아래로 움직입니다.

#2. 게임판에 비행기 불러오기

#PyGame 라이브러리를 import 하기
import pygame

#흰색을 표현하는 값과 게임판의 폭, 게임판의 높이를 전역변수 WHITE, pad_width, pad_height에 각각 지정합니다.
WHITE = (255, 255, 255)
pad_width = 740
pad_height = 370

#비행기를 게임판 위 (x,y)위치에 그리기
def airplane(x,y):
    global gamepad, aircraft
    gamepad.blit(aircraft, (x,y))

#실제 게임이 구동되는 함수, initGame()함수에서 호출하여 사용
def runGame() :
    global gamepad, clock, aircraft

    #비행기의 최초 위치를 게임판의 왼쪽 적당한 위치에 두도록 좌표 설정
    x = pad_width *0.05
    y = pad_height*0.8
    #비행기의 y좌표 변화를 y_change 변수로 나타내기
    y_change = 0

    #게임을 종료하기 위한 플래그
    crashed = False
    while not crashed :
        for event in pygame.event.get():
            if event.type == pygame.QUIT: #마우스로 창을 닫는 이벤트라면 종료
                crashed = True

            #키보드의 위와 아래 화살표 키를 누르면 비행기가 위 아래로 5픽셀씩 움직이도록 하기, 놓으면 변화 X
            if event.type == pygame.KEYDOWN :
                if event.key == pygame.K_UP:
                    y_change = -5
                elif event.key == pygame.K_DOWN:
                    y_change = 5
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_UP or event.key == pygame.K_DOWN :
                    y_change = 0
        #키보드 입력에 따라 비행기의 y좌표 변경
        y += y_change

        gamepad.fill(WHITE)
        #비행기의 새로운 위치를 호출
        airplane(x,y)
        pygame.display.update()
        clock.tick(60)

    pygame.quit()

#게임을 초기화하고 시작하는 함수
def initGame() :
    #gamepad, clock을 전역변수로 사용
    global gamepad, clock, aircraft
    #pygame 라이브러리를 초기화 >> pygame을 사용하기 위해서는 최초에 항상 pygame.init()을 호출해야 함
    pygame.init()
    #실행될 게임판의 크기를 1024*512로 설정
    gamepad = pygame.display.set_mode((pad_width, pad_height))
    #게임판 타이틀을 'PyFlying'으로 지정
    pygame.display.set_caption('PyFlying')
    #게임의 초당 프레임(FPS) 설정을 위해 클락 생성.
    aircraft = pygame.image.load('noonsong.png')
    clock = pygame.time.Clock()
    #실제 게임 구동을 위한 함수인 runGame()함수 호출
    runGame()

initGame()

2장에서 눈송이 그림을 불러오고,배치하고,움직이는 코드를 추가로 작성했습니다.

이번에도 각각의 코드가 어떤 기능을 하는 지 살펴보도록 할게요.변경된 코드만 설명하도록 하겠습니다!

 

>>먼저 각각의 함수에 새로운 전역 변수로aircraft가 선언된 것을 확인할 수 있습니다.이는 눈송이 파일을 불러와 그것을 가리키는 변수입니다!

def airplane(x,y):
    global gamepad, aircraft
    gamepad.blit(aircraft, (x,y))

>> airplane함수는 화면 상의x, y좌표를 매개변수로 받아와 우리의 비행기가 될 눈송이의 위치를 그리는 함수입니다.

 

아래부터는runGame함수에 들어가는 코드입니다.

#비행기의 최초 위치를 게임판의 왼쪽 적당한 위치에 두도록 좌표 설정
x = pad_width *0.05
y = pad_height*0.8
#비행기의 y좌표 변화를 y_change 변수로 나타내기
y_change = 0

>>비행기의 위치인x, y를 각각 게임판의 너비와 높이를 이용하여 설정합니다.

키보드 방향키를 누르면 비행기가 움직일 것이기 때문에 이를 나타내는 변수인y_change를 선언합니다.우리의 비행기는x축으로는 움직이지 않기 때문에y좌표의 변화만 설정합니다. 

#키보드의 위와 아래 화살표 키를 누르면 비행기가 위 아래로 5픽셀씩 움직이도록 하기, 놓으면 변화 X
    if event.type == pygame.KEYDOWN :
        if event.key == pygame.K_UP:
            y_change = -5
        elif event.key == pygame.K_DOWN:
            y_change = 5
    if event.type == pygame.KEYUP:
        if event.key == pygame.K_UP or event.key == pygame.K_DOWN :
            y_change = 0
#키보드 입력에 따라 비행기의 y좌표 변경
y += y_change

gamepad.fill(WHITE)
#비행기의 새로운 위치를 호출
airplane(x,y)

>>위의 코드는 비행기를 움직이는 역할을 합니다.

가장 상위의if문은 키보드가 눌렸는지 여부를 판단하고,하위의if문은 각각 눌린 키보드의 방향이 위쪽인지,아래족인지 판단하여 비행기를 위,아래로 움직이도록 합니다.

비행지의 위치를 조정하고 나면, airplane(x,y)를 통해 바뀐 비행기의 위치에 비행기를 호출합니다.

다음으로, initGame함수에서 추가된 부분을 살펴보겠습니다.

aircraft = pygame.image.load('noonsong.png')

>>위에서 설명한 눈송이를 나타내는 전역변수aircraft를 새로 선언한 것을 제외하면,게임을 초기화할 때 눈송이 그림 파일을 불러오는 것이 추가되었음을 확인할 수 있습니다.

여기까지 게임판에 눈송이를 불러와 움직이게 해 보았는데요, 잘 작동 되나요? 그렇다면, 다음 장에서는 그림판에 배경을 입혀보도록 하겠습니다!

 

④배경 입히기


④배경 입히기

이번 장에서는 지금까지 만들었던 게임판에 배경을 입혀보도록 할 건데요,이번 장이 완료되면 여러분은 아래와 같은 화면을 확인하실 수 있을 겁니다.

밋밋했던 하얀 배경을 벗어난 모습이네요!그럼 이번 장의 코드도 살펴보도록 해요.

import pygame

WHITE=(255,255,255)
pad_width=740
pad_height=370

def back(x,y):
    global gamepad,background
    gamepad.blit(background,(x,y))

def airplane(x,y):
    global gamepad, aircraft
    gamepad.blit(aircraft,(x,y))

def runGame():
    global gamepad, clock, aircraft

    x=pad_width*0.05
    y=pad_height*0.8
    y_change=0

    background_x=0

    crashed=False
    while not crashed:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                crashed=True
            if event.type==pygame.KEYDOWN:
                if event.key==pygame.K_UP:
                    y_change=-5
                elif event.key==pygame.K_DOWN:
                    y_change=5
            if event.type==pygame.KEYUP:
                if event.key==pygame.K_UP or event.key==pygame.K_DOWN:
                    y_change=0
        y+=y_change
        gamepad.fill(WHITE)
        back(background_x,0)
        airplane(x,y)
        pygame.display.update()
        clock.tick(60)
    pygame.quit()
    quit()
def initGame():
    global gamepad,clock,aircraft,background

    pygame.init()
    gamepad=pygame.display.set_mode((pad_width,pad_height))
    pygame.display.set_caption('PyFlying')
    aircraft=pygame.image.load('noonsong.png')
    background=pygame.image.load('background.png')

    clock=pygame.time.Clock()
    runGame()

initGame()

앞과 마찬가지로 기존의 코드에 살을 붙여보도록 해봐요!

앞의 장에서aircraft전역변수가 추가되었던 것과 마찬가지로,이번에는back함수와initGame함수에 배경 이미지를 가리키는 전역변수background가 선언된 것을 확인할 수 있습니다.

def back(x,y):
    global gamepad,background
    gamepad.blit(background,(x,y))

>>새로운 함수back이 추가되었습니다.함수의 매개변수x, y는 배경 이미지의 왼쪽 상단 꼭짓점의 위치를 나타냅니다. x, y를 기준으로 배경 이미지가 삽입됩니다.

 

runGame함수로 넘어와 추가된 것은 아래와 같습니다.

background_x=0

>>배경 이미지의 좌상단 모서리의x좌표를 나타내는 변수입니다.다음 장에서 우리는 배경 이미지를 움직이게 할 것이므로,이와 같이 변수를 미리 설정해 줍니다.

back(background_x,0)

>>위와 마찬가지로 다음 장에서 배경 이미지의 위치가 변동됨에 따라 새로 배경 이미지를 불러오는 함수를 다시 호출합니다.

background=pygame.image.load('background.png')

>> initGame함수에서는 배경 이미지를 불러오는 과정을 거칩니다.눈송이 그림을 불러올 때와 똑같이 하면 됩니다!

여기까지가 게임판에 배경 이미지를 불러오는 과정이었습니다.

다음 장에서는 조금 전 불러온 배경 이미지의 위치를 변동시켜 마치 배경이 움직이는 듯한 모습을 보이도록 하겠습니다. 

 

⑤ 배경 움직이기


⑤ 배경 움직이기

조금 전 불러온 배경 이미지의 위치를 조금씩 변화시켜, 배경이 움직이는 듯한 느낌을 살려보겠습니다.

원래의 배경이 왼쪽으로 10만큼 움직이면 오른쪽에는 10만큼의 빈 공간이 생기게 될 텐데요,

이 빈 공간에 배경 이미지를 다시 삽입하여 하나의 연결된 그림처럼 보이게 하는 것이 그 원리입니다.

import pygame

WHITE=(255,255,255)
pad_width=740
pad_height=370
background_width=740

def back(background,x,y):
    global gamepad
    gamepad.blit(background,(x,y))

def airplane(x,y):
    global gamepad, aircraft
    gamepad.blit(aircraft,(x,y))

def runGame():
    global gamepad, clock, aircraft, background1,background2

    x=pad_width*0.05
    y=pad_height*0.8
    y_change=0

    background1_x=0
    background2_x=background_width

    crashed=False
    while not crashed:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                crashed=True
            if event.type==pygame.KEYDOWN:
                if event.key==pygame.K_UP:
                    y_change=-5
                elif event.key==pygame.K_DOWN:
                    y_change=5
            if event.type==pygame.KEYUP:
                if event.key==pygame.K_UP or event.key==pygame.K_DOWN:
                    y_change=0
        y+=y_change
        gamepad.fill(WHITE)

        background1_x-=2
        background2_x-=2

        if background1_x==-background_width:
            background1_x=background_width

        if background2_x==-background_width:
            background2_x=background_width

        back(background1,background1_x,0)
        back(background2, background2_x, 0)

        airplane(x,y)
        pygame.display.update()
        clock.tick(60)
    pygame.quit()
    quit()
def initGame():
    global gamepad,clock,aircraft,background1,background2

    pygame.init()
    gamepad=pygame.display.set_mode((pad_width,pad_height))
    pygame.display.set_caption('PyFlying')
    aircraft=pygame.image.load('noonsong.png')
    background1=pygame.image.load('background.png')
    background2=background1.copy()

    clock=pygame.time.Clock()
    runGame()

if __name__=='__main__':
    initGame()

이제 슬슬 코드가 길어지고 있네요! 지금까지 했던 것처럼 기존의 코드에 약간의 살을 붙이면 되니까 어려움을 느끼지 않으셔도 됩니다! 바뀐 부분을 차근차근 살펴보도록 할게요.

먼저기존의runGame, initGame함수 모두에서background로 설정되어 있던 전역 변수가 사라지고, background1과 background2가 생성되었습니다.

앞서 설명했던 배경이 움직이는 것처럼 보이게 하기 위해서 같은 배경 이미지를 두 개 쓴다고 했는데, 각각의 이미지를 나타내는 변수입니다!

또한 background_width=740 으로 배경 이미지의 가로크기를 설정해줍니다.

background1_x=0
background2_x=background_width

>> 먼저, background1과 2의 위치를 나타낼 x좌표를 각각 0과 background_width로 설정합니다.

그러면 background2는 게임판의 가장 오른쪽부터 나타나므로 처음에는 background2의 모습이 나타나지 않습니다.

background1_x-=2
background2_x-=2

   >> 두 배경 이미지의 x좌표를 -2만큼 변경합니다. 이를 통해 두 이미지는 2픽셀 만큼 왼쪽으로 이동한 것처럼 보일 것입니다.

if background1_x==-background_width:
    background1_x=background_width

if background2_x==-background_width:
    background2_x=background_width

>> 계속해서 두 배경 이미지를 왼쪽으로 이동하다가, 만약 하나의 이미지가 완전히 화면 밖으로 사라지게 될 경우, 사라진 배경 이미지의 위치를 를 게임판의 오른쪽으로 다시 설정합니다.

back(background1,background1_x,0)
back(background2, background2_x, 0)

>> 바뀐 배경 이미지의 위치를 다시 호출하여 화면에 나타냅니다.

back(background, x, y) 함수에서 background 매개변수가 어느 background를 받을지 설정해주는 역할을 합니다.

 

여기까지가 runGame에서 변경된 부분이고, 아래부터는 initGame에서 추가/수정된 부분입니다.

background1=pygame.image.load('background.png')
background2=background1.copy()

>> 배경 이미지를 불러와 background1이라고 합니다. Background2는 copy를 이용하여 background1과 같은 이미지로 설정합니다.

initGame 함수를 벗어나 코드의 마지막 즈음에 아래와 같은 무언가가 추가된 것을 확인할 수 있는데요,  

if __name__=='__main__':
    initGame()

이것은 현재 스크립트 파일이 실행되는 상태를 파악하기 위해 작성합니다.

참고:https://dojang.io/mod/page/view.php?id=2448

 

그럼, 이제 눈송이를 위아래로 움직이고, 또 배경이 왼쪽으로 이동하고 있나요? 원활히 세미나가 진행되고 있다면, 다음 장에서는 우리의 적, 교수님을 등장시켜 보도록 해요!

 

⑥적 등장시키기


⑥적 등장시키기

이번에는 적들을 등장시켜 봅시다!적이 등장할 확률은 약25%로 설정하겠습니다.

import pygame, random

WHITE=(255,255,255)
pad_width=740
pad_height=370
background_width=740

def drawObject(obj,x,y):
    global gamepad
    gamepad.blit(obj,(x,y))

def back(background,x,y):
    global gamepad
    gamepad.blit(background,(x,y))

def airplane(x,y):
    global gamepad, aircraft
    gamepad.blit(aircraft,(x,y))

def runGame():
    global gamepad, clock, aircraft, background1,background2
    global gyosu,cs

    x=pad_width*0.05
    y=pad_height*0.8
    y_change=0

    background1_x=0
    background2_x=background_width

    gyosu_x=pad_width
    gyosu_y=random.randrange(0,pad_height)

    c_x=pad_width
    c_y=random.randrange(0,pad_height)
    random.shuffle(cs)
    c=cs[0]

    crashed=False
    while not crashed:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                crashed=True
            if event.type==pygame.KEYDOWN:
                if event.key==pygame.K_UP:
                    y_change=-5
                elif event.key==pygame.K_DOWN:
                    y_change=5
            if event.type==pygame.KEYUP:
                if event.key==pygame.K_UP or event.key==pygame.K_DOWN:
                    y_change=0
        y+=y_change
        gamepad.fill(WHITE)

        background1_x-=2
        background2_x-=2

        gyosu_x-=7
        if gyosu_x<=0:
            gyosu_x=pad_width
            gyosu_y=random.randrange(0,pad_height)

        if c==None:
            c_x-=30
        else:
            c_x-=15

        if c_x<=0:
            c_x=pad_width
            c_y=random.randrange(0,pad_height)
            random.shuffle(cs)
            c=cs[0]

        if background1_x==-background_width:
            background1_x=background_width

        if background2_x==-background_width:
            background2_x=background_width

        drawObject(background1,background1_x,0)
        drawObject(background2,background2_x,0)
        drawObject(gyosu,gyosu_x,gyosu_y)
        if c!=None:
            drawObject(c,c_x,c_y)
        drawObject(aircraft,x,y)

        pygame.display.update()
        clock.tick(60)

    pygame.quit()
    quit()
def initGame():
    global gamepad,clock,aircraft,background1,background2
    global gyosu, cs

    cs=[]

    pygame.init()
    gamepad=pygame.display.set_mode((pad_width,pad_height))
    pygame.display.set_caption('PyFlying')
    aircraft=pygame.image.load('noonsong.png')
    background1=pygame.image.load('background.png')
    background2=background1.copy()
    gyosu = pygame.image.load('gyosu.png')
    cs.append(pygame.image.load('c.png'))
    cs.append(pygame.image.load('c2.png'))

    for i in range(5):
        cs.append(None)

    clock=pygame.time.Clock()
    runGame()

if __name__=='__main__':
    initGame()

이번 장에서 사용될 코드는 위와 같습니다.추가/수정된 부분을 살펴보도록 할게요.


먼저, runGame과initGame함수에 새로이 전역변수gyosu와cs가 추가된 것을 확인할 수 있습니다.

또한, drawObject함수가airplane이나back을 대체했음을 알 수 있습니다. 천천히 살펴보겠습니다.

import pygame, random

>>적이 등장하는 위치를 랜덤으로 정할 것이기 때문에random모듈을 불러옵니다.

def drawObject(obj,x,y):
    global gamepad
    gamepad.blit(obj,(x,y))

>> drawObject함수가 추가되었습니다.객체들을 하나하나 그리다 보니 코드의 중복이 많아져 추가된 함수입니다.게임판에 그려지는 각 객체들을 그려주는 함수입니다.

 

runGame함수를 살펴보겠습니다.

gyosu_x=pad_width
gyosu_y=random.randrange(0,pad_height)

c_x=pad_width
c_y=random.randrange(0,pad_height)
random.shuffle(cs)
c=cs[0]

>>교수와c학점이 날아올 위치를 랜덤으로 정합니다.

리스트 자료인c학점을 무작위로 섞은 후,첫번째 요소를 택합니다. c학점이 될 수도 있고, None이 될 수도 있습니다.

gyosu_x-=7
if gyosu_x<=0:
    gyosu_x=pad_width
    gyosu_y=random.randrange(0,pad_height)

>>교수를 눈송이 쪽으로7픽셀씩 날아오도록 합니다.만약 교수의x좌표가0이 된다면 교수의 위치를 다시 랜덤으로 설정합니다.

if c==None:
    c_x-=30
else:
    c_x-=15

if c_x<=0:
    c_x=pad_width
    c_y=random.randrange(0,pad_height)
    random.shuffle(cs)
    c=cs[0]

>>만약c가None이라면30픽셀씩 다가오게 합니다. None은c학점과 달리 아무 것도 없는 상태이므로 장애물이 되지 않습니다. c학점이 나오는 시간을 조절하기 위한 장치입니다.

>>만약c가c학점이라면, c학점을15픽셀씩 다가오게 합니다.그리고 교수와 마찬가지로c학점의 x좌표가 게임판을 벗어날 경우,위에서 했던c학점을 배치시키는 과정을 다시 합니다.

drawObject(background1,background1_x,0)
drawObject(background2,background2_x,0)
drawObject(gyosu,gyosu_x,gyosu_y)
if c!=None:
    drawObject(c,c_x,c_y)
drawObject(aircraft,x,y)

>>게임판에 등장하는 각종 객체들을drawObject함수를 사용하여 그립니다.

 

drawObject(aircraft, x, y)와airplane(x, y)이 수행하는 역할은aircraft객체를 그린다는 점에서 동일하지만

drawObject는aircraft를 제외한 다양한 객체를 게임판에 그려줄 수 있기 때문에 좀 더 범용적인 역할을 수행합니다.

back(background1, background1_x, 0)
back(background2, background2_x, 0)


airplane(x,y)

<<< 따라서 위와 같은 코드들은

drawObject(background1, background1_x, 0)
drawObject(background2, background2_x, 0)
drawObject(aircraft, x, y)

<<< 이런 식으로 대체할 수 있습니다.

아래부터는 initGame 함수입니다.

cs=[]

>> c학점2개와None객체5개를 담을 리스트입니다.

gyosu = pygame.image.load('gyosu.png')
cs.append(pygame.image.load('c.png'))
cs.append(pygame.image.load('c2.png'))
for i in range(5):
    cs.append(None)

>>위에 만들어놓은cs리스트에c학점2개의 이미지 파일,그리고5개의None객체를 추가합니다.

이렇게 우리는 이제 게임판에 눈송이와 우리의 적인 교수, c학점을 등장시켰습니다. 다음 장에서는 눈송이가 총알을 발사하는 것을 구현하도록 하겠습니다.

 

⑦ 총알 쏘기 구현하기


⑦ 총알 쏘기 구현하기

 

 

 

이번에는 위 그림에서 빨간색 동그라미로 표시해 둔 총알을 발사하는 것을 구현하겠습니다.키보드의 왼쪽 컨트롤키를 누르면 총알이 한 발 씩 나가도록 해보겠습니다.

또한 추가로,눈송이가 게임패드 내에서만 움직이도록 수정하겠습니다.

이번에 사용될 코드는 아래와 같습니다.

**코드의 가독성을 위해 코드의 위치를 바꾸었습니다!**

import pygame, random
from time import sleep

WHITE=(255,255,255)
pad_width=740
pad_height=370
background_width=740
gyosu_width=110
aircraft_width=90
aircraft_height=55

def drawObject(obj,x,y):
    global gamepad
    gamepad.blit(obj,(x,y))

def back(background,x,y):
    global gamepad
    gamepad.blit(background,(x,y))

def airplane(x,y):
    global gamepad, aircraft
    gamepad.blit(aircraft,(x,y))

def runGame():
    global gamepad, clock, aircraft, background1,background2
    global gyosu,cs,snow

    snow_xy=[]

    x=pad_width*0.05
    y=pad_height*0.8
    y_change=0

    background1_x=0
    background2_x=background_width

    gyosu_x=pad_width
    gyosu_y=random.randrange(0,pad_height)

    c_x=pad_width
    c_y=random.randrange(0,pad_height)
    random.shuffle(cs)
    c=cs[0]

    crashed=False
    while not crashed:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                crashed=True
            if event.type==pygame.KEYDOWN:
                if event.key==pygame.K_UP:
                    y_change=-5
                elif event.key==pygame.K_DOWN:
                    y_change=5

                elif event.key==pygame.K_LCTRL:
                    snow_x=x+aircraft_width
                    snow_y=y+aircraft_height/2
                    snow_xy.append([snow_x,snow_y])
                elif event.key==pygame.K_SPACE:
                    sleep(5)
            if event.type==pygame.KEYUP:
                if event.key==pygame.K_UP or event.key==pygame.K_DOWN:
                    y_change=0
        #게임패드 클리어
        gamepad.fill(WHITE)
        #배경 그리기
        background1_x-=2
        background2_x-=2

        if background1_x==-background_width:
            background1_x=background_width

        if background2_x==-background_width:
            background2_x=background_width

        drawObject(background1,background1_x,0)
        drawObject(background2,background2_x,0)
        #비행기(눈송)위치
        y += y_change
        if y<0:
            y=0
        elif y>pad_height-aircraft_height:
            y=pad_height-aircraft_height
        #교수 위치
        gyosu_x-=7
        if gyosu_x<=0:
            gyosu_x=pad_width
            gyosu_y=random.randrange(0,pad_height)
        #C학점 위치
        if c==None:
            c_x-=30
        else:
            c_x-=15

        if c_x<=0:
            c_x=pad_width
            c_y=random.randrange(0,pad_height)
            random.shuffle(cs)
            c=cs[0]
        #눈 위치
        if len(snow_xy)!=0:
            for i,sxy in enumerate(snow_xy):
                sxy[0]+=15
                snow_xy[i][0]=sxy[0]
                if sxy[0]>=pad_width:
                    snow_xy.remove(sxy)

        drawObject(gyosu,gyosu_x,gyosu_y)
        drawObject(aircraft, x, y)

        if c!=None:
            drawObject(c,c_x,c_y)

        if len(snow_xy)!=0:
            for sx,sy in snow_xy:
                drawObject(snow,sx,sy)

        pygame.display.update()
        clock.tick(60)

    pygame.quit()
    quit()
def initGame():
    global gamepad,clock,aircraft,background1,background2
    global gyosu, cs, snow

    cs=[]

    pygame.init()
    gamepad=pygame.display.set_mode((pad_width,pad_height))
    pygame.display.set_caption('PyFlying')
    aircraft=pygame.image.load('noonsong.png')
    background1=pygame.image.load('background.png')
    background2=background1.copy()
    gyosu = pygame.image.load('gyosu.png')
    cs.append(pygame.image.load('c.png'))
    cs.append(pygame.image.load('c2.png'))

    for i in range(5):
        cs.append(None)

    snow=pygame.image.load('snow.png')

    clock=pygame.time.Clock()
    runGame()

if __name__=='__main__':
    initGame()

추가/수정된 부분을 살펴보도록 하겠습니다.


먼저,총알 객체가 추가되었기 때문에 우리의 총알인snow전역변수가 추가되었습니다!

from time import sleep

>>아래 부분에서 스페이스바를 누를 경우 발생하는 이벤트를 위해 모듈을 불러옵니다.

gyosu_width=110
aircraft_width=90
aircraft_height=55

>>교수와 눈송이의 너비/높이를 지정하는 변수도 선언한 것을 확인할 수 있습니다.

총알에 맞았을 경우 공격 판정이 발생하는 히트 박스의 범위라고 생각하시면 됩니다!

아래부터는runGame함수입니다.

snow_xy=[]

>>리스트 자료snow_xy에는 키보드의 왼쪽 컨트롤 키를 누를 때마다 생성되는 총알의 좌표를 추가할 예정입니다.

elif event.key==pygame.K_LCTRL:
    snow_x=x+aircraft_width
    snow_y=y+aircraft_height/2
    snow_xy.append([snow_x,snow_y])
elif event.key==pygame.K_SPACE:
    sleep(5)

>>키보드의 왼쪽 컨트롤 키를 누르면 현재 눈송이의 위치에서 총알이 나가도록 좌표를 설정한 후, snow_xy리스트에 추가합니다.

만약 스페이스바를 누르면5초간 게임이 정지됩니다. 5초가 지나면 다시 게임을 재개합니다.

#눈 위치
if len(snow_xy)!=0:
    for i,sxy in enumerate(snow_xy):
        sxy[0]+=15
        snow_xy[i][0]=sxy[0]
        if sxy[0]>=pad_width:
            snow_xy.remove(sxy)

>>리스트 자료snow_xy에 좌표가 들어 있다면 하나씩 추출하여 좌표를 갱신합니다.

총알의 위치는15픽셀씩 변경되도록 했습니다.총알이 게임판을 지나가면snow_xy리스트에서 해당 좌표를 삭제합니다.

if len(snow_xy)!=0:
    for sx,sy in snow_xy:
        drawObject(snow,sx,sy)

>>만약snow_xy리스트가 비어 있지 않다면,리스트에 추가된 총알의 위치를 불러와drawObject함수를 호출하여 게임판에 그립니다.

여기까지가 총알이 발사되는 코드였습니다.아래는 눈송이가 게임판 안에서만 움직이도록 제한하는 코드입니다.

#비행기(눈송)위치
y += y_change
if y<0:
    y=0
elif y>pad_height-aircraft_height:
    y=pad_height-aircraft_height

initGame함수에는 총알 그림 파일만 불러와주면 됩니다!

snow=pygame.image.load('snow.png')

이제 여러분은 게임을 실행하면서 좌측 컨트롤 키를 누르면 눈송이로부터 총알이 나가는 것을 확인하실 수 있을 겁니다.

그런데, 우리의 총알에 맞은 적들이 사라지지 않아요. 다음 장에서는 총알에 맞은 적이 파괴되는 것을 구현하도록 하겠습니다.

 

⑧적 파괴하기


⑧적 파괴하기

총알에 맞은 적이 파괴되는 것을 구현하도록 하겠습니다. 

import pygame, random
from time import sleep

WHITE=(255,255,255)
pad_width=740
pad_height=370
background_width=740
gyosu_width=110
aircraft_width=90
aircraft_height=55

gyosu_width=110
gyosu_height=67


def drawObject(obj,x,y):
    global gamepad
    gamepad.blit(obj,(x,y))

def back(background,x,y):
    global gamepad
    gamepad.blit(background,(x,y))

def airplane(x,y):
    global gamepad, aircraft
    gamepad.blit(aircraft,(x,y))

def runGame():
    global gamepad, clock, aircraft, background1,background2
    global gyosu,cs,snow,boom

    isShotGyosu=False
    boom_count=0

    snow_xy=[]

    x=pad_width*0.05
    y=pad_height*0.8
    y_change=0

    background1_x=0
    background2_x=background_width

    gyosu_x=pad_width
    gyosu_y=random.randrange(0,pad_height)

    c_x=pad_width
    c_y=random.randrange(0,pad_height)
    random.shuffle(cs)
    c=cs[0]

    crashed=False
    while not crashed:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                crashed=True
            if event.type==pygame.KEYDOWN:
                if event.key==pygame.K_UP:
                    y_change=-5
                elif event.key==pygame.K_DOWN:
                    y_change=5

                elif event.key==pygame.K_LCTRL:
                    snow_x=x+aircraft_width
                    snow_y=y+aircraft_height/2
                    snow_xy.append([snow_x,snow_y])
                elif event.key==pygame.K_SPACE:
                    sleep(5)
            if event.type==pygame.KEYUP:
                if event.key==pygame.K_UP or event.key==pygame.K_DOWN:
                    y_change=0
        #게임패드 클리어
        gamepad.fill(WHITE)
        #배경 그리기
        background1_x-=2
        background2_x-=2

        if background1_x==-background_width:
            background1_x=background_width

        if background2_x==-background_width:
            background2_x=background_width

        drawObject(background1,background1_x,0)
        drawObject(background2,background2_x,0)
        #비행기(눈송)위치
        y += y_change
        if y<0:
            y=0
        elif y>pad_height-aircraft_height:
            y=pad_height-aircraft_height
        #교수 위치
        gyosu_x-=7
        if gyosu_x<=0:
            gyosu_x=pad_width
            gyosu_y=random.randrange(0,pad_height)
        #C학점 위치
        if c==None:
            c_x-=30
        else:
            c_x-=15

        if c_x<=0:
            c_x=pad_width
            c_y=random.randrange(0,pad_height)
            random.shuffle(cs)
            c=cs[0]
        #눈 위치
        if len(snow_xy)!=0:
            for i,sxy in enumerate(snow_xy):
                sxy[0]+=15
                snow_xy[i][0]=sxy[0]

                if sxy[0]>=gyosu_x:
                    if sxy[1]>gyosu_y and sxy[1]<gyosu_y+gyosu_height:
                        snow_xy.remove(sxy)
                        isShotGyosu=True

                if sxy[0]>=pad_width:
                    try:
                        snow_xy.remove(sxy)
                    except:
                        pass

        drawObject(aircraft, x, y)

        if c!=None:
            drawObject(c,c_x,c_y)

        if len(snow_xy)!=0:
            for sx,sy in snow_xy:
                drawObject(snow,sx,sy)

        if not isShotGyosu:
            drawObject(gyosu,gyosu_x,gyosu_y)
        else:
            drawObject(boom,gyosu_x,gyosu_y)
            boom_count+=1
            if boom_count>5:
                boom_count=0
                gyosu_x=pad_width
                gyosu_y=random.randrange(0,pad_height-gyosu_height)
                isShotGyosu=False

        pygame.display.update()
        clock.tick(60)

    pygame.quit()
    quit()
def initGame():
    global gamepad,clock,aircraft,background1,background2
    global gyosu, cs, snow,boom

    cs=[]

    pygame.init()
    gamepad=pygame.display.set_mode((pad_width,pad_height))
    pygame.display.set_caption('PyFlying')
    aircraft=pygame.image.load('noonsong.png')
    background1=pygame.image.load('background.png')
    background2=background1.copy()
    gyosu = pygame.image.load('gyosu.png')
    cs.append(pygame.image.load('c.png'))
    cs.append(pygame.image.load('c2.png'))
    boom=pygame.image.load('boom.png')

    for i in range(5):
        cs.append(None)

    snow=pygame.image.load('snow.png')

    clock=pygame.time.Clock()
    runGame()

if __name__=='__main__':
    initGame()

이번에 추가/변경된 사항들은 아래와 같습니다.

먼저,전역변수boom이 새로 생성되었습니다.적이 파괴될 때 잠깐 나타나는 피격 이미지입니다.

isShotGyosu=False
boom_count=0

>> isShotGyosu변수는 총알이 교수에 명중했는지 아닌지를 판단하는 불린 형태의 변수입니다.

boom_count는 폭발 이미지가 화면에 표시되는 시간을 위한 변수입니다.

if sxy[0]>=gyosu_x:
    if sxy[1]>gyosu_y and sxy[1]<gyosu_y+gyosu_height:
        snow_xy.remove(sxy)
        isShotGyosu=True

if sxy[0]>=pad_width:
    try:
        snow_xy.remove(sxy)
    except:
        pass

>>눈송이가 발사한 총알이 교수에 명중했는지 체크하고,만약 명중했다면 총알을 게임판에서 제거하기 위해 해당 총알 좌표를 리스트에서 제거합니다.

그리고,교수에 명중했으므로isShotGyosu를True로 바꿔줍니다.

if not isShotGyosu:
    drawObject(gyosu,gyosu_x,gyosu_y)
else:
    drawObject(boom,gyosu_x,gyosu_y)
    boom_count+=1
    if boom_count>5:
        boom_count=0
        gyosu_x=pad_width
        gyosu_y=random.randrange(0,pad_height-gyosu_height)
        isShotGyosu=False

>>총알이 교수에 명중하지 않았다면,교수를 계속해서 게임판에 그려줍니다.

총알이 교수에 명중했다면 교수의 위치에 폭발 이미지를 그리고, while문을5번 돌 때까지 화면에 표시되게 합니다.다음으로,새로운 교수를 등장시킵니다.

initGame함수에서는 별다른 과정은 따로 있지 않고,폭발하는 이펙트 이미지 파일을 불러오는 코드만 추가합니다,

boom=pygame.image.load('boom.png')

이제 눈송이가 발사한 총알에 맞은 적들은 파괴되는 것을 확인하실 수 있을 겁니다.

우리만 적들을 파괴하면 재미가 없지요! 다음 장에서는 적들이 우리를 공격하는 것을 구현하도록 하겠습니다.

 

⑨ 적들의 반격 구현하기


⑨ 적들의 반격 구현하기

우리의 눈송이는 교수와c학점을 마주치면 풀이 죽는다고 해요.그래서 우리의 게임에서는 눈송이가c학점 혹은 교수와 충돌할 경우 공격판정이 발생한다고 정하겠습니다.

참,앞서 적을 공격하는 것을 구현할 때,교수는 파괴되지만c학점은 파괴되지 않았는데요…우리는c학점을 요리조리 피해야 하는 운명입니다☹

 

 

만약 여러분이c학점을 피하지 못한다면 이렇게 재수강할 운명을 맞이할 거에요…

(여기서 잠깐!Retake는'재수강'을 의미하는 단어입니다.)

 

그럼,이번 장에서 사용될 코드를 먼저 보여드리겠습니다!

import pygame, random
from time import sleep

WHITE=(255,255,255)
RED=(255,0,0)
pad_width=740
pad_height=370
background_width=740
aircraft_width=90
aircraft_height=55

gyosu_width=110
gyosu_height=67
c1_width=140
c1_height=60
c2_width=86
c2_height=60

def textObj(text,font):
    textSurface=font.render(text,True,RED)
    return textSurface,textSurface.get_rect()

def dispMessage(text):
    global gamepad

    largeText=pygame.font.Font('freesansbold.ttf',115)
    TextSurf,TextRect=textObj(text,largeText)
    TextRect.center=((pad_width/2),(pad_height/2))
    gamepad.blit(TextSurf,TextRect)
    pygame.display.update()
    sleep(2)
    runGame()

def crash():
    global gamepad
    dispMessage('RETAKE')


def drawObject(obj,x,y):
    global gamepad
    gamepad.blit(obj,(x,y))


def runGame():
    global gamepad, clock, aircraft, background1,background2
    global gyosu,cs,snow,boom

    isShotGyosu=False
    boom_count=0

    snow_xy=[]

    x=pad_width*0.05
    y=pad_height*0.8
    y_change=0

    background1_x=0
    background2_x=background_width

    gyosu_x=pad_width
    gyosu_y=random.randrange(0,pad_height)

    c_x=pad_width
    c_y=random.randrange(0,pad_height)
    random.shuffle(cs)
    c=cs[0]

    crashed=False
    while not crashed:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                crashed=True
            if event.type==pygame.KEYDOWN:
                if event.key==pygame.K_UP:
                    y_change=-5
                elif event.key==pygame.K_DOWN:
                    y_change=5

                elif event.key==pygame.K_LCTRL:
                    snow_x=x+aircraft_width
                    snow_y=y+aircraft_height/2
                    snow_xy.append([snow_x,snow_y])
                elif event.key==pygame.K_SPACE:
                    sleep(5)
            if event.type==pygame.KEYUP:
                if event.key==pygame.K_UP or event.key==pygame.K_DOWN:
                    y_change=0
        #게임패드 클리어
        gamepad.fill(WHITE)
        #배경 그리기
        background1_x-=2
        background2_x-=2

        if background1_x==-background_width:
            background1_x=background_width

        if background2_x==-background_width:
            background2_x=background_width

        drawObject(background1,background1_x,0)
        drawObject(background2,background2_x,0)
        #비행기(눈송)위치
        y += y_change
        if y<0:
            y=0
        elif y>pad_height-aircraft_height:
            y=pad_height-aircraft_height
        #교수 위치
        gyosu_x-=7
        if gyosu_x<=0:
            gyosu_x=pad_width
            gyosu_y=random.randrange(0,pad_height)
        #C학점 위치
        if c[1]==None: #수정됨
            c_x-=30
        else:
            c_x-=15

        if c_x<=0:
            c_x=pad_width
            c_y=random.randrange(0,pad_height)
            random.shuffle(cs)
            c=cs[0]
        #눈 위치
        if len(snow_xy)!=0:
            for i,sxy in enumerate(snow_xy):
                sxy[0]+=15
                snow_xy[i][0]=sxy[0]

                if sxy[0]>=gyosu_x:
                    if sxy[1]>gyosu_y and sxy[1]<gyosu_y+gyosu_height:
                        snow_xy.remove(sxy)
                        isShotGyosu=True

                if sxy[0]>=pad_width:
                    try:
                        snow_xy.remove(sxy)
                    except:
                        pass
        if x+aircraft_width>gyosu_x:
            if(y>gyosu_y and y<gyosu_y+gyosu_height)or\
                    (y+aircraft_height>gyosu_y and y+aircraft_height<gyosu_y+gyosu_height):
                crash()
        drawObject(aircraft, x, y)

        if c[1]!=None:
            if c[0]==0:
                c_width=c1_width
                c_height=c1_height
            elif c[0]==1:
                c_width=c2_width
                c_height=c2_height

            if x+aircraft_width>c_x:
                if(y>c_y and y<c_y+c_height)or\
                        (y+aircraft_height>c_y and y+aircraft_height<c_y+c_height):
                    crash()


        if len(snow_xy)!=0:
            for sx,sy in snow_xy:
                drawObject(snow,sx,sy)

        if not isShotGyosu:
            drawObject(gyosu,gyosu_x,gyosu_y)
        else:
            drawObject(boom,gyosu_x,gyosu_y)
            boom_count+=1
            if boom_count>5:
                boom_count=0
                gyosu_x=pad_width
                gyosu_y=random.randrange(0,pad_height-gyosu_height)
                isShotGyosu=False

        if c[1]!=None:
            drawObject(c[1],c_x,c_y)

        pygame.display.update()
        clock.tick(60)

    pygame.quit()
    quit()
def initGame():
    global gamepad,clock,aircraft,background1,background2
    global gyosu, cs, snow,boom

    cs=[]

    pygame.init()
    gamepad=pygame.display.set_mode((pad_width,pad_height))
    pygame.display.set_caption('PyFlying')
    aircraft=pygame.image.load('noonsong.png')
    background1=pygame.image.load('background.png')
    background2=background1.copy()
    gyosu = pygame.image.load('gyosu.png')
    cs.append((0,pygame.image.load('c.png')))
    cs.append((1,pygame.image.load('c2.png')))
    boom=pygame.image.load('boom.png')

    for i in range(3):
        cs.append((i+2,None))

    snow=pygame.image.load('snow.png')

    clock=pygame.time.Clock()
    runGame()

if __name__=='__main__':
    initGame()

 

 

먼저, 3개의 새로운 함수가 추가되었습니다.

def textObj(text,font):
    textSurface=font.render(text,True,RED)
    return textSurface,textSurface.get_rect()

>> textObj는 게임 화면에 표시될 텍스트의 모양과 영역을 설정하는 함수입니다.

textObj함수에서 설정한 바를 토대로 아래의dispMessage함수에서 게임판에 글자를 출력합니다.

def dispMessage(text):
    global gamepad

    largeText=pygame.font.Font('freesansbold.ttf',115)
    TextSurf,TextRect=textObj(text,largeText)
    TextRect.center=((pad_width/2),(pad_height/2))
    gamepad.blit(TextSurf,TextRect)
    pygame.display.update()
    sleep(2)
    runGame()

>> dispMessage는 매개변수text를 받아와 게임판의 정중앙에 출력합니다.

폰트는freesansbold.ttf로,그리고 글자의 크기는115,색깔은 빨간색으로 출력합니다.

**글자의 색이 빨간색인 것은textObj함수에서 설정했습니다!

만약 게임이 오버가 되고,텍스트가 화면에 출력된다면,게임을2초동안 정지하고, runGame함수를 호출하여 게임을 재시작합니다.

**화면에 글자가 출력되는 경우가 게임오버일 때만이라고 설정했기 때문에 그렇습니다!

def crash():
    global gamepad
    dispMessage('RETAKE')

>> crash함수는 눈송이가 적의 공격에 당했을 때 호출되는 함수입니다.공격을 당했을 경우dispMessage함수를 호출하여RETAKE문구가 출력됩니다.

runGame함수에서 추가/수정된 부분은 아래와 같습니다.

if c[1]==None:

>>전체 코드를 기준으로114번째에 해당하는 코드입니다.

전 장에서를if c == None이었는데, c의 멤버가(식별자,이미지객체)로 수정되었기 때문에c학점의 이미지는c[1]이므로 이를 반영한 것입니다.

175번째 줄에 같은 코드가 있습니다.이 역시 같은 이유로 수정되었습니다!

if x+aircraft_width>gyosu_x:
    if(y>gyosu_y and y<gyosu_y+gyosu_height)or\
            (y+aircraft_height>gyosu_y and y+aircraft_height<gyosu_y+gyosu_height):
        crash()
drawObject(aircraft, x, y)

>>눈송이가 교수와 충돌했는지 확인하는 코드입니다.

이전에 설명했던 히트박스의 위치가 서로 겹치는지 체크하여 충돌 여부를 판단합니다.만약 충돌했다면, crash함수를 호출합니다.

if c[1]!=None:
    if c[0]==0:
        c_width=c1_width
        c_height=c1_height
    elif c[0]==1:
        c_width=c2_width
        c_height=c2_height

    if x+aircraft_width>c_x:
        if(y>c_y and y<c_y+c_height)or\
                (y+aircraft_height>c_y and y+aircraft_height<c_y+c_height):
            crash()

>>눈송이와c학점이 충돌했는지 체크하는 코드입니다.

먼저 체크하려는c학점의 식별자가0이면,첫번째c학점 이미지의 정보를,식별자가1이면 두번째c학점 이미지의 정보를 취합니다.

다음으로 눈송이와c학점이 충돌하는 조건인지 체크하고,만약 충돌한 경우라면crash함수를 호출합니다.

다음으로, initGame에서 추가/수정된 부분은 아래와 같습니다.

cs.append((0,pygame.image.load('c.png')))
cs.append((1,pygame.image.load('c2.png')))

>>리스트 변수인cs의 멤버를(숫자,이미지객체)로 수정했습니다.

 

이는c학점의 두 이미지의 크기가 다르므로 어떤 이미지에 눈송이가 충돌했는지 알아내기 위해 식별자가 필요했기 때문입니다.

for i in range(3):
    cs.append((i+2,None))

>> 위와 마찬가지로 식별자를 추가하여 None도 cs의 멤버로 추가합니다.

 

⑨새로운 규칙 추가하기


⑨새로운 규칙 추가하기

어느덧 세미나의 마지막 장입니다!

이제 기본적인 게임 자체는 다 만들었습니다.

그런데 게임이 너무 단순하다고 생각되지 않나요?그래서 간단한 룰 하나를 추가하도록 하겠습니다!

바로,교수를 눈으로 맞추지 못하고3번 놓치면 게임 오버되기!

게임판 좌측 상단에 놓친 교수의 수를 표시하여 플레이어가 알 수 있도록 하겠습니다.이 숫자가3이 되면 게임 오버입니다☹

#교수가 화면 내에서 넘어갈 시 카운트 되고, 3명을 못 잡고 지나갈 시 RETAKE 됩니다.
#RETAKE는 영어로 재수강입니다.
import pygame, random
from time import sleep

WHITE=(255,255,255)
RED=(255,0,0)
pad_width=740
pad_height=370
background_width=740
gyosu_width=110
aircraft_width=90
aircraft_height=55

gyosu_width=110
gyosu_height=67
c1_width=140
c1_height=60
c2_width=86
c2_height=60

def drawScore(count):
    global gamepad

    font=pygame.font.SysFont(None,25)
    text=font.render('Passed Gyosu:'+str(count),True,WHITE)
    gamepad.blit(text,(0,0))

def gameOver():
    global gamepad
    dispMessage('RETAKE')

def textObj(text,font):
    textSurface=font.render(text,True,RED)
    return textSurface,textSurface.get_rect()

def dispMessage(text):
    global gamepad

    largeText=pygame.font.Font('freesansbold.ttf',115)
    TextSurf,TextRect=textObj(text,largeText)
    TextRect.center=((pad_width/2),(pad_height/2))
    gamepad.blit(TextSurf,TextRect)
    pygame.display.update()
    sleep(2)
    runGame()

def crash():
    global gamepad
    dispMessage('RETAKE')


def drawObject(obj,x,y):
    global gamepad
    gamepad.blit(obj,(x,y))


def runGame():
    global gamepad, clock, aircraft, background1,background2
    global gyosu,cs,snow,boom

    isShotGyosu=False
    boom_count=0

    gyosu_passed=0

    snow_xy=[]

    x=pad_width*0.05
    y=pad_height*0.8
    y_change=0

    background1_x=0
    background2_x=background_width

    gyosu_x=pad_width
    gyosu_y=random.randrange(0,pad_height)

    c_x=pad_width
    c_y=random.randrange(0,pad_height)
    random.shuffle(cs)
    c=cs[0]

    crashed=False
    while not crashed:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                crashed=True
            if event.type==pygame.KEYDOWN:
                if event.key==pygame.K_UP:
                    y_change=-5
                elif event.key==pygame.K_DOWN:
                    y_change=5

                elif event.key==pygame.K_LCTRL:
                    snow_x=x+aircraft_width
                    snow_y=y+aircraft_height/2
                    snow_xy.append([snow_x,snow_y])
                elif event.key==pygame.K_SPACE:
                    sleep(5)
            if event.type==pygame.KEYUP:
                if event.key==pygame.K_UP or event.key==pygame.K_DOWN:
                    y_change=0
        #게임패드 클리어
        gamepad.fill(WHITE)
        #배경 그리기
        background1_x-=2
        background2_x-=2

        if background1_x==-background_width:
            background1_x=background_width

        if background2_x==-background_width:
            background2_x=background_width

        drawObject(background1,background1_x,0)
        drawObject(background2,background2_x,0)

        drawScore(gyosu_passed)
        if gyosu_passed>2:
            gameOver()
        #비행기(눈송)위치
        y += y_change
        if y<0:
            y=0
        elif y>pad_height-aircraft_height:
            y=pad_height-aircraft_height
        #교수 위치
        gyosu_x-=7
        if gyosu_x<=0:
            gyosu_passed+=1
            gyosu_x=pad_width
            gyosu_y=random.randrange(0,pad_height)
        #C학점 위치
        if c[1]==None: #수정됨
            c_x-=30
        else:
            c_x-=15

        if c_x<=0:
            c_x=pad_width
            c_y=random.randrange(0,pad_height)
            random.shuffle(cs)
            c=cs[0]
        #눈 위치
        if len(snow_xy)!=0:
            for i,sxy in enumerate(snow_xy):
                sxy[0]+=15
                snow_xy[i][0]=sxy[0]

                if sxy[0]>=gyosu_x:
                    if sxy[1]>gyosu_y and sxy[1]<gyosu_y+gyosu_height:
                        snow_xy.remove(sxy)
                        isShotGyosu=True

                if sxy[0]>=pad_width:
                    try:
                        snow_xy.remove(sxy)
                    except:
                        pass
        if x+aircraft_width>gyosu_x:
            if(y>gyosu_y and y<gyosu_y+gyosu_height)or\
                    (y+aircraft_height>gyosu_y and y+aircraft_height<gyosu_y+gyosu_height):
                crash()
        drawObject(aircraft, x, y)

        if c[1]!=None:
            if c[0]==0:
                c_width=c1_width
                c_height=c1_height
            elif c[0]==1:
                c_width=c2_width
                c_height=c2_height

            if x+aircraft_width>c_x:
                if(y>c_y and y<c_y+c_height)or\
                        (y+aircraft_height>c_y and y+aircraft_height<c_y+c_height):
                    crash()


        if len(snow_xy)!=0:
            for sx,sy in snow_xy:
                drawObject(snow,sx,sy)

        if not isShotGyosu:
            drawObject(gyosu,gyosu_x,gyosu_y)
        else:
            drawObject(boom,gyosu_x,gyosu_y)
            boom_count+=1
            if boom_count>5:
                boom_count=0
                gyosu_x=pad_width
                gyosu_y=random.randrange(0,pad_height-gyosu_height)
                isShotGyosu=False

        if c[1]!=None:
            drawObject(c[1],c_x,c_y)

        pygame.display.update()
        clock.tick(60)

    pygame.quit()
    quit()
def initGame():
    global gamepad,clock,aircraft,background1,background2
    global gyosu, cs, snow,boom

    cs=[]

    pygame.init()
    gamepad=pygame.display.set_mode((pad_width,pad_height))
    pygame.display.set_caption('PyFlying')
    aircraft=pygame.image.load('noonsong.png')
    background1=pygame.image.load('background.png')
    background2=background1.copy()
    gyosu = pygame.image.load('gyosu.png')
    cs.append((0,pygame.image.load('c.png')))
    cs.append((1,pygame.image.load('c2.png')))
    boom=pygame.image.load('boom.png')

    for i in range(3):
        cs.append((i+2,None))

    snow=pygame.image.load('snow.png')

    clock=pygame.time.Clock()
    runGame()

if __name__=='__main__':
    initGame()

최종 코드는 위와 같습니다.이번 코드에서 추가/변경된 사항은 아래와 같습니다.

 

>>새로운2개의 함수가 추가되었습니다.

def drawScore(count):
    global gamepad

    font=pygame.font.SysFont(None,25)
    text=font.render('Passed Gyosu:'+str(count),True,WHITE)
    gamepad.blit(text,(0,0))

>> drawScore함수는 게임판 좌측 상단에 크기25의 흰색 글씨로 놓친 교수의 수를 표시하는 함수입니다.

출력되는 형식은‘Passed Gyosu: 0’과 같습니다.

def gameOver():
    global gamepad
    dispMessage('RETAKE')

>> gameOver는 교수를3번 놓치면 호출되는 함수입니다. RETAKE를 출력하도록dispMessage함수를 호출합니다.

 

다음으로, runGame에서 추가된 부분입니다.

gyosu_passed=0

>>눈송이가 교수를 몇 번 놓쳤는지 나타내는 변수입니다.

drawScore(gyosu_passed)
if gyosu_passed>2:
    gameOver()

>>놓친 교수의 수를 표시하기 위해drawScore를 호출합니다.

만약 놓친 교수의 수가2보다 크다면gameOver를 호출합니다.

if gyosu_x<=0:
    gyosu_passed+=1

>>교수가 게임판의 왼쪽에 도착하면 눈송이가 교수를 놓쳤다는 의미이므로gyosu_passed를1증가합니다.

 

initGame에서 수정된 부분은 없습니다.

 

전체코드


전체코드

import pygame, random
from time import sleep

WHITE=(255,255,255)
RED=(255,0,0)
pad_width=740
pad_height=370
background_width=740
gyosu_width=110
aircraft_width=90
aircraft_height=55

gyosu_width=110
gyosu_height=67
c1_width=140
c1_height=60
c2_width=86
c2_height=60

def drawScore(count):
    global gamepad

    font=pygame.font.SysFont(None,25)
    text=font.render('Passed Gyosu:'+str(count),True,WHITE)
    gamepad.blit(text,(0,0))

def gameOver():
    global gamepad
    dispMessage('RETAKE')

def textObj(text,font):
    textSurface=font.render(text,True,RED)
    return textSurface,textSurface.get_rect()

def dispMessage(text):
    global gamepad

    largeText=pygame.font.Font('freesansbold.ttf',115)
    TextSurf,TextRect=textObj(text,largeText)
    TextRect.center=((pad_width/2),(pad_height/2))
    gamepad.blit(TextSurf,TextRect)
    pygame.display.update()
    sleep(2)
    runGame()

def crash():
    global gamepad
    dispMessage('RETAKE')


def drawObject(obj,x,y):
    global gamepad
    gamepad.blit(obj,(x,y))


def runGame():
    global gamepad, clock, aircraft, background1,background2
    global gyosu,cs,snow,boom

    isShotGyosu=False
    boom_count=0

    gyosu_passed=0

    snow_xy=[]

    x=pad_width*0.05
    y=pad_height*0.8
    y_change=0

    background1_x=0
    background2_x=background_width

    gyosu_x=pad_width
    gyosu_y=random.randrange(0,pad_height)

    c_x=pad_width
    c_y=random.randrange(0,pad_height)
    random.shuffle(cs)
    c=cs[0]

    crashed=False
    while not crashed:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                crashed=True
            if event.type==pygame.KEYDOWN:
                if event.key==pygame.K_UP:
                    y_change=-5
                elif event.key==pygame.K_DOWN:
                    y_change=5

                elif event.key==pygame.K_LCTRL:
                    snow_x=x+aircraft_width
                    snow_y=y+aircraft_height/2
                    snow_xy.append([snow_x,snow_y])
                elif event.key==pygame.K_SPACE:
                    sleep(5)
            if event.type==pygame.KEYUP:
                if event.key==pygame.K_UP or event.key==pygame.K_DOWN:
                    y_change=0
        #게임패드 클리어
        gamepad.fill(WHITE)
        #배경 그리기
        background1_x-=2
        background2_x-=2

        if background1_x==-background_width:
            background1_x=background_width

        if background2_x==-background_width:
            background2_x=background_width

        drawObject(background1,background1_x,0)
        drawObject(background2,background2_x,0)

        drawScore(gyosu_passed)
        if gyosu_passed>2:
            gameOver()
        #비행기(눈송)위치
        y += y_change
        if y<0:
            y=0
        elif y>pad_height-aircraft_height:
            y=pad_height-aircraft_height
        #교수 위치
        gyosu_x-=7
        if gyosu_x<=0:
            gyosu_passed+=1
            gyosu_x=pad_width
            gyosu_y=random.randrange(0,pad_height)
        #C학점 위치
        if c[1]==None: #수정됨
            c_x-=30
        else:
            c_x-=15

        if c_x<=0:
            c_x=pad_width
            c_y=random.randrange(0,pad_height)
            random.shuffle(cs)
            c=cs[0]
        #눈 위치
        if len(snow_xy)!=0:
            for i,sxy in enumerate(snow_xy):
                sxy[0]+=15
                snow_xy[i][0]=sxy[0]

                if sxy[0]>=gyosu_x:
                    if sxy[1]>gyosu_y and sxy[1]<gyosu_y+gyosu_height:
                        snow_xy.remove(sxy)
                        isShotGyosu=True

                if sxy[0]>=pad_width:
                    try:
                        snow_xy.remove(sxy)
                    except:
                        pass
        if x+aircraft_width>gyosu_x:
            if(y>gyosu_y and y<gyosu_y+gyosu_height)or\
                    (y+aircraft_height>gyosu_y and y+aircraft_height<gyosu_y+gyosu_height):
                crash()
        drawObject(aircraft, x, y)

        if c[1]!=None:
            if c[0]==0:
                c_width=c1_width
                c_height=c1_height
            elif c[0]==1:
                c_width=c2_width
                c_height=c2_height

            if x+aircraft_width>c_x:
                if(y>c_y and y<c_y+c_height)or\
                        (y+aircraft_height>c_y and y+aircraft_height<c_y+c_height):
                    crash()


        if len(snow_xy)!=0:
            for sx,sy in snow_xy:
                drawObject(snow,sx,sy)

        if not isShotGyosu:
            drawObject(gyosu,gyosu_x,gyosu_y)
        else:
            drawObject(boom,gyosu_x,gyosu_y)
            boom_count+=1
            if boom_count>5:
                boom_count=0
                gyosu_x=pad_width
                gyosu_y=random.randrange(0,pad_height-gyosu_height)
                isShotGyosu=False

        if c[1]!=None:
            drawObject(c[1],c_x,c_y)

        pygame.display.update()
        clock.tick(60)

    pygame.quit()
    quit()
def initGame():
    global gamepad,clock,aircraft,background1,background2
    global gyosu, cs, snow,boom

    cs=[]

    pygame.init()
    gamepad=pygame.display.set_mode((pad_width,pad_height))
    pygame.display.set_caption('PyFlying')
    aircraft=pygame.image.load('noonsong.png')
    background1=pygame.image.load('background.png')
    background2=background1.copy()
    gyosu = pygame.image.load('gyosu.png')
    cs.append((0,pygame.image.load('c.png')))
    cs.append((1,pygame.image.load('c2.png')))
    boom=pygame.image.load('boom.png')

    for i in range(3):
        cs.append((i+2,None))

    snow=pygame.image.load('snow.png')

    clock=pygame.time.Clock()
    runGame()

if __name__=='__main__':
    initGame()

 

 

 

세미나 자료 출처: m.blog.naver.com/samsjang/220706335386