본문 바로가기
개발(Develop)/파이썬(Python)

[Python] pygame 모듈 게임 만들기(2) / 파이썬 캐릭터 이동 및 이동속도 조절 / fps 설정 pygame.time.Clock() .tick() 파이썬 좌표 설정

by 왁왁s 2022. 3. 4.

이전 글에 이어서 작성하도록 하겠다.

 

이전 글은 아래에 첨부할테니, 

혹시나 이전부터 참고하고자 한다면 이전 글을 참고해주길 바란다.\

 

[Python] pygame 모듈 / 파이썬 게임 배경 및 캐릭터 설정하기

 

[Python] pygame 모듈 / 파이썬 게임 배경 및 캐릭터 설정하기 /기본 구조, 초기화, import, 메인루프, ima

파이썬에는 여러 모듈을 불러와 사용할 수가 있다. 그 중에 'pygame'이라는 python을 통해 게임을 만들 수 있도록 지원해주는 모듈을 가져와 사용해보려고 한다. 1. pygame 모듈 설치 및 환경 설정 우선

parkjh7764.tistory.com

 


 

그러면 이제는 이벤트를 설정할 것이다.

 

1. pygame 이벤트 설정하기

 

(1) 키보드의 키가 눌렸을 때 이벤트

 

우선 pygame의 키보드 이벤트 타입에는 무엇이 있는지 알아야 한다.

아래의 표로 남겨놓을 것인데, 이것 이외에 다른 이벤트 타입들이 있다.

 

여기서 pygame.KEYDOWN 키가 눌렸을 때의 이벤트를 작성할 것이다.

 

이벤트 타입 이벤트 동작 이벤트 속성
pygame.QUIT 윈도우의 x 버튼, 창 닫기 버튼 눌림 none
pygame.ACTIVEEVENT 마우스가 화면에 들어가거나 나가면 발생


gain : 0 - 마우스가 화면에 들어올때
gain : 1 - 마우스가 화면에서 나갔을 때


state : 1 - 창이 활성화
state : 2 - 창이 비활성화
state : 6 - 비활성화된 창이 활성화 될때
gain, state
pygame.KEYDOWN 키가 눌렸을 때 key, mod, unicode, scancode
pygame.KEYUP 키가 올라갔을 때 key, mod
pygame.MOUSEMOTION 마우스 움직일때 pos, rel, buttons
pygame.MOUSEBUTTONUP 마우스 버튼 뗄 때 pos, button
pygame.MOUSEBUTTONDOWN 마우스 버튼 눌렀을 때 pos, button

 

 


이벤트 루프에 if event.type == pygame.KEYDOWN: 을 추가해준다.

그리고 키가 눌렸을 때 어떤 키가 눌렸는 지를 조건문을 사용해서 표현을 해주는데,

pygame.K_LEFT 와 같이 방향키가 눌렸을 때 캐릭터의 이동을 설정해줄 것이다.

 

갑자기 pygame.K_LEFT 값이 나와서 당황했을텐데, 이는 공식 사이트에서 보도록 하자. 아래 링크를 달아두겠다.

#이벤트 루프
running = True #게임 진행 여부에 대한 변수 True : 게임 진행 중
while running:
    for event in pygame.event.get(): #이벤트의 발생 여부에 따른 반복문
        if event.type == pygame.QUIT: #창을 닫는 이벤트 발생했는가?
            running = False

        if event.type == pygame.KEYDOWN: #키보드의 키가 눌러졌을 경우
            if event.key == pygame.K_LEFT: #왼쪽 방향키가 눌렸을 때
                pass
            elif event.key == pygame.K_RIGHT: #오른쪽 방향키가 눌렸을 때
                pass
            elif event.key == pygame.K_DOWN: #아래쪽 방향키가 눌렸을 때
                pass
            elif evnet.key == pygame.K_UP: #위쪽 방향키가 눌렸을 때
                pass


    screen.blit(background, (0, 0)) #배경에 이미지 그려주고 위치 지정
    screen.blit(character, (character_x_pos - character_width / 2, character_y_pos - character_height)) #배경에 캐릭터 그려주기
    pygame.display.update()

 

 

아래의 pygame 공식 사이트에 들어가게 되면,

pygame 모듈의 key 값들을 보여준다.

 

pygame.key — pygame v2.1.1 documentation

Returns a sequence of boolean values representing the state of every key on the keyboard. Use the key constant values to index the array. A True value means that the button is pressed. Note Getting the list of pushed buttons with this function is not the p

web.archive.org

 

긁어서 오자면 아래와 같은 키 값들을 가진다.

Description을 보고 어떤 키 값을 가져오고 싶은 지 확인해보도록 하자.

pygame
Constant      ASCII   Description
---------------------------------
K_BACKSPACE   \b      backspace
K_TAB         \t      tab
K_CLEAR               clear
K_RETURN      \r      return
K_PAUSE               pause
K_ESCAPE      ^[      escape
K_SPACE               space
K_EXCLAIM     !       exclaim
K_QUOTEDBL    "       quotedbl
K_HASH        #       hash
K_DOLLAR      $       dollar
K_AMPERSAND   &       ampersand
K_QUOTE               quote
K_LEFTPAREN   (       left parenthesis
K_RIGHTPAREN  )       right parenthesis
K_ASTERISK    *       asterisk
K_PLUS        +       plus sign
K_COMMA       ,       comma
K_MINUS       -       minus sign
K_PERIOD      .       period
K_SLASH       /       forward slash
K_0           0       0
K_1           1       1
K_2           2       2
K_3           3       3
K_4           4       4
K_5           5       5
K_6           6       6
K_7           7       7
K_8           8       8
K_9           9       9
K_COLON       :       colon
K_SEMICOLON   ;       semicolon
K_LESS        <       less-than sign
K_EQUALS      =       equals sign
K_GREATER     >       greater-than sign
K_QUESTION    ?       question mark
K_AT          @       at
K_LEFTBRACKET [       left bracket
K_BACKSLASH   \       backslash
K_RIGHTBRACKET ]      right bracket
K_CARET       ^       caret
K_UNDERSCORE  _       underscore
K_BACKQUOTE   `       grave
K_a           a       a
K_b           b       b
K_c           c       c
K_d           d       d
K_e           e       e
K_f           f       f
K_g           g       g
K_h           h       h
K_i           i       i
K_j           j       j
K_k           k       k
K_l           l       l
K_m           m       m
K_n           n       n
K_o           o       o
K_p           p       p
K_q           q       q
K_r           r       r
K_s           s       s
K_t           t       t
K_u           u       u
K_v           v       v
K_w           w       w
K_x           x       x
K_y           y       y
K_z           z       z
K_DELETE              delete
K_KP0                 keypad 0
K_KP1                 keypad 1
K_KP2                 keypad 2
K_KP3                 keypad 3
K_KP4                 keypad 4
K_KP5                 keypad 5
K_KP6                 keypad 6
K_KP7                 keypad 7
K_KP8                 keypad 8
K_KP9                 keypad 9
K_KP_PERIOD   .       keypad period
K_KP_DIVIDE   /       keypad divide
K_KP_MULTIPLY *       keypad multiply
K_KP_MINUS    -       keypad minus
K_KP_PLUS     +       keypad plus
K_KP_ENTER    \r      keypad enter
K_KP_EQUALS   =       keypad equals
K_UP                  up arrow
K_DOWN                down arrow
K_RIGHT               right arrow
K_LEFT                left arrow
K_INSERT              insert
K_HOME                home
K_END                 end
K_PAGEUP              page up
K_PAGEDOWN            page down
K_F1                  F1
K_F2                  F2
K_F3                  F3
K_F4                  F4
K_F5                  F5
K_F6                  F6
K_F7                  F7
K_F8                  F8
K_F9                  F9
K_F10                 F10
K_F11                 F11
K_F12                 F12
K_F13                 F13
K_F14                 F14
K_F15                 F15
K_NUMLOCK             numlock
K_CAPSLOCK            capslock
K_SCROLLOCK           scrollock
K_RSHIFT              right shift
K_LSHIFT              left shift
K_RCTRL               right control
K_LCTRL               left control
K_RALT                right alt
K_LALT                left alt
K_RMETA               right meta
K_LMETA               left meta
K_LSUPER              left Windows key
K_RSUPER              right Windows key
K_MODE                mode shift
K_HELP                help
K_PRINT               print screen
K_SYSREQ              sysrq
K_BREAK               break
K_MENU                menu
K_POWER               power
K_EURO                Euro
K_AC_BACK             Android back button

 


 

(2) 키보드 방향키가 눌렸을 때 캐릭터 이동시키기

 

다시 돌아와서

해당 이벤트 루프 코드에서 pass 부분에 방향키를 눌렀을 때

캐릭터의 좌표이동을 설정해줄 것이다.

#이벤트 루프
running = True #게임 진행 여부에 대한 변수 True : 게임 진행 중
while running:
    for event in pygame.event.get(): #이벤트의 발생 여부에 따른 반복문
        if event.type == pygame.QUIT: #창을 닫는 이벤트 발생했는가?
            running = False

        if event.type == pygame.KEYDOWN: #키보드의 키가 눌러졌을 경우
            if event.key == pygame.K_LEFT: #왼쪽 방향키가 눌렸을 때
                pass
            elif event.key == pygame.K_RIGHT: #오른쪽 방향키가 눌렸을 때
                pass
            elif event.key == pygame.K_DOWN: #아래쪽 방향키가 눌렸을 때
                pass
            elif evnet.key == pygame.K_UP: #위쪽 방향키가 눌렸을 때
                pass


    screen.blit(background, (0, 0)) #배경에 이미지 그려주고 위치 지정
    screen.blit(character, (character_x_pos, character_y_pos)) #배경에 캐릭터 그려주기
    pygame.display.update()

 

그럼 위의 코드에서 

캐릭터 이동 좌표를 to_x, to_y 변수로 두고 키보드 방향키가 눌림에 따라

캐릭터의 좌표에서 빼거나 더해줄 것이다.

 

학교에서 수학시간에 배운 x, y좌표와는 다르다. 아래 사진을 참고하자 

왼쪽 상단이 (0, 0)이며, 

x좌표 기준으로 오른쪽은 +, 왼쪽은 -이고

y좌표 기준으로 위쪽은 -, 아래쪽은 +이다.

 

 

if event.key == pygame.K_LEFT:

    to_x -= 0.5

 

코드를 보면 왼쪽 방향키가 눌렸을 때 x좌표를 -0.5 만큼 빼서 왼쪽으로 이동시켜주는 것이다.

 

그리고 

if event.type == pygame.KEYUP 이란 조건문을 두어

사용자가 키보드에서 손을 뗐을 때, 캐릭터의 이동이 멈추어야 하므로

좌표 이동 변수 to_x, to_y를 0으로 설정해준다.

 

해당 좌표 이동 변수를 character_x_pos, character_y_pos에 더해줌으로써

캐릭터의 이동이 이루어지는 것이다.

 

#캐릭터 이동 좌표
to_x = 0
to_y = 0

#이벤트 루프
running = True #게임 진행 여부에 대한 변수 True : 게임 진행 중
while running:
    for event in pygame.event.get(): #이벤트의 발생 여부에 따른 반복문
        if event.type == pygame.QUIT: #창을 닫는 이벤트 발생했는가?
            running = False

        if event.type == pygame.KEYDOWN: #키보드의 키가 눌러졌을 경우
            if event.key == pygame.K_LEFT: #왼쪽 방향키가 눌렸을 때
                to_x -= 0.5
            elif event.key == pygame.K_RIGHT: #오른쪽 방향키가 눌렸을 때
                to_x += 0.5
            elif event.key == pygame.K_DOWN: #아래쪽 방향키가 눌렸을 때
                to_y += 0.5
            elif event.key == pygame.K_UP: #위쪽 방향키가 눌렸을 때
                to_y -= 0.5

        if event.type == pygame.KEYUP: #방향키를 뗐을 때 캐릭터 멈춤
            if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                to_x = 0
            elif event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                to_y = 0

    character_x_pos += to_x
    character_y_pos += to_y

 

 

그런데 위와 같이 코드를 작성하게 되면 

캐릭터를 이동 시키다보면 화면 밖으로 나가게 된다.

 

이때 조건문을 설정해주어 캐릭터가 화면 밖으로 나가지 않도록 해주어야 한다.

 

해당 조건문을 이벤트 루프 안에 작성해주어야 한다.

 

캐릭터 좌표와 스크린 좌표 값을 비교하여, 해당 값을 벗어났을 때

캐릭터 좌표의 값을 그 값에 머물게 함으로써 넘어가지 못 하는 듯한 느낌을 준다.

    #왼쪽, 오른쪽 경계 정하기
    if character_x_pos < 0:
        character_x_pos = 0

    elif character_x_pos > screen_width - character_width:
        character_x_pos = screen_width - character_width

    #위, 아래쪽 경계 정하기
    if character_y_pos < 0:
        character_y_pos = 0

    elif character_y_pos > screen_height - character_height:
        character_y_pos = screen_height - character_height

 

경계선을 설정해주면 아래와 같이

캐릭터가 스크린 밖으로 넘어가지 않는 것을 확인할 수 있다.

 

 


 

 

2. FPS 설정하기

 

fps는 frame per second의 약자로 주사율, 즉 초당 몇 번의 이미지를 나타내어 주는 지에 대한 척도이다.

 

fps를 10으로 설정하면 초당 10번의 화면을 출력한다는 의미이다.

해당 값이 높을수록 화면에 부드럽게 출력이 되지만 CPU를 많이 사용하게 됨으로

주로 10, 30, 60을 주로 쓰니 너무 높은 fps 설정은 하지 않는 것이 좋다.

 

fps의 설정은 pygame.time.Clock() 을 변수에 넣고 해당 변수의 메소드로 .tick()을 사용한다.

 

clock.tick(60)으로 설정을 하게 되면 1초당 60프레임을 화면에 보여주는 것이다.

숫자가 커질수록 캐릭터의 모션이 부드럽게 출력이 된다.

#FPS 설정
clock = pygame.time.Clock()

#이벤트 루프
running = True #게임 진행 여부에 대한 변수 True : 게임 진행 중
while running:
    dt = clock.tick(60) #초당 프레임 수 fps 설정
    for event in pygame.event.get(): #이벤트의 발생 여부에 따른 반복문
        if event.type == pygame.QUIT: #창을 닫는 이벤트 발생했는가?
            running = False
            
#아랫 부분 생략..

 

 

 

그런데, 여기서 한가지 문제가 발생한다.

 

캐릭터의 속도를 조절하는 것이 아닌

프레임만을 조절했을 때 한 번 방향키를 클릭할 때의

캐릭터의 이동 거리가 달라지게 된다.

 

왼쪽 : clock.tick(60) / 오른쪽 : clock.tick(10)

 

캐릭터의 속도가 같아지게 해줘야 한다. 예를 한번 들어보자.

 

캐릭터가 1초 동안 100만큼 이동을 해야 하는데, 

fps가 10일 때는 1초에 10번 동작을 하게 된다. 그렇다면 1번에 10만큼 이동을 해야 1초에 100만큼 이동할 수 있다.

 

fps가 50일 때는 1초에 50번 동작을 하게 된다. 그렇다면 1번에 2만큼 이동을 해야 1초에 100만큼 이동할 수 있다.

 

그렇다면 어떻게 해줘야 할까?

 

프레임이 달라졌을 때 캐릭터의 1번 클릭했을 때 이동 거리를 바꿔주면 된다.

 

-> 이렇게 해줘야 하는 이유는

컴퓨터가 프레임 수가 안 나올 때 캐릭터의 이동속도가 달라져 버리면 형평성이 떨어진다.

fps가 낮더라도 캐릭터의 이동 속도가 같아야 이동하는 거리가 같아진다.

 

 

아래와 같이 캐릭터가 이동하는 거리에 프레임 수를 곱해주면 된다.

 

그러면 캐릭터 프레임에 맞게 캐릭터가 이동속도를 가지게 된다.

60프레임에서는 to_x * 60

10프레임에서는 to_y * 10

 

프레임에 맞는 이동속도를 가지게 되어 같은 속도처럼 보인다.

    character_x_pos += to_x * dt
    character_y_pos += to_y * dt

 

아래를 참고하면 

10fps는 조금 끊기는 감이 있긴 하지만 같은 속도로 움직이게 된다.

 

왼쪽 : clock.tick(60) / 오른쪽 : clock.tick(10)

 

아래는 전체 코드이다. 

import pygame

pygame.init() #초기화 

#화면 크기 설정
screen_width = 480 #가로 크기
screen_height = 640 #세로 크기
screen = pygame.display.set_mode((screen_width, screen_height))

#화면 타이틀 설정
pygame.display.set_caption("HwanE Game")

#FPS 설정
clock = pygame.time.Clock()

#배경 이미지 불러오기
background = pygame.image.load("C:/Users/A/Desktop/PythonWorkSpace/pygame_basic/background.png")

#캐릭터 불러오기
character = pygame.image.load("C:/Users/A/Desktop/PythonWorkSpace/pygame_basic/character.png")
character_size = character.get_rect().size #캐릭터 이미지 사이즈 구하기
character_width = character_size[0] #캐릭터 가로 크기
character_height = character_size[1] #캐릭터 세로 크기
#캐릭터의 기준 좌표를 캐릭터의 왼쪽 상단으로 둔다.
character_x_pos = (screen_width / 2) - (character_width / 2) #화면 가로 절반의 중간에 위치. 좌우로 움직이는 변수
character_y_pos = screen_height - character_height #이미지가 화면 세로의 가장 아래 위치  

#캐릭터 이동 좌표
to_x = 0
to_y = 0

#캐릭터 이동 속도 변수
charcter_speed = 1

#이벤트 루프
running = True #게임 진행 여부에 대한 변수 True : 게임 진행 중
while running:
    dt = clock.tick(10) #초당 프레임 수 fps 설정
    for event in pygame.event.get(): #이벤트의 발생 여부에 따른 반복문
        if event.type == pygame.QUIT: #창을 닫는 이벤트 발생했는가?
            running = False

        if event.type == pygame.KEYDOWN: #키보드의 키가 눌러졌을 경우
            if event.key == pygame.K_LEFT: #왼쪽 방향키가 눌렸을 때
                to_x -= charcter_speed
            elif event.key == pygame.K_RIGHT: #오른쪽 방향키가 눌렸을 때
                to_x += charcter_speed
            elif event.key == pygame.K_DOWN: #아래쪽 방향키가 눌렸을 때
                to_y += charcter_speed
            elif event.key == pygame.K_UP: #위쪽 방향키가 눌렸을 때
                to_y -= charcter_speed

        if event.type == pygame.KEYUP: #방향키를 뗐을 때 캐릭터 멈춤
            if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                to_x = 0
            elif event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                to_y = 0

    character_x_pos += to_x * dt
    character_y_pos += to_y * dt

    #왼쪽, 오른쪽 경계 정하기
    if character_x_pos < 0:
        character_x_pos = 0

    elif character_x_pos > screen_width - character_width:
        character_x_pos = screen_width - character_width

    #위, 아래쪽 경계 정하기
    if character_y_pos < 0:
        character_y_pos = 0

    elif character_y_pos > screen_height - character_height:
        character_y_pos = screen_height - character_height

    screen.blit(background, (0, 0)) #배경에 이미지 그려주고 위치 지정
    screen.blit(character, (character_x_pos, character_y_pos)) #배경에 캐릭터 그려주기
    pygame.display.update()


#pygame 종료
pygame.quit()

댓글