Pygame 으로 Flappy Bird 만들기

PythonBeginner
지금 연습하기

This tutorial is from open-source community. Access the source code

소개

이 프로젝트에서는 Pygame 라이브러리를 사용하여 간단한 Flappy Bird 게임을 만드는 코드를 관리 가능한 단계로 나눌 것입니다. 이 단계를 따르면 게임을 점진적으로 구축하는 방법을 배울 수 있습니다. 각 단계에는 간략한 설명, 코드 블록 및 주석이 포함되어 게임을 이해하고 구현하는 데 도움이 됩니다. 시작해 봅시다!

👀 미리보기

Flappy Bird game preview

🎯 작업

이 프로젝트에서 다음을 배우게 됩니다.

  • Flappy Bird 게임을 위한 프로젝트 파일을 설정하는 방법
  • 게임의 시작 애니메이션을 표시하는 방법
  • Flappy Bird 의 주요 게임 로직을 구현하는 방법
  • 플레이어가 졌을 때 게임 오버 화면을 표시하는 방법
  • 게임을 위한 헬퍼 함수를 정의하는 방법

🏆 성과

이 프로젝트를 완료하면 다음을 수행할 수 있습니다.

  • Pygame 라이브러리를 사용하여 게임을 만들 수 있습니다.
  • 게임 루프, 충돌 및 애니메이션과 같은 게임 개발 개념을 이해할 수 있습니다.

프로젝트 파일 생성

먼저, Flappy Bird 게임을 위한 프로젝트 파일을 생성합니다.

cd ~/project
touch flappy.py
sudo pip install pygame

이 단계에서는 기본적인 프로젝트 구조를 설정하고 필요한 라이브러리를 가져옵니다. 또한 몇 가지 상수 (constants) 를 정의하고 초기 게임 에셋 (assets) 을 로드합니다.

from itertools import cycle
import random
import sys
import pygame
from pygame.locals import *

FPS = 30
SCREENWIDTH = 288
SCREENHEIGHT = 512
PIPEGAPSIZE = 100  ## 파이프 상하 간격
BASEY = SCREENHEIGHT * 0.79
## 이미지 및 hitmask 딕셔너리
IMAGES, HITMASKS = {}, {}

## 가능한 모든 플레이어 목록 (플랩의 3 가지 위치 튜플)
PLAYERS_LIST = (
    ## 빨간 새
    (
        "data/sprites/redbird-upflap.png",
        "data/sprites/redbird-midflap.png",
        "data/sprites/redbird-downflap.png",
    ),
    ## 파란 새
    (
        "data/sprites/bluebird-upflap.png",
        "data/sprites/bluebird-midflap.png",
        "data/sprites/bluebird-downflap.png",
    ),
    ## 노란 새
    (
        "data/sprites/yellowbird-upflap.png",
        "data/sprites/yellowbird-midflap.png",
        "data/sprites/yellowbird-downflap.png",
    ),
)

## 배경 목록
BACKGROUNDS_LIST = (
    "data/sprites/background-day.png",
    "data/sprites/background-night.png",
)

## 파이프 목록
PIPES_LIST = (
    "data/sprites/pipe-green.png",
    "data/sprites/pipe-red.png",
)
  • 게임을 만들기 위한 pygame, 무작위 요소를 생성하기 위한 random, 시스템 관련 함수를 위한 sys, 주요 상수를 위한 pygame.locals를 포함하여 게임에 필요한 라이브러리를 가져옵니다.
  • pygame.init()로 Pygame 을 초기화합니다.
  • FPS, SCREENWIDTH, SCREENHEIGHT, PIPEGAPSIZE, BASEY와 같은 상수를 정의하여 게임의 차원과 속도를 설정합니다.
  • 게임 에셋을 저장하기 위해 빈 딕셔너리 (IMAGESHITMASKS) 를 생성합니다.
  • 플레이어, 배경 및 파이프 에셋의 목록은 파일 경로를 사용하여 정의됩니다.

시작 화면 애니메이션 표시

이 단계에서는 Flappy Bird 게임의 시작 화면 애니메이션을 생성합니다.

def showWelcomeAnimation():
    """Flappy Bird 의 시작 화면 애니메이션을 표시합니다."""
    ## 화면에 blit 할 플레이어의 인덱스
    playerIndex = 0
    playerIndexGen = cycle([0, 1, 2, 1])
    ## 5 번째 반복마다 playerIndex 를 변경하는 데 사용되는 반복자
    loopIter = 0

    playerx = int(SCREENWIDTH * 0.2)
    playery = int((SCREENHEIGHT - IMAGES["player"][0].get_height()) / 2)

    messagex = int((SCREENWIDTH - IMAGES["message"].get_width()) / 2)
    messagey = int(SCREENHEIGHT * 0.12)

    basex = 0
    ## base 가 왼쪽으로 최대 이동할 수 있는 양
    baseShift = IMAGES["base"].get_width() - IMAGES["background"].get_width()

    ## 시작 화면에서 위아래 움직임을 위한 플레이어 shm
    playerShmVals = {"val": 0, "dir": 1}

    while True:
        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):
                ## 첫 번째 플랩 소리를 내고 mainGame 에 대한 값을 반환합니다.
                return {
                    "playery": playery + playerShmVals["val"],
                    "basex": basex,
                    "playerIndexGen": playerIndexGen,
                }

        ## playery, playerIndex, basex 조정
        if (loopIter + 1) % 5 == 0:
            playerIndex = next(playerIndexGen)
        loopIter = (loopIter + 1) % 30
        basex = -((-basex + 4) % baseShift)
        playerShm(playerShmVals)

        ## 스프라이트 그리기
        SCREEN.blit(IMAGES["background"], (0, 0))
        SCREEN.blit(
            IMAGES["player"][playerIndex], (playerx, playery + playerShmVals["val"])
        )
        SCREEN.blit(IMAGES["message"], (messagex, messagey))
        SCREEN.blit(IMAGES["base"], (basex, BASEY))

        pygame.display.update()
        FPSCLOCK.tick(FPS)
  • 시작 화면 애니메이션을 표시하는 역할을 하는 showWelcomeAnimation 함수를 정의합니다.
  • 이 함수는 애니메이션에 대한 변수를 설정하고 게임을 시작하기 위한 사용자 입력을 처리합니다.
  • 루프를 사용하여 애니메이션 프레임을 업데이트하고 게임을 시작하기 위한 사용자 입력을 확인합니다.
  • 애니메이션에는 새가 날갯짓하는 모습과 화면에 표시되는 메시지가 포함됩니다.
  • pygame.display.update() 함수는 디스플레이를 업데이트하는 데 사용되며, FPSCLOCK.tick(FPS)는 프레임 속도를 제어합니다.

메인 게임 로직 구현

이 단계에서는 Flappy Bird 게임의 메인 게임 로직을 구현합니다.

def mainGame(movementInfo):
    score = playerIndex = loopIter = 0
    playerIndexGen = movementInfo["playerIndexGen"]
    playerx, playery = int(SCREENWIDTH * 0.2), movementInfo["playery"]

    basex = movementInfo["basex"]
    baseShift = IMAGES["base"].get_width() - IMAGES["background"].get_width()

    ## 상단 및 하단 파이프에 추가할 새 파이프 2 개 가져오기
    newPipe1 = getRandomPipe()
    newPipe2 = getRandomPipe()

    ## 상단 파이프 목록
    upperPipes = [
        {"x": SCREENWIDTH + 200, "y": newPipe1[0]["y"]},
        {"x": SCREENWIDTH + 200 + (SCREENWIDTH / 2), "y": newPipe2[0]["y"]},
    ]

    ## 하단 파이프 목록
    lowerPipes = [
        {"x": SCREENWIDTH + 200, "y": newPipe1[1]["y"]},
        {"x": SCREENWIDTH + 200 + (SCREENWIDTH / 2), "y": newPipe2[1]["y"]},
    ]

    dt = FPSCLOCK.tick(FPS) / 1000
    pipeVelX = -128 * dt

    ## 플레이어 속도, 최대 속도, 하강 가속도, 플랩 시 가속도
    playerVelY = -9  ## Y 축을 따라가는 플레이어의 속도, 기본값은 playerFlapped 와 동일
    playerMaxVelY = 10  ## Y 축 최대 속도, 최대 하강 속도
    playerMinVelY = -8  ## Y 축 최소 속도, 최대 상승 속도
    playerAccY = 1  ## 플레이어의 하강 가속도
    playerRot = 45  ## 플레이어의 회전
    playerVelRot = 3  ## 각속도
    playerRotThr = 20  ## 회전 임계값
    playerFlapAcc = -9  ## 플랩 시 플레이어 속도
    playerFlapped = False  ## 플레이어가 플랩할 때 True

    while True:
        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):
                if playery > -2 * IMAGES["player"][0].get_height():
                    playerVelY = playerFlapAcc
                    playerFlapped = True

        ## 여기에서 충돌 확인
        crashTest = checkCrash(
            {"x": playerx, "y": playery, "index": playerIndex}, upperPipes, lowerPipes
        )
        if crashTest[0]:
            return {
                "y": playery,
                "groundCrash": crashTest[1],
                "basex": basex,
                "upperPipes": upperPipes,
                "lowerPipes": lowerPipes,
                "score": score,
                "playerVelY": playerVelY,
                "playerRot": playerRot,
            }

        ## 점수 확인
        playerMidPos = playerx + IMAGES["player"][0].get_width() / 2
        for pipe in upperPipes:
            pipeMidPos = pipe["x"] + IMAGES["pipe"][0].get_width() / 2
            if pipeMidPos <= playerMidPos < pipeMidPos + 4:
                score += 1

        ## playerIndex basex 변경
        if (loopIter + 1) % 3 == 0:
            playerIndex = next(playerIndexGen)
        loopIter = (loopIter + 1) % 30
        basex = -((-basex + 100) % baseShift)

        ## 플레이어 회전
        if playerRot > -90:
            playerRot -= playerVelRot

        ## 플레이어의 움직임
        if playerVelY < playerMaxVelY and not playerFlapped:
            playerVelY += playerAccY
        if playerFlapped:
            playerFlapped = False

            ## 임계값을 커버하기 위한 더 많은 회전 (가시적 회전으로 계산됨)
            playerRot = 45

        playerHeight = IMAGES["player"][playerIndex].get_height()
        playery += min(playerVelY, BASEY - playery - playerHeight)

        ## 파이프를 왼쪽으로 이동
        for uPipe, lPipe in zip(upperPipes, lowerPipes):
            uPipe["x"] += pipeVelX
            lPipe["x"] += pipeVelX

        ## 첫 번째 파이프가 화면 왼쪽 가장자리에 닿으려고 할 때 새 파이프 추가
        if 3 > len(upperPipes) > 0 and 0 < upperPipes[0]["x"] < 5:
            newPipe = getRandomPipe()
            upperPipes.append(newPipe[0])
            lowerPipes.append(newPipe[1])

        ## 화면 밖으로 나간 경우 첫 번째 파이프 제거
        if len(upperPipes) > 0 and upperPipes[0]["x"] < -IMAGES["pipe"][0].get_width():
            upperPipes.pop(0)
            lowerPipes.pop(0)

        ## 스프라이트 그리기
        SCREEN.blit(IMAGES["background"], (0, 0))

        for uPipe, lPipe in zip(upperPipes, lowerPipes):
            SCREEN.blit(IMAGES["pipe"][0], (uPipe["x"], uPipe["y"]))
            SCREEN.blit(IMAGES["pipe"][1], (lPipe["x"], lPipe["y"]))

        SCREEN.blit(IMAGES["base"], (basex, BASEY))
        ## 플레이어가 점수를 겹치도록 점수를 출력합니다.
        showScore(score)

        ## 플레이어 회전에는 임계값이 있습니다.
        visibleRot = playerRotThr
        if playerRot <= playerRotThr:
            visibleRot = playerRot

        playerSurface = pygame.transform.rotate(
            IMAGES["player"][playerIndex], visibleRot
        )
        SCREEN.blit(playerSurface, (playerx, playery))

        pygame.display.update()
        FPSCLOCK.tick(FPS)
  • mainGame 함수를 정의하며, 이 함수는 Flappy Bird 게임의 핵심 로직을 포함합니다.
  • 이 함수는 사용자 입력을 처리하고, 게임 상태를 업데이트하며, 충돌을 확인하고, 점수를 추적합니다.
  • 게임 루프는 지속적으로 실행되어 게임의 디스플레이와 로직을 업데이트합니다.
  • 플레이어 컨트롤은 키 이벤트 (스페이스 또는 위쪽 화살표) 를 통해 처리됩니다.
  • 이 함수는 또한 파이프 및 지면과의 충돌을 확인하고, 점수를 업데이트하며, 새의 애니메이션을 관리합니다.
  • 게임 루프는 플레이어가 충돌하거나 게임을 종료할 때까지 계속됩니다.

게임 오버 화면 표시

이 단계에서는 플레이어가 졌을 때 나타나는 게임 오버 화면을 생성합니다.

def showGameOverScreen(crashInfo):
    """플레이어를 추락시키고 게임 오버 이미지를 표시합니다."""
    score = crashInfo["score"]
    playerx = SCREENWIDTH * 0.2
    playery = crashInfo["y"]
    playerHeight = IMAGES["player"][0].get_height()
    playerVelY = crashInfo["playerVelY"]
    playerAccY = 2
    playerRot = crashInfo["playerRot"]
    playerVelRot = 7

    basex = crashInfo["basex"]

    upperPipes, lowerPipes = crashInfo["upperPipes"], crashInfo["lowerPipes"]

    while True:
        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):
                if playery + playerHeight >= BASEY - 1:
                    return

        ## 플레이어 y 이동
        if playery + playerHeight < BASEY - 1:
            playery += min(playerVelY, BASEY - playery - playerHeight)

        ## 플레이어 속도 변경
        if playerVelY < 15:
            playerVelY += playerAccY

        ## 파이프 충돌일 때만 회전
        if not crashInfo["groundCrash"]:
            if playerRot > -90:
                playerRot -= playerVelRot

        ## 스프라이트 그리기
        SCREEN.blit(IMAGES["background"], (0, 0))

        for uPipe, lPipe in zip(upperPipes, lowerPipes):
            SCREEN.blit(IMAGES["pipe"][0], (uPipe["x"], uPipe["y"]))
            SCREEN.blit(IMAGES["pipe"][1], (lPipe["x"], lPipe["y"]))

        SCREEN.blit(IMAGES["base"], (basex, BASEY))
        showScore(score)

        playerSurface = pygame.transform.rotate(IMAGES["player"][1], playerRot)
        SCREEN.blit(playerSurface, (playerx, playery))
        SCREEN.blit(IMAGES["gameover"], (50, 180))

        FPSCLOCK.tick(FPS)
        pygame.display.update()
  • showGameOverScreen 함수는 플레이어가 졌을 때 게임 오버 화면을 표시합니다.
  • 플레이어의 최종 점수를 표시하고, 사운드 효과를 재생하며, 플레이어가 스페이스 또는 위쪽 화살표를 눌러 게임을 다시 시작할 때까지 기다립니다.
  • 애니메이션에는 새가 땅에 떨어지는 모습과 화면에 표시되는 게임 오버 메시지가 포함됩니다.

헬퍼 함수 정의

이 단계에서는 게임에서 사용되는 헬퍼 함수를 정의합니다.

def playerShm(playerShm):
    """playerShm['val'] 의 값을 8 과 -8 사이에서 진동시킵니다."""
    if abs(playerShm["val"]) == 8:
        playerShm["dir"] *= -1

    if playerShm["dir"] == 1:
        playerShm["val"] += 1
    else:
        playerShm["val"] -= 1


def getRandomPipe():
    """무작위로 생성된 파이프를 반환합니다."""
    ## 상단 및 하단 파이프 사이의 간격 y
    gapY = random.randrange(0, int(BASEY * 0.6 - PIPEGAPSIZE))
    gapY += int(BASEY * 0.2)
    pipeHeight = IMAGES["pipe"][0].get_height()
    pipeX = SCREENWIDTH + 10

    return [
        {"x": pipeX, "y": gapY - pipeHeight},  ## 상단 파이프
        {"x": pipeX, "y": gapY + PIPEGAPSIZE},  ## 하단 파이프
    ]


def showScore(score):
    """화면 중앙에 점수를 표시합니다."""
    scoreDigits = [int(x) for x in list(str(score))]
    totalWidth = 0  ## 인쇄할 모든 숫자의 총 너비

    for digit in scoreDigits:
        totalWidth += IMAGES["numbers"][digit].get_width()

    Xoffset = (SCREENWIDTH - totalWidth) / 2

    for digit in scoreDigits:
        SCREEN.blit(IMAGES["numbers"][digit], (Xoffset, SCREENHEIGHT * 0.1))
        Xoffset += IMAGES["numbers"][digit].get_width()


def checkCrash(player, upperPipes, lowerPipes):
    """플레이어가 지면 또는 파이프와 충돌하는 경우 True 를 반환합니다."""
    pi = player["index"]
    player["w"] = IMAGES["player"][0].get_width()
    player["h"] = IMAGES["player"][0].get_height()

    ## 플레이어가 지면에 충돌하는 경우
    if player["y"] + player["h"] >= BASEY - 1:
        return [True, True]
    else:
        playerRect = pygame.Rect(player["x"], player["y"], player["w"], player["h"])
        pipeW = IMAGES["pipe"][0].get_width()
        pipeH = IMAGES["pipe"][0].get_height()

        for uPipe, lPipe in zip(upperPipes, lowerPipes):
            ## 상단 및 하단 파이프 사각형
            uPipeRect = pygame.Rect(uPipe["x"], uPipe["y"], pipeW, pipeH)
            lPipeRect = pygame.Rect(lPipe["x"], lPipe["y"], pipeW, pipeH)

            ## 플레이어 및 상단/하단 파이프 히트마스크
            pHitMask = HITMASKS["player"][pi]
            uHitmask = HITMASKS["pipe"][0]
            lHitmask = HITMASKS["pipe"][1]

            ## 새가 상단 파이프 또는 하단 파이프와 충돌한 경우
            uCollide = pixelCollision(playerRect, uPipeRect, pHitMask, uHitmask)
            lCollide = pixelCollision(playerRect, lPipeRect, pHitMask, lHitmask)

            if uCollide or lCollide:
                return [True, False]

    return [False, False]


def pixelCollision(rect1, rect2, hitmask1, hitmask2):
    """두 객체가 단순히 사각형이 아닌 충돌하는지 확인합니다."""
    rect = rect1.clip(rect2)

    if rect.width == 0 or rect.height == 0:
        return False

    x1, y1 = rect.x - rect1.x, rect.y - rect1.y
    x2, y2 = rect.x - rect2.x, rect.y - rect2.y

    for x in range(rect.width):
        for y in range(rect.height):
            if hitmask1[x1 + x][y1 + y] and hitmask2[x2 + x][y2 + y]:
                return True
    return False


def getHitmask(image):
    """이미지의 알파를 사용하여 히트마스크를 반환합니다."""
    mask = []
    for x in range(image.get_width()):
        mask.append([])
        for y in range(image.get_height()):
            mask[x].append(bool(image.get_at((x, y))[3]))
    return mask
  • playerShm 함수는 playerShm["val"]의 값을 8 과 -8 사이에서 진동시킵니다. 이는 시작 화면에서 새를 위아래로 움직이는 데 사용됩니다.
  • getRandomPipe 함수는 무작위로 생성된 파이프를 반환합니다. 상단 및 하단 파이프 사이에 무작위 간격을 생성합니다.
  • showScore 함수는 화면 중앙에 점수를 표시합니다. IMAGES["numbers"] 목록을 사용하여 점수를 표시합니다.
  • checkCrash 함수는 플레이어가 지면 또는 파이프와 충돌하는 경우 True를 반환합니다. pixelCollision 함수를 사용하여 충돌을 확인합니다.
  • pixelCollision 함수는 두 객체가 단순히 사각형이 아닌 충돌하는지 확인합니다. getHitmask 함수를 사용하여 플레이어 및 파이프에 대한 히트마스크를 가져옵니다.
  • getHitmask 함수는 이미지의 알파를 사용하여 히트마스크를 반환합니다. image.get_at 함수를 사용하여 이미지의 각 픽셀의 알파 값을 가져옵니다.

메인 함수

이 단계에서는 게임을 초기화하고 게임 루프를 시작하는 메인 함수를 정의합니다.

def main():
    global SCREEN, FPSCLOCK
    pygame.init()
    FPSCLOCK = pygame.time.Clock()
    SCREEN = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))
    pygame.display.set_caption("Flappy Bird")

    ## 점수 표시를 위한 숫자 스프라이트
    IMAGES["numbers"] = (
        pygame.image.load("data/sprites/0.png").convert_alpha(),
        pygame.image.load("data/sprites/1.png").convert_alpha(),
        pygame.image.load("data/sprites/2.png").convert_alpha(),
        pygame.image.load("data/sprites/3.png").convert_alpha(),
        pygame.image.load("data/sprites/4.png").convert_alpha(),
        pygame.image.load("data/sprites/5.png").convert_alpha(),
        pygame.image.load("data/sprites/6.png").convert_alpha(),
        pygame.image.load("data/sprites/7.png").convert_alpha(),
        pygame.image.load("data/sprites/8.png").convert_alpha(),
        pygame.image.load("data/sprites/9.png").convert_alpha(),
    )

    ## 게임 오버 스프라이트
    IMAGES["gameover"] = pygame.image.load("data/sprites/gameover.png").convert_alpha()
    ## 시작 화면용 메시지 스프라이트
    IMAGES["message"] = pygame.image.load("data/sprites/message.png").convert_alpha()
    ## 기본 (지면) 스프라이트
    IMAGES["base"] = pygame.image.load("data/sprites/base.png").convert_alpha()

    while True:
        ## 무작위 배경 스프라이트 선택
        randBg = random.randint(0, len(BACKGROUNDS_LIST) - 1)
        IMAGES["background"] = pygame.image.load(BACKGROUNDS_LIST[randBg]).convert()

        ## 무작위 플레이어 스프라이트 선택
        randPlayer = random.randint(0, len(PLAYERS_LIST) - 1)
        IMAGES["player"] = (
            pygame.image.load(PLAYERS_LIST[randPlayer][0]).convert_alpha(),
            pygame.image.load(PLAYERS_LIST[randPlayer][1]).convert_alpha(),
            pygame.image.load(PLAYERS_LIST[randPlayer][2]).convert_alpha(),
        )

        ## 무작위 파이프 스프라이트 선택
        pipeindex = random.randint(0, len(PIPES_LIST) - 1)
        IMAGES["pipe"] = (
            pygame.transform.flip(
                pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(), False, True
            ),
            pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(),
        )

        ## 파이프용 히트마스크
        HITMASKS["pipe"] = (
            getHitmask(IMAGES["pipe"][0]),
            getHitmask(IMAGES["pipe"][1]),
        )

        ## 플레이어용 히트마스크
        HITMASKS["player"] = (
            getHitmask(IMAGES["player"][0]),
            getHitmask(IMAGES["player"][1]),
            getHitmask(IMAGES["player"][2]),
        )

        movementInfo = showWelcomeAnimation()
        crashInfo = mainGame(movementInfo)
        showGameOverScreen(crashInfo)
  • main 함수는 게임을 초기화하고, 디스플레이를 설정하며, 게임 루프를 시작합니다.
  • 이미지와 같은 게임 에셋을 로드하고, 배경, 플레이어 및 파이프 스프라이트를 무작위로 선택합니다.
  • 게임 루프는 시작 애니메이션, 메인 게임 및 게임 오버 화면을 처리합니다.
  • 플레이어가 게임을 종료하거나 창을 닫을 때까지 게임은 계속 루프됩니다.

게임 실행

이 단계에서는 Flappy Bird 게임을 실행합니다.

if __name__ == "__main__":
    main()
  • __name__ == "__main__" 조건은 현재 모듈이 자체적으로 실행되는지 또는 다른 모듈에 의해 가져와지는지 확인합니다.
  • 현재 모듈이 자체적으로 실행되는 경우, main 함수가 호출되어 게임을 시작합니다.

모든 단계를 완료했으면 다음 명령을 사용하여 Flappy Bird 게임을 실행할 수 있습니다.

cd ~/project
python flappy.py

Flappy Bird game screenshot

요약

이 프로젝트에서는 Flappy Bird 게임 코드를 여러 단계로 나누고 각 단계에 대한 설명을 제공했습니다. 기본적인 게임 구조를 만들고, 플레이어 입력을 처리하고, 게임 상태를 업데이트하고, 충돌을 확인하고, 게임 화면을 표시하는 방법을 배웠습니다. 이제 제공된 코드를 사용하여 Flappy Bird 게임을 실행하고 플레이할 수 있습니다. 게임 개발 여정을 즐기세요!

✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습