개인프로젝트 게임 만들기! - 결과물

Frel·2022년 4월 26일
0

내일 배움 캠프

목록 보기
13/35

오늘은 개인프로젝트인 Python에서 pygame로 만든 게임입니다!

프로젝트 만드느라 제대로 작성할 부분은 없어서 코드에 적혀있는 주석들

봐주시면 감사하겠습니다.
아래 링크는 만든 게임 시연 영상입니다.

영상 보러가기

# random 기능, sleep 기능
import random
from time import sleep

# pygame 설치
import pygame
from pygame.locals import *

# 화면 크기 설정
window_width = 500
window_height = 800

# 색깔 설정 (RGB값)
black = (0, 0, 0)
white = (255, 255, 255)
red = (250, 50, 50)
yellow = (250, 250, 50)
blue = (35, 60, 200)
occur_prob = 40
shot_count = 0  # 맞춘 적
count_missed = 0  # 놓친 적
# FPS 설정
fps = 60


# 전투기
class Fighter(pygame.sprite.Sprite):  # sprite 사용
    def __init__(self):  # __init__를 사용해서 상속자처럼 사용 가능
        super(Fighter, self).__init__()  # 상속을 받았으니 super로 상위 class에서 __init__ 콜해줌
        self.image = pygame.image.load(
            'C:/Users/user/Desktop/PyShooting_project/img/fighter.png')  # pygame의 image를 load하는 기능
        self.rect = self.image.get_rect()  # 이미지의 크기를 가져오는 기능
        self.rect.x = int(window_width / 2)  # 이미지의 위치(화면의 가운데)
        self.rect.y = window_height - self.rect.height  # 화면 크기에 아래부분에 출력
        self.dx = 0  # x방향 설정하는 기능
        self.dy = 0  # y방향 설정하는 기능

    def update(self):  # 업데이트(비행기가 움직일때를 처리해주기 위해서)
        self.rect.x += self.dx  # 이미지 x축 위치를 +
        self.rect.y += self.dy  # 이미지 y축 위치를 +

        # 우주선이 창밖으로 넘어가지 못하게 설정
        if self.rect.x < 0 or self.rect.x + self.rect.width > window_width:
            self.rect.x -= self.dx

        if self.rect.y < 0 or self.rect.y + self.rect.height > window_height:
            self.rect.y -= self.dy

    # 화면 그리기
    def draw(self, screen):
        screen.blit(self.image, self.rect)  # 우주선의 이미지를 그려줌

    # 충돌 관리
    def collide(self, sprites):  # 우주선이 충돌이 나면 리턴
        for sprite in sprites:
            if pygame.sprite.collide_rect(self, sprite):
                return sprite


# 미사일
class Missile(pygame.sprite.Sprite):  # 미사일 여러발 가능
    def __init__(self, xpos, ypos, speed):  # 초기화 (위치 값, 속도)
        super(Missile, self).__init__()  # Missile 상위 class를 호출
        self.image = pygame.image.load('C:/Users/user/Desktop/PyShooting_project/img/Laser.png')  # 미사일 이미지
        # 미사일이 발사되는 위치를 정해주기
        self.rect = self.image.get_rect()
        self.rect.x = xpos  # x방향 설정하는 기능
        self.rect.y = ypos  # y방향 설정하는 기능
        self.speed = speed  # 전투기의 속도
        self.sound = pygame.mixer.Sound('C:/Users/user/Desktop/PyShooting_project/sound/laser_sound.wav')  # 미사일 발사 소리

    # 미사일 발사 소리
    def launch(self):
        self.sound.play()  # 소리를 재생

    # 화면 밖으로 나가는 미사일 제거
    def update(self):
        self.rect.y -= self.speed  # 미사일 발사 방향이 위로 1자로 가는데 그러면 y는 -다.
        if self.rect.y + self.rect.height < 0:  # 미사일이 화면 밖으로 나가면
            self.kill()  # 미사일을 kill

    # 미사일 충돌의 여부 판단
    def collide(self, sprites):
        for sprite in sprites:
            if pygame.sprite.collide_rect(self, sprite):
                return sprite


# 적 이미지
class Enemy(pygame.sprite.Sprite):
    def __init__(self, xpos, ypos, speed):
        super(Enemy, self).__init__()
        # 적 이미지 변수
        self.speed = None
        Enemy_images = ('C:/Users/user/Desktop/PyShooting_project/img/Enemy01.png',
                        'C:/Users/user/Desktop/PyShooting_project/img/Enemy02.png',
                        'C:/Users/user/Desktop/PyShooting_project/img/Enemy03.png',
                        'C:/Users/user/Desktop/PyShooting_project/img/Enemy04.png')

        self.image = pygame.image.load(random.choice(Enemy_images))  # 적 이미지를 랜덤으로 가져오기
        self.rect = self.image.get_rect()  # 이미지 크기를 가져옴
        self.rect.x = xpos  # 위치 값
        self.rect.y = ypos  # 위치 값
        self.speed = speed  # 속도  값

    # 화면 업데이트
    def update(self):
        self.rect.y += self.speed

    # 화면을 나갈때
    def out_of_screen(self):
        if self.rect.y > window_height:
            return True


# 게임 점수
def draw_text(text, font, surface, x, y, main_color):
    text_obj = font.render(text, True, main_color)  # 폰트 정의
    text_rect = text_obj.get_rect()  # 위치 값 받아오기
    text_rect.centerx = x  # centerx 에 x를 넣어줌
    text_rect.centery = y  # centery 에 y를 넣어줌
    surface.blit(text_obj, text_rect)  # 화면에 그리기

# 게임 클리어
def gameclear():
    global screen
    font_70 = pygame.font.Font('C:/Users/user/Desktop/PyShooting_project/fonts/NanumGothic.ttf', 40)
    text = font_70.render("Game Clear", True, yellow)
    screen.blit(text, (150, 350))
    pygame.display.update() # text부분 업데이트 하기 위해서
    sleep(5)


# 폭발 이미지 (폭발이 일어나면 거기에 이미지가 뜨고 소리도 나옴)
def occur_explosion(surface, x, y):
    explosion_image = pygame.image.load('C:/Users/user/Desktop/PyShooting_project/img/explosion.png')  # 폭발 이미지
    explosion_rect = explosion_image.get_rect()
    explosion_rect.x = x  # rect에 x값 넣어줌
    explosion_rect.y = y  # rect에 y값 넣어줌
    surface.blit(explosion_image, explosion_rect)  # 화면에 그리기

    explosion_sounds = pygame.mixer.Sound('C:/Users/user/Desktop/PyShooting_project/sound/enemy_death.wav')  # 적이 죽을때 나는 소리
    explosion_sounds.play()  # 재생


# 게임의 반복 이벤트 기능
def game_loop():
    default_font = pygame.font.Font('C:/Users/user/Desktop/PyShooting_project/fonts/NanumGothic.ttf', 28)  # 폰트
    background_image = pygame.image.load('C:/Users/user/Desktop/PyShooting_project/img/background.png')  # 게임 배경
    gameover_sound = pygame.mixer.Sound('C:/Users/user/Desktop/PyShooting_project/sound/gameover.wav')  # 게임 오버하면 나오는 브금
    pygame.mixer.music.load('C:/Users/user/Desktop/PyShooting_project/sound/music.mp3')  # 게임 배경 브금
    pygame.mixer.music.play(-1)  # 노래 무한 재생
    fps_clock = pygame.time.Clock()  # 프레임

    fighter = Fighter()
    missiles = pygame.sprite.Group()  # 여러개가 들어갈 수 있어야 함
    enemys = pygame.sprite.Group()  # 여러개가 들어갈 수 있어야 함

    occur_prob = 40
    shot_count = 0  # 맞춘 적
    count_missed = 0  # 놓친 적

    # 게임 키보드 이벤트
    done = False
    totalgame = True
    while totalgame:

        while not done:
            for event in pygame.event.get():  # 이벤트처리로 get을 통해 가져옴
                if event.type == pygame.KEYDOWN:  # 키가 눌리면
                    if event.key == pygame.K_LEFT:  # 눌린 키가 왼쪽 방향키라면
                        fighter.dx -= 5  # fighter의 x축이 -5만큼 이동
                    elif event.key == pygame.K_RIGHT:  # 눌린 키가 오른쪽 방향키라면
                        fighter.dx += 5  # fighter의 x축이 -5만큼 이동
                    elif event.key == pygame.K_UP:  # 눌린 키가 위쪽 방향키라면
                        fighter.dy -= 5  # fighter의 y축이 -5만큼 이동
                    elif event.key == pygame.K_DOWN:  # 눌린 키가 아래쪽 방향키라면
                        fighter.dy += 5  # fighter의 y축이 -5만큼 이동
                    elif event.key == pygame.K_SPACE:  # 눌린 키가 스페이스바라면
                        missile = Missile(fighter.rect.centerx, fighter.rect.y, 7)  # fighter의 가운데에서 미사일이 10의 속도로 나감
                        missile.launch()  # 발사
                        missiles.add(missile)  # missiles안에 missile 저장

                if event.type == pygame.KEYUP:  # 키보드에서 손을 땠을때
                    if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:  # 왼쪽 or 오른쪽 키를 손 땠을때
                        fighter.dx = 0  # 좌우로 움직이는 값 0
                    elif event.key == pygame.K_UP or event.key == pygame.K_DOWN:  # 위 or 아래 키를 손 땠을때
                        fighter.dy = 0  # 위, 아래로 움직이는 값 0

            screen.blit(background_image, background_image.get_rect())  # 배경을 그리고 get_rect을 이용해서 위치를 알려줌

            # 적을 많이 맞출때마다 적 속도가 빨라지고 나온는 적의 수가 많아짐
            occur_of_enemy = 1 + int(shot_count / 300)  # 적 300명 처치시
            min_enemy_speed = 1 + int(shot_count / 200)  # 적 200명 처치시
            max_enemy_speed = 1 + int(shot_count / 100)  # 적 100명 처치시

            # 점수
            if random.randint(1, occur_prob) == 1:
                for i in range(occur_of_enemy):
                    speed = random.randint(min_enemy_speed, max_enemy_speed)  # 랜덤으로 속도를 선택
                    enemy = Enemy(random.randint(0, window_width - 50), 0, speed)  # 적을 안쪽으로 나오게 만들고 y값은 0 스피드만큼
                    enemys.add(enemy)

            # 처치한 적 점수 확인
            draw_text('처치한 적: {}'.format(shot_count), default_font, screen, 100, 20, yellow)  # 점수 카운트, 글꼴, 화면 x, y, 색
            draw_text('침입한 적: {}'.format(count_missed), default_font, screen, 400, 20, red)  # 점수 카운트, 글꼴, 화면 x, y, 색

            # 여러개 발사된 미사일의 충돌 체크
            for missile in missiles:
                enemy = missile.collide(enemys)
                if enemy:  # 충돌이 진짜로 난다면
                    missile.kill()  # 미사일 kill
                    enemy.kill()  # 적 kill
                    occur_explosion(screen, enemy.rect.x, enemy.rect.y)  # 적의 위치 값
                    shot_count += 1  # 처치한 적에따라 점수 증가

            for enemy in enemys:  # 전체 적
                if enemy.out_of_screen():  # 적이 화면 밖으로 나가면
                    enemy.kill()  # 적 kill
                    count_missed += 1  # 침입한 적에 따라 점수 증가

            enemys.update()
            enemys.draw(screen)  # 적 스크린 그리기
            missiles.update()  # 미사일 업데이트
            missiles.draw(screen)  # 미사일 그리기
            fighter.update()  # 전투기 업데이트
            fighter.draw(screen)  # 전투기 그리기
            pygame.display.flip()  # 현재 업데이트된 값을 바로 flip으로 전체 반영

            draw_x = int(window_width / 2)  # 문구 위치
            draw_y = int(window_height / 4)  # 문구 위치
            font_70 = pygame.font.Font('C:/Users/user/Desktop/PyShooting_project/fonts/NanumGothic.ttf', 40)  # 글꼴:나눔 고딕 글자 크기:40

            if fighter.collide(enemys) or count_missed >= 3:  # 전투기가 충돌이 났을때 or 적이 3명이상 침입했을때
                pygame.mixer_music.stop()  # 노래 멈추기
                occur_explosion(screen, fighter.rect.x, fighter.rect.y)  # 폭발 위치
                pygame.display.update() # 업데이트 화면에 출력
                gameover_sound.play()  # 게임 오버 소리
                text = font_70.render('Game Over', True, red)  # 폰트 변수 입히기
                text_rect = text.get_rect()  # 위치 값 받아오기
                text_rect.centerx = draw_x  # centerx 에 draw_x를 넣어줌
                text_rect.centery = draw_y  # centery 에 draw_y를 넣어줌
                screen.blit(text, (160, 350))  # 화면에 그리기
                pygame.display.update()  # screen.blit을 하면 무조건 pygame.display.update하기
                sleep(5)  # 쉬는 타이밍 1초
                done = True  # 반복문이 끝
            # 500마리 처치시 게임 클리어
            # 클리어 문구 뜨게 하기
            if shot_count == 500:
                done = True
                pygame.display.update()
                gameclear()
            fps_clock.tick(fps)  # FPS

        return 'game_menu' # while totalgame이랑 묶여있음

# 게임 시작 화면
def game_menu():
    start_image = pygame.image.load('C:/Users/user/Desktop/PyShooting_project/img/background.png')  # 게임 메인 화면
    screen.blit(start_image, [0, 0])  # 게임 화면 메인에 그리기
    draw_x = int(window_width / 2)  # 문구 위치
    draw_y = int(window_height / 4)  # 문구 위치
    font_70 = pygame.font.Font('C:/Users/user/Desktop/PyShooting_project/fonts/NanumGothic.ttf',70)  # 글꼴:나눔 고딕 글자 크기:70
    font_40 = pygame.font.Font('C:/Users/user/Desktop/PyShooting_project/fonts/NanumGothic.ttf',40)  # 글꼴:나눔 고딕 글자 크기:40
    font_20 = pygame.font.Font('C:/Users/user/Desktop/PyShooting_project/fonts/NanumGothic.ttf',20)  # 글꼴:나눔 고딕 글자 크기:20

    draw_text('테란 VS 저그', font_40, screen, draw_x, draw_y - 50, blue)  # 문구와 글자 크기, 화면, 문구 위치, 색
    draw_text('Enter 키를 누르면', font_40, screen, draw_x, draw_y + 130, white)  # 문구와 글자 크기, 화면, 문구 위치 + 200, 색
    draw_text('게임이 시작됩니다.', font_40, screen, draw_x, draw_y + 180, white)  # 문구와 글자 크기, 화면, 문구 위치 + 250, 색
    draw_text('처치할수록 더 많이 몰려오는', font_20, screen, draw_x, draw_y + 300, white) # 문구와 글자 크기, 화면, 문구 위치 + 250, 색
    draw_text('저그들을 물리쳐서 테란을 지켜주세요!', font_20, screen, draw_x, draw_y + 350, white) # 문구와 글자 크기, 화면, 문구 위치 + 250, 색
    draw_text('적에게 닿으면 게임 오버', font_20, screen, draw_x, draw_y + 450, red) # 문구와 글자 크기, 화면, 문구 위치 + 250, 색
    draw_text('적 3마리 이상 화면 아래에 닿으면 게임 오버', font_20, screen, draw_x, draw_y + 500, red) # 문구와 글자 크기, 화면, 문구 위치 + 250, 색

    pygame.display.update()  # 업데이트 화면에 출력

    for event in pygame.event.get(): # pygame에서 이벤트를 가져옴
        if event.type == pygame.KEYDOWN: # 키가 눌리면
            if event.key == pygame.K_RETURN: # 키가 눌린 값이 엔터값이면
                return 'play' # 게임 플레이로 넘어감
        if event.type == QUIT: # 게임 종료
            return 'quit' # 게임종료

    return 'game_menu' # 다시 게임메뉴로 돌아감

def main():
    global screen # 게임 전체에서 사용

    pygame.init() # 초기화
    screen = pygame.display.set_mode((window_width, window_height)) # 사용할 디스플레이 셋팅
    pygame.display.set_caption('테란 VS 저그') # 제목

    action = 'game_menu' # 액션 = 게임 메뉴
    while action != 'quit': # while 에서 반복
        if action == 'game_menu': # 액션이 게임 메뉴라면?
            action = game_menu() # 액션은 게임메뉴
        elif action == 'play': # 액션이 플레이라면?
            action = game_loop() # 게임 수행중으로 변경

pygame.quit() # pygame 종료

if __name__ == "__main__": #
    main()

후기

와.. 아직 잘 모르는데 첫 프로젝트로 게임을 만들어오라고 해서 처음에 너무 당황했어요..
그리고 처음에 만들던 프로젝트도 너무 꼬여서 어제 저녁 7시부터 새벽5시까지 새로 열심히
만들었어요.. 근데 진짜 이렇게 새벽까지 코딩하면서 힘들긴했지만, 진짜 재미있었던것 같아요.
영상들, 자료들 참고하면서 만들었지만 오류들이 생기는걸 찾아내서 하나씩 고쳐나가고,
오류가 하나씩 풀려나갈때 진짜 행복했고 마지막에 오류 없이 창이 뜨는걸 보니,
이 맛에 코딩하는구나 하고 느꼈습니다.. 진짜 재미있네요..

profile
자기개발을 해서 발전하는 미래의 개발자

2개의 댓글

comment-user-thumbnail
2022년 4월 27일

에러를 해결한 만큼 연봉이 올라간다. - 지나가던 개발자 -

ㅎㅎㅎ 고생하셨어요
스타 겔러그 잘 보고 가용 ㅎㅎ

답글 달기
comment-user-thumbnail
2022년 4월 28일

파이썬 문법 배우고 처음 만들어보는 게임이라 힘드셨을수도 있을텐데 기본적이지만 많은것을 배울수 있는 스타크래프트 슈팅게임 구현하느라 너무 수고하셨습니다.
개발한 코드 git에 올려서 버전관리 하시면서 틈틈히 코드 정리하면서 리팩토링하시길 추천 드립니다.!
틈틈히 다시 복습하면서 디버깅했던 부분만 따로 정리하는것도 많은 도움되실꺼에요!
짧은기간 동안 구현하시느라 수고하셨습니다!

답글 달기