🐸

003 개리 뛰기 및 점프 구현

1. 실행 코드

import pygame as pg # 이미지 초기화 def 스프라이트_생성(이미지): 스프라이트 = pg.sprite.Sprite() 스프라이트.image = 이미지 스프라이트.rect = 스프라이트.image.get_rect() return 스프라이트 pg.init() # 게임 기본 설정 실행여부 = True 화면가로길이, 화면세로길이 = 800, 450 화면 = pg.display.set_mode([화면가로길이, 화면세로길이]) pg.display.set_caption('동족을 노역장에서 구출하라!') 배경이미지 = pg.image.load('img/배경.png') 배경이미지 = pg.transform.scale(배경이미지, (화면가로길이, 화면세로길이)) 개리점프이미지 = pg.image.load('img/개리-뛰는-모습5(점프).png') 개리점프이미지 = pg.transform.scale(개리점프이미지, (100, 100)) 개리착지이미지 = pg.image.load('img/개리-뛰는-모습6(착지).png') 개리착지이미지 = pg.transform.scale(개리착지이미지, (100, 100)) 개리뛰기이미지리스트 = [pg.image.load(f'img/개리-뛰는-모습{인덱스}.png') for 인덱스 in range(1, 5)] for 인덱스 in range(len(개리뛰기이미지리스트)): 개리뛰기이미지리스트[인덱스] = pg.transform.scale(개리뛰기이미지리스트[인덱스], (100, 100)) 개리스프라이트 = 스프라이트_생성(개리뛰기이미지리스트[0]) 돌이미지 = pg.image.load('img/돌.png') 돌이미지 = pg.transform.scale(돌이미지, (100, 100)) # 게임 요소 초기화 개리시작높이 = 255 개리뛰기상태 = 0 개리뛰는흐름 = 1 개리동작업데이트시간 = 0 점프기본속도 = 0.1 점프속도 = 점프기본속도 점프상태 = False 개리위치 = [70, 개리시작높이] 시계 = pg.time.Clock() while 실행여부: 화면.blit(배경이미지, (0, 0)) # 게임 시간 계산 경과시간 = 시계.tick(60) / 1000 개리스프라이트.rect.x, 개리스프라이트.rect.y = 개리위치[0], 개리위치[1] 화면.blit(개리스프라이트.image, 개리스프라이트.rect) 화면.blit(돌이미지, (500, 280)) # 개리 점프 if 점프상태: 개리스프라이트.image = 점프속도 > 0 and 개리점프이미지 or 개리착지이미지 개리위치[1] -= 점프속도 * 경과시간 * 1000 점프속도 -= 점프기본속도 * 경과시간 * 2 if 개리위치[1] >= 개리시작높이: 개리위치[1] = 개리시작높이 점프상태 = False 점프속도 = 점프기본속도 else: 개리동작업데이트시간 += 경과시간 if 개리동작업데이트시간 > 0.2: 개리동작업데이트시간 = 0 개리스프라이트.image = 개리뛰기이미지리스트[개리뛰기상태] 개리뛰기상태 += 개리뛰는흐름 if 개리뛰기상태 == len(개리뛰기이미지리스트) - 1 or 개리뛰기상태 == 0: 개리뛰는흐름 *= -1 for 이벤트 in pg.event.get(): if 이벤트.type == pg.QUIT: 실행여부 = False elif 이벤트.type == pg.KEYDOWN: if 이벤트.key == pg.K_SPACE and not 점프상태: 점프상태 = True pg.display.update() pg.display.quit()

2. 상세 설명

2-1. 구현 방식(정리)

  1. 스프라이트_생성 함수를 만들고 개리의 랜더링 객체를 스프라이트로 만들어 줍니다.
  1. 각 모션들을 로드합니다.(달리기는 리스트에 점프와 착지는 별도)
  1. 달리기 모션에 필요한 요소(개리뛰는상태, 개리뛰는흐름)를 만들고 달리기 모션을 구현합니다.
  1. 프레임에 따라 속도도 다르고 너무 빠르므로 개리동작업데이트시간과 시계 객체를 만들어 프레임 동기적으로 모션이 바뀌도록 합니다.
  1. 점프상태를 만들어 점프할 때와 아닐 때를 구분한 후 점프에 필요한 요소(점프기본속도, 점프속도, 개리 위치)를 생성합니다.(이 때 점프기본속도는 임의로 조정합니다.)
  1. 점프 속도에 따라 이미지를 변경되도록 하고 프레임에 따라 점프 속도와 위치가 조정되도록 합니다.
      • 중력 가속도를 구현하기 위한 방식에는 다음과 같은 2가지 방식이 있습니다.
    1. 실제 중력 가속도(일반적으로 9.8)와 적분을 활용하여 가속도와 좌표의 상관 관계를 구하여 식을 대입하는 방식
    2. 가속이 없는 일반적으로 구현하기 쉬운 방식에서 점프_속도를 조금씩 줄여나가는 방식 → 이 방식을 게임에 적용하였습니다.
  1. 점프를 하고 시작 위치보다 밑으로 가면 시작 위치로 변경하고 달리기 모션으로 전환합니다.

2-2. 구현

def 스프라이트_생성(이미지): 스프라이트 = pg.sprite.Sprite() 스프라이트.image = 이미지 스프라이트.rect = 스프라이트.image.get_rect() return 스프라이트
  • 스프라이트 생성 시 기본적으로 받는 크기와 Surface 객체는 동등하게 생성하기 때문에 함수 처리합니다.
  • 스프라이트 기본 설명
    • pg.sprite.Sprite()로 생성합니다.
    • Sprite.image : pg.image.load(경로) 함수 등의 방식으로 가져온 Surface객체를 저장합니다.
    • Sprite.rect : 스프라이트 객체의 크기와 좌표를 저장합니다.
      • Rect 객체의 좌표는 top, left, x, y 등의 여러 속성이 있습니다.
💡
스프라이트를 사용하는 가장 큰 이유는 각 객체들에 대한 다양한 충돌이벤트를 처리할 수 있기 때문입니다. 기존에 사용했던 collidepoint를 통한 객체의 rect에 대한 처리보다 더욱 쉽게 접근할 수 있습니다.
 
개리점프이미지 = pg.image.load('img/개리-뛰는-모습5(점프).png') 개리점프이미지 = pg.transform.scale(개리점프이미지, (100, 100)) 개리착지이미지 = pg.image.load('img/개리-뛰는-모습6(착지).png') 개리착지이미지 = pg.transform.scale(개리착지이미지, (100, 100)) 개리뛰기이미지리스트 = [pg.image.load(f'img/개리-뛰는-모습{인덱스}.png') for 인덱스 in range(1, 5)] for 인덱스 in range(len(개리뛰기이미지리스트)): 개리뛰기이미지리스트[인덱스] = pg.transform.scale(개리뛰기이미지리스트[인덱스], (100, 100)) 개리스프라이트 = 스프라이트_생성(개리뛰기이미지리스트[0])
  • 각 상황에 맞는 이미지를 가져옵니다.
  • for문으로 개리뛰기이미지리스트에 있는 요소들을 scale함수를 통하여 사이즈를 100 x 100으로 설정합니다.
  • 개리스프라이트를 앞서 만든 함수를 통해 만듭니다. 이때, 리스트[0]으로 생성한 이유는 먼저 개리의 이미지를 대기인 상태로 만들기 위함입니다.
 
# 게임 요소 초기화 개리시작높이 = 255 개리뛰기상태 = 0 개리뛰는흐름 = 1 개리동작업데이트시간 = 0 점프기본속도 = 0.1 점프속도 = 점프기본속도 점프상태 = False 개리위치 = [70, 개리시작높이] 시계 = pg.time.Clock()
게임이 시작되면 개리는 크게 달리는 모션과 점프 및 착지 모션으로 분류됩니다.
  • 개리뛰는상태 : 개리뛰기이미지리스트의 인덱스입니다.
  • 개리뛰는흐름 : 달리기 이미지 모션이 1-2-3-4-3-2-1 순이므로 4까지 올라간 다음은 내려가야 합니다. 따라서 모션을 1-2-3-4의 정방향으로 바꾸어야 하는지 또는 4-3-2-1 순의 역방향으로 바꾸어야하는지 결정하기 위해서 개리뛰는상태의 연산을 도와주는 변수입니다.
  • 개리동작업데이트시간 : 프레임별로 이미지를 바꾸게 되면 캐릭터의 속도가 너무 빠르게 보입니다. 그래서 특정시간마다 이미지를 바꾸어 주어 어떤 프레임에서도 자연스러운 동작을 보여주기 위하여 사용됩니다.
  • 점프기본속도 : 점프의 최대 속도(힘)을 나타내는 변수입니다.
  • 점프속도 : 중력가속도의 원리를 이용하기 위해서 올라가는 동안 서서히 느려지게 되도록 합니다.
  • 점프상태 : 달리기 상태와 점프상태를 구분하는 변수입니다.
  • 개리위치 : 점프시 개리 위치가 조정될 수 있도록 구성한 리스트입니다. 스프라이트의 rect 객체에서의 좌표는 정수단위의 연산밖에 할 수 없으므로 시간에 의한 동기적 연산을 위해서는 소수 연산이 필요하여 사용합니다.
  • 시계 : 프레임을 고정하기 위해 사용합니다. 프레임을 고정하는 이유는 화면이 자연스럽게(부드럽게) 업데이트 되기 위함입니다.
 
경과시간 = 시계.tick(60) / 1000 개리스프라이트.rect.x, 개리스프라이트.rect.y = 개리위치[0], 개리위치[1] 화면.blit(개리스프라이트.image, 개리스프라이트.rect)
  • 시계는 Clock 클래스의 객체이고 Clock.tick(milli) 함수는 milli(밀리)초를 1프레임으로 계산하고 그 시간값을 반환하는 함수입니다.
  • 경과시간 : 한 프레임의 시간을 초 단위로 계산한 함수입니다.
  • 개리위치리스트를 활용하여 개리스프라이트의 x,y좌표를 설정하고 화면에 추가합니다.
 
if 점프상태: 개리스프라이트.image = 점프속도 > 0 and 개리점프이미지 or 개리착지이미지 개리위치[1] -= 점프속도 * 경과시간 * 1000 점프속도 -= 점프기본속도 * 경과시간 * 2 if 개리위치[1] >= 개리시작높이: 개리위치[1] = 개리시작높이 점프상태 = False 점프속도 = 점프기본속도 else: 개리동작업데이트시간 += 경과시간 if 개리동작업데이트시간 > 0.2: 개리동작업데이트시간 = 0 개리스프라이트.image = 개리뛰기이미지리스트[개리뛰기상태] 개리뛰기상태 += 개리뛰는흐름 if 개리뛰기상태 == len(개리뛰기이미지리스트) - 1 or 개리뛰기상태 == 0: 개리뛰는흐름 *= -1
💡
pygame에서의 좌표는 화면 왼쪽 위를 0, 0 기준으로 x좌표는 오른쪽, y좌표는 아래쪽으로 좌표값이 증가합니다. 따라서 캐릭터가 위로 가는 모션은 y좌표를 빼는 형태로 설정해야합니다.
  • 점프 중일 때
    • 점프 속도를 기준으로 이미지를 조정합니다
      • 점프속도 > 0 이면 점프 이미지를 아니면 착지이미지를 사용합니다.(3항 연산자를 사용하였습니다.)
    • 계산 방식 : 위치와 힘의 조정은 프레임을 기준으로 적용합니다.
      • 점프속도을 기준으로 개리의 위치(y)를 조정합니다.
      • 점프 속도를 조정합니다.(점프 속도가 음수가 되었을 때 떨어지게 됩니다.)
    • 시작 높이까지 떨어지면 기본 높이로
    • 개리위치[1] -= 점프속도 * 지난시간 * 1000 → 원래는 점프속도만 빼주나 지난시간 * 1000이라는 식이 1초를 1로 계산하는 방식이기 때문에 사용합니다.
  • 달리는 중일 때
    • 0.2초를 기준으로 이미지를 변경합니다.(개리동작업데이트시간의 기준이 짧을 수록 모션이 좀 더 빨리 변화합니다.)
    • 모션의 변화가 1-2-3-4-3-2-1이므로 이를 나누면 1-2-3-4와 4-3-2-1이 됩니다. 즉 달리기 모션의 흐름을 각 인덱스의 끝에 다다랐을 때 변경해줌으로써 인덱스를 더해야 할지 빼야할지를 결정합니다.
 
elif 이벤트.type == pg.KEYDOWN: if 이벤트.key == pg.K_SPACE and not 점프상태: 점프상태 = True
  • Space 키가 눌리고 점프상태가 아닐 때 점프를 할 수 있도록 이벤트를 줍니다.

3. 실행 화면

notion imagenotion image
 
notion imagenotion image
 
notion imagenotion image