개인 프로젝트 1: 미니 게임 만들기 3

hyelog·2022년 4월 27일
0

TIL

목록 보기
5/20
post-thumbnail

파이썬으로 미니 게임 만들기


pygame 미니게임
파이썬 라이브러리 pygame을 사용해 미니 게임 로직을 구현.
실제 작동까지 진행.


GAME: Annoying Ghost

  • 적이 랜덤하게 하강하는 슈팅 게임
  • 방향키로 캐릭터를 이동
  • 스페이스 바를 누르면 한정된 미사일을 사용
  • 화면 상단에 슈팅 성공한 만큼 점수 표시
  • 충돌이 일어나면 즉시 게임은 종료, 리스타트 문구를 안내
  • R키를 누르면 재시작
  • QUIT 버튼을 눌러 게임을 종료

Annoying Ghost 전체 코드

import pygame
from pygame.rect import *
import random

def restart():
    global isGameOver, score
    isGameOver = False
    score = 0
    for i in range(len(ghost)):
        ghost_rect[i].y = -1
    for i in range(len(missile)):
        missile_rect[i].y = -1

def eventProcess():
    global 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:  # 캐릭터를 왼쪽으로
                move.x = -1
            if event.key == pygame.K_RIGHT:  # 캐릭터를 오른쪽으로
                move.x = 1
            if event.key == pygame.K_UP:  # 캐릭터를 위로
                move.y = -1
            if event.key == pygame.K_DOWN:  # 캐릭터를 아래로
                move.y = 1
            if event.key == pygame.K_r:  # 리스타트
                restart()
            if event.key == pygame.K_SPACE:
                makeMissile()
        if event.type == pygame.KEYUP:  # 방향키를 떼면 멈춤
            if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                move.x = 0
            elif event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                move.y = 0

def movePlayer():
    if not isGameOver:
        player_rect.x += move.x
        player_rect.y += move.y

    if player_rect.x < 0:
        player_rect.x = 0
    if player_rect.x > screen_width - player_rect.width:
        player_rect.x = screen_width - player_rect.width

    if player_rect.y < 0:
        player_rect.y = 0
    if player_rect.y > screen_height - player_rect.height:
        player_rect.y = screen_height - player_rect.height

    screen.blit(player, player_rect)

def timeDelay500ms():
    global time_delay_500ms
    if time_delay_500ms > 5:
        time_delay_500ms = 0
        return True

    time_delay_500ms += 1
    return False

def makeGhost():
    if isGameOver:
        return
    if timeDelay500ms():
        idex = random.randint(0, len(ghost)-1)
        if ghost_rect[idex].y == -1:
            ghost_rect[idex].x = random.randint(0, screen_width)
            ghost_rect[idex].y = 0

def moveGhost():
    makeGhost()

    for i in range(len(ghost)):
        if ghost_rect[i].y == -1:
            continue

        if not isGameOver:
            ghost_rect[i].y += 1
        if ghost_rect[i].y > screen_height:
            ghost_rect[i].y = 0

        screen.blit(ghost[i], ghost_rect[i])

def CheckCollisionMissile():
    global score, isGameOver

    if isGameOver:
        return

    for rec in ghost_rect:
        if rec.y == -1:
            continue
        for recM in missile_rect:
            if rec.top < recM.bottom \
                and recM.top < rec.bottom \
                and rec.left < recM.right \
                and recM.left < rec.right:
                rec.y = -1
                recM.y = -1
                score += 10
                break

def makeMissile():
    if isGameOver:
        return
    for i in range(len(missile)):
        if missile_rect[i].y == -1:
            missile_rect[i].x = player_rect.x
            missile_rect[i].y = player_rect.y
            break

def moveMissile():
    for i in range(len(missile)):
        if missile_rect[i].y == -1:
            continue

        if not isGameOver:
            missile_rect[i].y -= 1
        if missile_rect[i].y < 0:
            missile_rect[i].y = -1

        screen.blit(missile[i], missile_rect[i])

def CheckCollision():
    global score, isGameOver

    if isGameOver:
        return

    for rec in ghost_rect:
        if rec.y == -1:
            continue
        if rec.top < player_rect.bottom \
            and player_rect.top < rec.bottom \
            and rec.left < player_rect.right \
            and player_rect.left < rec.right:
            print('충돌')
            isGameOver = True
            break
    # score += 1

def blinking():
    global time_dealy_4sec, toggle
    time_dealy_4sec += 1
    if time_dealy_4sec > 40:
        time_dealy_4sec = 0
        toggle = ~toggle

    return toggle


def setText():
    mFont = pygame.font.SysFont("arial", 20, True, False)
    screen.blit(mFont.render(
        f'SCORE {score}', True, 'yellow'), (10, 10, 0, 0))
    # print(pygame.font.get_fonts())

    if isGameOver and blinking():
        mFont = pygame.font.SysFont("arial", 100, True, False)
        screen.blit(mFont.render(
            'GAME', True, 'white'), (85, 200, 0, 0))
        mFont = pygame.font.SysFont("arial", 100, True, False)
        screen.blit(mFont.render(
            'OVER', True, 'white'), (95, 300, 0, 0))
        mFont = pygame.font.SysFont("arial", 20, True, False)
        screen.blit(mFont.render(
            'Press R Key Restart', True, 'white'), (145, 450, 0, 0))

# 변수 초기화
running = True  # 게임이 진행중
screen_width = 480
screen_height = 640
move = Rect(0, 0, 0, 0) # x, y, 가로, 세로
time_delay_500ms = 0
time_dealy_4sec = 0
toggle = False
score = 0
isGameOver = False

# 초기화, 반드시 필요
pygame.init()
pygame.mixer.init()

# 배경음
pygame.mixer.music.load('sound.mp3')
pygame.mixer.music.play(-1)

# 화면 크기 설정
screen = pygame.display.set_mode((screen_width, screen_height))
background = pygame.image.load("background.png")
# 화면 타이틀
pygame.display.set_caption('Annoying Ghost')

# 플레이어
player = pygame.image.load("character.png")
player = pygame.transform.scale(player, (40, 40))
player_rect = player.get_rect()
player_size = player.get_rect().size # 이미지 크기를 구해옴
player_rect.centerx = (screen_width / 2) # 화면 가로의 절반 크기에 해당하는 곳에 위치
player_rect.centery = (screen_height / 2) # 화면 세로 크기 가장 아래에 해당하는 곳에 위치

# 고스트
ghost = [pygame.image.load("ghost.png") for i in range(20)]
ghost_rect = [None for i in range(len(ghost))]
for i in range(len(ghost)):
    ghost[i] = pygame.transform.scale(ghost[i], (30, 30))
    ghost_rect[i] = ghost[i].get_rect()
    ghost_rect[i].y = -1

# 미사일
missile = [pygame.image.load("missile.png") for i in range(30)]
missile_rect = [None for i in range(len(missile))]
for i in range(len(missile)):
    missile[i] = pygame.transform.scale(missile[i], (15, 15))
    missile_rect[i] = missile[i].get_rect()
    missile_rect[i].y = -1

# FPS
clock = pygame.time.Clock()

### 반복 이벤트 ###
while running:
    # screen.fill((0, 0, 255))  # 배경 그리기, RGB
    screen.blit(background, (0, 0)) # 배경 그리기, 이미지

    eventProcess() # 이벤트 처리
    movePlayer() # 플레이어 이동
    moveGhost() # 적 생성 및 이동
    moveMissile() # 미사일 생성 및 이동
    CheckCollisionMissile()
    CheckCollision() # 충돌 확인
    setText() # 텍스트 업데이트
    pygame.display.flip()  # 화면 갱신 # 게임 화면을 다시 그리기
    clock.tick(100) # ms 주기로 그리기
### 반복 이벤트###


이번 프로젝트로 얻게된 것:

  • 파이썬 라이브러리 pygame의 사용방법
  • 게임 초기화, 반복 이벤트처리, 화면 그리기, 게임종료 등과 같은 기초 로직
  • 리팩토링의 중요성

아쉬운 점:

  • 느린 속도
  • 백엔드 기술 구현보다 시각적 디자인 연출에 중점
profile
다 놓치고 있습니다

1개의 댓글

comment-user-thumbnail
2022년 4월 28일

고생 많으셨습니다 :)
이제 진짜 개발을 시작하셨네요 ㅎㅎ
뭔가 색감이 귀엽습니다 ㅋㅋㅋ

우선 해당 게임의 기능을 다시 한번 정리 하시면서 한 번쯤 함수 혹은 클래스로 분류해보시는 것을 권장드립니다!
그리고 에러 코드는 완벽하게 잡을 수 없습니다. 그럼 이를 어떻게 막을 수 있을지 고민해보세요.
(예외처리 검색)

마지막으로 게임을 만들게 되면 조건문을 꽤 많이 정의하게 되며
자연스럽게 코드가 약간 지저분해집니다.

이럴때 파이썬에서는 어떻게 코드를 깔끔하게 정리할 수 있는지 검색해보세요 :)
(switch 문법)

이렇게 공부를 열심히 하신 만큼 다음 프로젝트도 잘 해내실 수 있을것 입니다.

더불이 이 기세를 몰아서 알고리즘도 한번..? ㅎㅎ

답글 달기