본문 바로가기

코딩과 교육/파이썬

파이썬으로 게임 만들기 튜토리얼

Julian Meyer 라는 13세 소년이 만든 pygame 에 대한 튜토리얼이다. 이 게임은 기본적인 슈팅게임의 기본을 담고 있다. 방법을 익혀두면 다양하게 게임을 만들거나 응용이 가능하다. Beginning Game Programming for Teens with Python 이라는 이름으로 과정과 소스가 모두 인터넷에 공개되어 있다. 2.7 버젼으로 된 것을 최신 버젼인 3.6 버젼으로 수정했고, 일부 코드를 이해하기 쉽게 고쳤다.



STEP 0. 파이썬 & pygame 설치, 그래픽파일과 음악파일 준비


이 글은 윈도우 사용자를 위해 쓰였다. 리눅스나 맥은 가지고 있지 않은 관계로 설치해보질 못했으면 과정이 크게 다르지 않으리라는 것은 알지만 여기에 그 내용을 기록하지 않는다. 또한 원글과는 달리 최신 버젼인 파이썬 3.6 을 설치하여 사용하였다. 파이게임은 이글을 쓰는 당시에 있는 버젼인 1.9.3 을 설치하였다. 파이게임의 설치방법은


pip install pygame


으로 설치한다. 혹시 파이썬 설치방법을 모르는 사람은 다른 글을 통해 익히기 바란다.(가까운 시일내 파이썬 기본 강의를 올릴 예정이며 그때 파이썬 설치에 대해서 자세히 기록할 예정)


게임에 사용되는 배경음악과 그래픽 파일은 여기에서 다운로드하여 사용한다. 


[음악과 그래픽 다운로드]


이후의 과정은 최대한 설명은 생략하고 소스 중심으로 방법만 기술한다.



STEP 1. 토끼 보이기


다음의 코드를 사용한다.


# 1 - Import library
import pygame
from pygame.locals import *

# 2 - Initialize the game
pygame.init()
width, height = 640, 480
screen=pygame.display.set_mode((width, height))

# 3 - Load images
player = pygame.image.load("resources/images/dude.png")

# 4 - keep looping through
while 1:
    # 5 - clear the screen before drawing it again
    screen.fill(0)
    # 6 - draw the screen elements
    screen.blit(player, (100,100))
    # 7 - update the screen
    pygame.display.flip()
    # 8 - loop through the events
    for event in pygame.event.get():
        # check if the event is the X button 
        if event.type==pygame.QUIT:
            # if it is quit the game
            pygame.quit() 
            exit(0) 




STEP 2. 장면추가하기


다음 내용을 #3의 마지막 부분에 넣는다.


grass = pygame.image.load("resources/images/grass.png")
castle = pygame.image.load("resources/images/castle.png")


다음 코드를 #6 의 처음 부분에 추가한다.


for x in range(int(width/grass.get_width()+1)): for y in range(int(height/grass.get_height()+1)): screen.blit(grass,(x*100,y*100)) screen.blit(castle,(0,30)) screen.blit(castle,(0,135)) screen.blit(castle,(0,240)) screen.blit(castle,(0,345 ))


STEP 3. 토끼전사가 움직이게 하기


#2 의 마지막 부분에 다음 코드를 넣는다.

keys = [False, False, False, False]
playerpos=[100,100]


#6 에서 

    screen.blit(player, (100,100))

screen.blit(player, playerpos)

으로 바꾼다.

#8의 마지막 부분에서 for event.type==pygame.QUIT: 블럭 다음에 아래 코드를 넣는다.


        if event.type == pygame.KEYDOWN:
            if event.key==K_w:
                keys[0]=True
            elif event.key==K_a:
                keys[1]=True
            elif event.key==K_s:
                keys[2]=True
            elif event.key==K_d:
                keys[3]=True
        if event.type == pygame.KEYUP:
            if event.key==pygame.K_w:
                keys[0]=False
            elif event.key==pygame.K_a:
                keys[1]=False
            elif event.key==pygame.K_s:
                keys[2]=False
            elif event.key==pygame.K_d:
                keys[3]=False


파일의 마지막 부분에 아래 내용을 추가한다.


    # 9 - Move player
    if keys[0]:
        playerpos[1]-=5
    elif keys[2]:
        playerpos[1]+=5
    if keys[1]:
        playerpos[0]-=5
    elif keys[3]:
        playerpos[0]+=5


여기까지 코드를 실행시켜보면 키보드로 WASD 를 누를때 고양이전사가 움직인다.


STEP 4. 고양이전사 방향전환하기


atan2() 라는 수학함수를 사용한다. atan2 함수는 math 라이브러리를 사용한다.


#1 의 끝 부분에 다음을 추가한다.


import math

#6 의 마지막줄을 다음 내용으로 교체(수정)한다.

    # 6.1 - Set player position and rotation
    position = pygame.mouse.get_pos()
    angle = math.atan2(position[1]-(playerpos[1]+32),position[0]-(playerpos[0]+26))
    playerrot = pygame.transform.rotate(player, 360-angle*57.29)
    playerpos1 = (playerpos[0]-playerrot.get_rect().width/2, playerpos[1]-playerrot.get_rect().height/2)
    screen.blit(playerrot, playerpos1) 


STEP 5. 발사 !


#2 의 마지막 부분에 다음 코드를 추가한다.

acc=[0,0]
arrows=[]


#3 의 마지막 부분에 화살 이미지를 넣는 코드를 추가한다.

arrow = pygame.image.load("resources/images/bullet.png")

#8 마지막 부분에 새로운 이벤트 핸들러를 추가한다.
        if event.type==pygame.MOUSEBUTTONDOWN:
            position=pygame.mouse.get_pos()
            acc[1]+=1
            arrows.append([math.atan2(position[1]-(playerpos1[1]+32),position[0]-(playerpos1[0]+26)),playerpos1[0]+32,playerpos1[1]+32])


#6.1 바로 다음에 아래 코드를 추가한다.

    # 6.2 - Draw arrows
    for bullet in arrows:
        index=0
        velx=math.cos(bullet[0])*10
        vely=math.sin(bullet[0])*10
        bullet[1]+=velx
        bullet[2]+=vely
        if bullet[1]<-64 or bullet[1]>640 or bullet[2]<-64 or bullet[2]>480:
            arrows.pop(index)
        index+=1
        for projectile in arrows:
            arrow1 = pygame.transform.rotate(arrow, 360-projectile[0]*57.29)
            screen.blit(arrow1, (projectile[1], projectile[2]))


STEP 6. 오소리 출현

#2 마지막 부분에 아래 코드 추가

badtimer=100
badtimer1=0
badguys=[[640,100]]
healthvalue=194


#3 마지막 부분에 다음 코드 추가

badguyimg1 = pygame.image.load("resources/images/badguy.png")
badguyimg=badguyimg1


#6.2 다음에 아래 코드 추가

    # 6.3 - Draw badgers
    if badtimer==0:
        badguys.append([640, random.randint(50,430)])
        badtimer=100-(badtimer1*2)
        if badtimer1>=35:
            badtimer1=35
        else:
            badtimer1+=5
    index=0
    for badguy in badguys:
        if badguy[0]<-64:
            badguys.pop(index)
        badguy[0]-=7
        index+=1
    for badguy in badguys:
        screen.blit(badguyimg, badguy)


랜덤함수 사용을 위해서 다음 코드를 #1 의 끝 부분에 추가

import random

#4 의 while 문 바로 다음에 badtimer 를 감소시키는 코드 추가
    badtimer-=1

#6.3 의 첫번째 for 반복문 안에서 index+=1 바로 앞에 아래 코드를 추가
        # 6.3.1 - Attack castle
        badrect=pygame.Rect(badguyimg.get_rect())
        badrect.top=badguy[1]
        badrect.left=badguy[0]
        if badrect.left<64:
            healthvalue -= random.randint(5,20)
            badguys.pop(index)
        # 6.3.3 - Next bad guy


STEP 7. 오소리와 화살이 만날때


#6.3.1 아래에 다음 코드를 추가

        #6.3.2 - Check for collisions
        index1=0
        for bullet in arrows:
            bullrect=pygame.Rect(arrow.get_rect())
            bullrect.left=bullet[1]
            bullrect.top=bullet[2]
            if badrect.colliderect(bullrect):
                acc[0]+=1
                badguys.pop(index)
                arrows.pop(index1)
            index1+=1


STEP 8. 체력바와 시간 나타내기


#7 의 시작전에 아래 코드를 추가

# 6.4 - Draw clock font = pygame.font.Font(None, 24) survivedtext = font.render(str(int(90000-pygame.time.get_ticks()))+":"+str(int((90000-pygame.time.get_ticks())/1000%60)).zfill(2), True, (0,0,0)) textRect = survivedtext.get_rect() textRect.topright=[635,5] screen.blit(survivedtext, textRect)


#3 의 끝부분에 다음 코드를 추가

healthbar = pygame.image.load("resources/images/healthbar.png")
health = pygame.image.load("resources/images/health.png")


#6.4 다음에 아래 코드를 추가

    # 6.5 - Draw health bar
    screen.blit(healthbar, (5,5))
    for health1 in range(healthvalue):
        screen.blit(health, (health1+8,8))


STEP 9. WIN / LOSE


아래 코드를 마지막 부분에 추가

    #10 - Win/Lose check
    if pygame.time.get_ticks()>=90000:
        running=0
        exitcode=1
    if healthvalue<=0:
        running=0
        exitcode=0
    if acc[1]!=0:
        accuracy=acc[0]*1.0/acc[1]*100
    else:
        accuracy=0
# 11 - Win/lose display        
if exitcode==0:
    pygame.font.init()
    font = pygame.font.Font(None, 24)
    text = font.render("Accuracy: "+str(accuracy)+"%", True, (255,0,0))
    textRect = text.get_rect()
    textRect.centerx = screen.get_rect().centerx
    textRect.centery = screen.get_rect().centery+24
    screen.blit(gameover, (0,0))
    screen.blit(text, textRect)
else:
    pygame.font.init()
    font = pygame.font.Font(None, 24)
    text = font.render("Accuracy: "+str(accuracy)+"%", True, (0,255,0))
    textRect = text.get_rect()
    textRect.centerx = screen.get_rect().centerx
    textRect.centery = screen.get_rect().centery+24
    screen.blit(youwin, (0,0))
    screen.blit(text, textRect)
while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit(0)
    pygame.display.flip()


#3 의 마지막 부분에 아래 코드를 추가

gameover = pygame.image.load("resources/images/gameover.png")
youwin = pygame.image.load("resources/images/youwin.png")


#4의 아래 내용을 수정한다.

[수정전]

# 4 - keep looping through
while 1:
    badtimer-=1

[수정후]

# 4 - keep looping through
running = 1
exitcode = 0
while running:
    badtimer-=1


STEP 10. 배경음악과 효과음악


#2 마지막 부분에 추가

pygame.mixer.init()

#3의 마지막 부분에 추가
# 3.1 - Load audio
hit = pygame.mixer.Sound("resources/audio/explode.wav")
enemy = pygame.mixer.Sound("resources/audio/enemy.wav")
shoot = pygame.mixer.Sound("resources/audio/shoot.wav")
hit.set_volume(0.05)
enemy.set_volume(0.05)
shoot.set_volume(0.05)
pygame.mixer.music.load('resources/audio/moonlight.wav')
pygame.mixer.music.play(-1, 0.0)
pygame.mixer.music.set_volume(0.25)


#6.3.1 의 if badrect.left<64: 다음에 아래 코드 추가

# section 6.3.1 after if badrect.left<64:
hit.play()


#6.3.2 의 if badrect.colliderect(bullrect): 다음에 아래 코드 추가

# section 6.3.2 after if badrect.colliderect(bullrect):
enemy.play()


#8 의 if event.type==pygame.MOUSEBUTTONDOWN: 다음에 아래 코드 추가

# section 8, after if event.type==pygame.MOUSEBUTTONDOWN:
shoot.play()