Skip to content

Latest commit

 

History

History
600 lines (537 loc) · 31.6 KB

220106.md

File metadata and controls

600 lines (537 loc) · 31.6 KB

Day 90

파이썬으로 배우는 게임 개발 실전편

Chapter 7

실드 추가하기

import pygame
import sys
import math
import random
from pygame.locals import *

# 이미지 로딩
img_galaxy = pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/galaxy.png')
img_sship = [
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/starship.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/starship_l.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/starship_r.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/starship_burner.png')
]
img_weapon = pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/bullet.png')
img_shield = pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/shield.png')
img_enemy = [
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/enemy0.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/enemy1.png')
]
img_explode = [
    None,
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/explosion1.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/explosion2.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/explosion3.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/explosion4.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/explosion5.png')
]

tmr = 0
bg_y = 0

ss_x = 480      # 플레이어 기체의 X 좌표
ss_y = 360      # 플레이어 기체의 Y 좌표
ss_d = 0        # 플레이어 기체의 기울기 변수
ss_shield = 100 # 플레이어 기체의 실드량 변수
ss_muteki = 0   # 플레이어 기체의 무적 상태 변수
key_spc = 0     # 스페이스 키를 눌렀을 때 사용하는 변수
key_z = 0       # Z 키를 눌렀을 때 사용할 변수

MISSILE_MAX = 200               # 플레이어가 발사한 최대 탄환 수 정의
msl_no = 0                      # 탄환 발사에 사용할 리스트 인덱스 변수
msl_f = [False] * MISSILE_MAX   # 탄환을 발사 중인지 관리하는 플래그 리스트
msl_x = [0] * MISSILE_MAX       # 탄환의 X, Y 좌표 리스트
msl_y = [0] * MISSILE_MAX
msl_a = [0] * MISSILE_MAX       # 탄환이 날아가는 각도 리스트

ENEMY_MAX = 100             # 적 최대 수 정의
emy_no = 0                  # 적 등장시 사용할 리스트 인덱스 변수
emy_f = [False] * ENEMY_MAX # 적 등장 여부 관리 플래그 리스트
emy_x = [0] * ENEMY_MAX     # 적 X, Y 좌표 리스트
emy_y = [0] * ENEMY_MAX
emy_a = [0] * ENEMY_MAX     # 적의 비행 각도 리스트
emy_type = [0] * ENEMY_MAX  # 적의 종류 리스트
emy_speed = [0] * ENEMY_MAX # 적 속도 리스트

EMY_BULLET = 0  # 적의 탄환 번호 관리 상수
LINE_T = -80    # 적이 나타나는(사라지는) 위쪽 좌표
LINE_B = 800    # 적이 나타나는(사라지는) 아래쪽 좌표
LINE_L = -80    # 적이 나타나는(사라지는) 왼쪽 좌표
LINE_R = 1040   # 적이 나타나는(사라지는) 오른쪽 좌표

EFFECT_MAX = 100            # 폭발 연출 최대 수 정의
eff_no = 0                  # 폭발 연출 시 사용할 리스트 인덱스 변수
eff_p = [0] * EFFECT_MAX    # 폭발 연출 이미지 번호 리스트
eff_x = [0] * EFFECT_MAX    # 폭발 연출 X, Y 좌표 리스트
eff_y = [0] * EFFECT_MAX

def get_dis(x1, y1, x2, y2):    # 두 점 사이 거리 계산
    return ((x1 -x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))

def move_starship(scrn, key):   # 플레이어 기체 이동
    global ss_x, ss_y, ss_d, ss_shield, ss_muteki, key_spc, key_z
    ss_d = 0                    # 기체 기울기 변수에 0(기울지 않음 대입)
    if key[K_UP] == 1:          # 방향 키를 눌렀다면
        ss_y = ss_y - 20
        if ss_y < 80:           # 화면 밖으로 나가지 않도록 조정
            ss_y = 80
    if key[K_DOWN] == 1:
        ss_y = ss_y + 20
        if ss_y > 640:
            ss_y = 640
    if key[K_LEFT] == 1:
        ss_d = 1                # 기체 기울기 변수에 1(왼쪽) 대입
        ss_x = ss_x - 20
        if ss_x < 40:
            ss_x = 40
    if key[K_RIGHT] == 1:
        ss_d = 2                # 기체 기울기 변수에 2(오른쪽) 대입
        ss_x = ss_x + 20
        if ss_x > 920:
            ss_x = 920
    key_spc = (key_spc + 1) * key[K_SPACE]  # 스페이스 키를 누르는 동안 변수 값 증가
    if key_spc % 5 == 1:                    # 스페이스 키를 처음 누른 후, 5 프레임마다
        set_missile(0)                       # 탄환 발사
    key_z = (key_z + 1) * key[K_z]          # Z 키를 누르는 동안 변수 값 증가
    if key_z == 1 and ss_shield > 10:       # 1 번 눌렀을 때 실드량이 10보다 크다면
        set_missile(10)                     # 탄막 치기
        ss_shield = ss_shield - 10          # 실드량 10 감소
    
    if ss_muteki % 2 == 0:                                              # 무적 상태에서 깜빡이기 위한 if 문
        scrn.blit(img_sship[3], [ss_x - 8, ss_y + 40 + (tmr % 3) *2])   # 엔진 불꽃 그리기
        scrn.blit(img_sship[ss_d], [ss_x - 37, ss_y - 48])              # 플레이어 기체 그리기

    if ss_muteki > 0:                                           # 무적 상태라면
        ss_muteki = ss_muteki -  1                              # ss_muteki 값 감소
        return                                                  # 함수에서 벗어남(적과 히트 체크 미수행)
    for i in range(ENEMY_MAX):                                  # 적 기체외 히트 체크 수행
        if emy_f[i] == True:                                    # 적 기체와 히트 체크
            w = img_enemy[emy_type[i]].get_width()
            h = img_enemy[emy_type[i]].get_height()
            r = int((w + h) / 4 + (74 + 96) / 4)
            if get_dis(emy_x[i], emy_y[i], ss_x, ss_y) < r * r: # 적 기체와 접촉한 경우
                set_effect(ss_x, ss_y)                          # 폭발 연출 설정
                ss_shield = ss_shield - 10                      # 실드량 감소
                if ss_shield <= 0:                              # ss_shield 값이 0 이하이면
                    ss_shield = 0                               # ss_shield 값에 0 대입
                if ss_muteki == 0:                              # 무적 상태가 아니면
                    ss_muteki = 60                              # 무적 상태로 설정
                emy_f[i] = False

def set_missile(typ):      # 플레이어 기체 발사 탄환 설정
    global msl_no
    if typ == 0:    # 단발
        msl_f[msl_no] = True    # 탄환 발사 플래그 True 설정
        msl_x[msl_no] = ss_x    # 탄환의 X, Y 좌표 대입(플레이어 기체 앞 끝)
        msl_y[msl_no] = ss_y - 50
        msl_a[msl_no] = 270     # 탄환 발사 각도
        msl_no = (msl_no + 1) % MISSILE_MAX # 다음 설정을 위한 번호 계산
    if typ == 10:   # 탄막
        for a in range(160, 390, 10):
            msl_f[msl_no] = True    # 탄환 발사 플래그 True 설정
            msl_x[msl_no] = ss_x    # 탄환의 X, Y 좌표 대입(플레이어 기체 앞 끝)
            msl_y[msl_no] = ss_y - 50
            msl_a[msl_no] = a       # 탄환 발사 각도
            msl_no = (msl_no + 1) % MISSILE_MAX # 다음 설정을 위한 번호 계산

def move_missile(scrn):     # 탄환 이동
    for i in range(MISSILE_MAX):
        if msl_f[i] == True:            # 탄환이 발사된 상태라면
            msl_x[i] = msl_x[i] + 36 * math.cos(math.radians(msl_a[i]))                                 # X, Y 좌표 계산
            msl_y[i] = msl_y[i] + 36 * math.sin(math.radians(msl_a[i]))
            img_rz = pygame.transform.rotozoom(img_weapon, -90 - msl_a[i], 1.0)                         # 날아가는 각도의 회전 이미지 생성
            scrn.blit(img_rz, [msl_x[i] - img_rz.get_width() / 2, msl_y[i] - img_rz.get_height() / 2])  # 탄환 이미지 그리기
            if msl_y[i] < 0 or msl_x[i] < 0 or msl_x[i] > 960:                                          # 탄환이 화면 밖으로 나가면
                msl_f[i] = False                                                                        # 탄환 삭제

def bring_enemy():      # 적 기체 등장
    if tmr % 30 == 0:   # 이 타이밍에서
        set_enemy(random.randint(20, 940), LINE_T, 90, 1, 6) # 일반 기체 1기 등장

def set_enemy(x, y, a, ty, sp): # 적 기체 설정
    global emy_no
    while True:
        if emy_f[emy_no] == False:  # 비어있는 리스트라면
            emy_f[emy_no] = True    # 플래그 설정
            emy_x[emy_no] = x       # X, Y 좌표 대입
            emy_y[emy_no] = y
            emy_a[emy_no] = a       # 각도 대입
            emy_type[emy_no] = ty   # 적 종류 대입
            emy_speed[emy_no] = sp  # 적 속도 대입
            break
        emy_no = (emy_no + 1) % ENEMY_MAX   # 다음 설정을 위한 번호 계산

def move_enemy(scrn): # 적 기체 이동
    global ss_shield
    for i in range(ENEMY_MAX):
        if emy_f[i] == True:        # 적 기체가 존재한다면
            ang = -90 - emy_a[i]    # ang에 이미지 회전 각도 대입
            png = emy_type[i]       # png에 이미지 번호 대입
            emy_x[i] = emy_x[i] + emy_speed[i] * math.cos(math.radians(emy_a[i]))   # X, Y 좌표 변화
            emy_y[i] = emy_y[i] + emy_speed[i] * math.sin(math.radians(emy_a[i]))
            if emy_type[i] == 1 and emy_y[i] > 360:     # 적 기체의 Y 좌표가 360을 넘었다면
                set_enemy(emy_x[i], emy_y[i], 90, 0, 8) # 탄환 발사
                emy_a[i] = -45      # 방향 변경
                emy_speed[i] = 16   # 속도 변경
            if emy_x[i] < LINE_L or LINE_R < emy_x[i] or emy_y[i] < LINE_T or LINE_B < emy_y[i]:    # 화면 상하좌우에서 벗어났다면
                emy_f[i] = False    # 적 기체 삭제
            if emy_type[i] != EMY_BULLET:               # 플레이어 기체 발사 탄환과 히트 체크
                w = img_enemy[emy_type[i]].get_width()  # 적 기체 이미지 폭(픽셀 수)
                h = img_enemy[emy_type[i]].get_height() # 적 기체 이미지 높이(픽셀 수)
                r = int((w + h) / 4) + 12               # 히트 체크에 사용할 거리 계산
                for n in range(MISSILE_MAX):
                    if msl_f[n] == True and get_dis(emy_x[i], emy_y[i], msl_x[n], msl_y[n]) < r * r: # 플레이어 기체 탄환과 접촉 여부 판단
                        msl_f[n] = False                # 탄환 삭제
                        set_effect(emy_x[i], emy_y[i])  # 폭발 이펙트
                        emy_f[i] = False                # 적 기체 삭제
                        if ss_shield < 100:             # 플레이어 실드량이 100 미만이면
                            ss_shield = ss_shield + 1   # 실드량 증가
            img_rz = pygame.transform.rotozoom(img_enemy[png], ang, 1.0)
            scrn.blit(img_rz, [emy_x[i] - img_rz.get_width() / 2, emy_y[i] - img_rz.get_height() / 2])

def set_effect(x, y):   # 폭발 설정
    global eff_no
    eff_p[eff_no] = 1   # 폭발 연출 이미지 번호 대입
    eff_x[eff_no] = x   # 폭발 연출 X, Y 좌표 대입
    eff_y[eff_no] = y
    eff_no = (eff_no + 1) % EFFECT_MAX  # 다음 설정을 위한 번호 계산

def draw_effect(scrn):  # 폭발 연출
    for i in range(EFFECT_MAX):
        if eff_p[i] > 0:            # 폭발 연출 중이면
            scrn.blit(img_explode[eff_p[i]], [eff_x[i] - 48, eff_y[i] - 48]) # 폭발 연출 표시
            eff_p[i] = eff_p[i] + 1 # eff_p 값 1 증가
            if eff_p[i] == 6:       # eff_p가 6이 되었다면
                eff_p[i] = 0        # eff_p에 0 대입 후 연출 종료

def main():
    global tmr, bg_y

    pygame.init()
    pygame.display.set_caption('Galaxy Lancer')
    screen = pygame.display.set_mode((960, 720))
    clock = pygame.time.Clock()

    while True:
        tmr = tmr + 1
        for e in pygame.event.get():
            if e.type == QUIT:
                pygame.quit()
                sys.exit()
            if e.type == KEYDOWN:
                if e.key == K_F1:
                    screen = pygame.display.set_mode((960, 720), pygame.FULLSCREEN)
                if e.key == K_F2 or e.key == K_ESCAPE:
                    screen = pygame.display.set_mode((960, 720))

        # 배경 스크롤
        bg_y = (bg_y + 16) % 720                    # 배경 스크롤 위치 계산
        screen.blit(img_galaxy, [0, bg_y - 720])    # 배경 그리기(위쪽)
        screen.blit(img_galaxy, [0, bg_y])          # 배경 그리기(아래쪽)

        key = pygame.key.get_pressed()              # key에 모든 키 상태를 대입
        move_starship(screen, key)                  # 플레이어 기체 이동
        move_missile(screen)                        # 플레이어 탄환 이동
        bring_enemy()                               # 적 기체 등장
        move_enemy(screen)                          # 적 기체 이동
        draw_effect(screen)                         # 폭발 연출
        screen.blit(img_shield, [40, 680])          # 실드 화면 그리기
        pygame.draw.rect(screen, (64, 32, 32), [40 + ss_shield * 4, 680, (100 - ss_shield) * 4, 12]) # 감소한 실드를 사각형으로 칠함

        pygame.display.update()
        clock.tick(30)

if __name__ == '__main__':
    main()

추가 및 변경된 점 : move_starship() 함수에서 적 기체와 접촉했을 때 무적 설정, 실드량 감소 부분 추가, move_enemy() 함수에 적 격추 시 실드량 회복 부분 추가

적의 탄환이나 기체에 닿으면 실드가 감소하고, 플레이어 기체가 깜빡이며, 그 동안 데미지를 받지 않는다. 실드가 깎여 있을 때 적을 격추하면 실드가 회복된다.

ss_shield로 실드량, ss_muteki로 무적 상태 시간을 관리한다.

if ss_muteki > 0:                                           # 무적 상태라면
    ss_muteki = ss_muteki -  1                              # ss_muteki 값 감소
    return                                                  # 함수에서 벗어남(적과 히트 체크 미수행)
    for i in range(ENEMY_MAX):                                  # 적 기체외 히트 체크 수행
    if emy_f[i] == True:                                    # 적 기체와 히트 체크
        w = img_enemy[emy_type[i]].get_width()
        h = img_enemy[emy_type[i]].get_height()
        r = int((w + h) / 4 + (74 + 96) / 4)
        if get_dis(emy_x[i], emy_y[i], ss_x, ss_y) < r * r: # 적 기체와 접촉한 경우
            set_effect(ss_x, ss_y)                          # 폭발 연출 설정
            ss_shield = ss_shield - 10                      # 실드량 감소
            if ss_shield <= 0:                              # ss_shield 값이 0 이하이면
                ss_shield = 0                               # ss_shield 값에 0 대입
            if ss_muteki == 0:                              # 무적 상태가 아니면
                ss_muteki = 60                              # 무적 상태로 설정
            emy_f[i] = False

if ss_muteki > 0이라는 조건 분기에서 무적 상태인 경우에는 ss_muteki 값을 감소시키고 return 명령으로 함수를 빠져 나와 히트 체크를 수행하지 않는다.

타이틀, 게임 플레이, 게임 오버

import pygame
import sys
import math
import random
from pygame.locals import *

# 색 정의
BLACK = (0, 0, 0)
SILVER = (192, 208, 224)
RED = (255, 0, 0)
CYAN = (0, 224, 255)

# 이미지 로딩
img_galaxy = pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/galaxy.png')
img_sship = [
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/starship.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/starship_l.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/starship_r.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/starship_burner.png')
]
img_weapon = pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/bullet.png')
img_shield = pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/shield.png')
img_enemy = [
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/enemy0.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/enemy1.png')
]
img_explode = [
    None,
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/explosion1.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/explosion2.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/explosion3.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/explosion4.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/explosion5.png')
]
img_title = [
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/nebula.png'),
    pygame.image.load('Python_workspace/python_game/Chapter7/img_gl/logo.png')
]

idx = 0
tmr = 0
score = 0
bg_y = 0

ss_x = 0        # 플레이어 기체의 X, Y 좌표
ss_y = 0
ss_d = 0        # 플레이어 기체의 기울기 변수
ss_shield = 0   # 플레이어 기체의 실드량 변수
ss_muteki = 0   # 플레이어 기체의 무적 상태 변수
key_spc = 0     # 스페이스 키를 눌렀을 때 사용하는 변수
key_z = 0       # Z 키를 눌렀을 때 사용할 변수

MISSILE_MAX = 200               # 플레이어가 발사한 최대 탄환 수 정의
msl_no = 0                      # 탄환 발사에 사용할 리스트 인덱스 변수
msl_f = [False] * MISSILE_MAX   # 탄환을 발사 중인지 관리하는 플래그 리스트
msl_x = [0] * MISSILE_MAX       # 탄환의 X, Y 좌표 리스트
msl_y = [0] * MISSILE_MAX
msl_a = [0] * MISSILE_MAX       # 탄환이 날아가는 각도 리스트

ENEMY_MAX = 100             # 적 최대 수 정의
emy_no = 0                  # 적 등장시 사용할 리스트 인덱스 변수
emy_f = [False] * ENEMY_MAX # 적 등장 여부 관리 플래그 리스트
emy_x = [0] * ENEMY_MAX     # 적 X, Y 좌표 리스트
emy_y = [0] * ENEMY_MAX
emy_a = [0] * ENEMY_MAX     # 적의 비행 각도 리스트
emy_type = [0] * ENEMY_MAX  # 적의 종류 리스트
emy_speed = [0] * ENEMY_MAX # 적 속도 리스트

EMY_BULLET = 0  # 적의 탄환 번호 관리 상수
LINE_T = -80    # 적이 나타나는(사라지는) 위쪽 좌표
LINE_B = 800    # 적이 나타나는(사라지는) 아래쪽 좌표
LINE_L = -80    # 적이 나타나는(사라지는) 왼쪽 좌표
LINE_R = 1040   # 적이 나타나는(사라지는) 오른쪽 좌표

EFFECT_MAX = 100            # 폭발 연출 최대 수 정의
eff_no = 0                  # 폭발 연출 시 사용할 리스트 인덱스 변수
eff_p = [0] * EFFECT_MAX    # 폭발 연출 이미지 번호 리스트
eff_x = [0] * EFFECT_MAX    # 폭발 연출 X, Y 좌표 리스트
eff_y = [0] * EFFECT_MAX

def get_dis(x1, y1, x2, y2):    # 두 점 사이 거리 계산
    return ((x1 -x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))

def draw_text(scrn, txt, x, y, siz, col):  # 문자 표시
    fnt = pygame.font.Font(None, siz)
    sur = fnt.render(txt, True, col)
    x = x - sur.get_width() / 2
    y = y - sur.get_height() / 2
    scrn.blit(sur, [x, y])

def move_starship(scrn, key):   # 플레이어 기체 이동
    global idx, tmr, ss_x, ss_y, ss_d, ss_shield, ss_muteki, key_spc, key_z
    ss_d = 0                    # 기체 기울기 변수에 0(기울지 않음 대입)
    if key[K_UP] == 1:          # 방향 키를 눌렀다면
        ss_y = ss_y - 20
        if ss_y < 80:           # 화면 밖으로 나가지 않도록 조정
            ss_y = 80
    if key[K_DOWN] == 1:
        ss_y = ss_y + 20
        if ss_y > 640:
            ss_y = 640
    if key[K_LEFT] == 1:
        ss_d = 1                # 기체 기울기 변수에 1(왼쪽) 대입
        ss_x = ss_x - 20
        if ss_x < 40:
            ss_x = 40
    if key[K_RIGHT] == 1:
        ss_d = 2                # 기체 기울기 변수에 2(오른쪽) 대입
        ss_x = ss_x + 20
        if ss_x > 920:
            ss_x = 920
    key_spc = (key_spc + 1) * key[K_SPACE]  # 스페이스 키를 누르는 동안 변수 값 증가
    if key_spc % 5 == 1:                    # 스페이스 키를 처음 누른 후, 5 프레임마다
        set_missile(0)                       # 탄환 발사
    key_z = (key_z + 1) * key[K_z]          # Z 키를 누르는 동안 변수 값 증가
    if key_z == 1 and ss_shield > 10:       # 1 번 눌렀을 때 실드량이 10보다 크다면
        set_missile(10)                     # 탄막 치기
        ss_shield = ss_shield - 10          # 실드량 10 감소
    
    if ss_muteki % 2 == 0:                                              # 무적 상태에서 깜빡이기 위한 if 문
        scrn.blit(img_sship[3], [ss_x - 8, ss_y + 40 + (tmr % 3) *2])   # 엔진 불꽃 그리기
        scrn.blit(img_sship[ss_d], [ss_x - 37, ss_y - 48])              # 플레이어 기체 그리기

    if ss_muteki > 0:                                           # 무적 상태라면
        ss_muteki = ss_muteki -  1                              # ss_muteki 값 감소
        return                                                  # 함수에서 벗어남(적과 히트 체크 미수행)
    elif idx == 1:                                              # 무적 상태가 아니고, idx가 1이면
        for i in range(ENEMY_MAX):                                  # 적 기체외 히트 체크 수행
            if emy_f[i] == True:                                    # 적 기체와 히트 체크
                w = img_enemy[emy_type[i]].get_width()
                h = img_enemy[emy_type[i]].get_height()
                r = int((w + h) / 4 + (74 + 96) / 4)
                if get_dis(emy_x[i], emy_y[i], ss_x, ss_y) < r * r: # 적 기체와 접촉한 경우
                    set_effect(ss_x, ss_y)                          # 폭발 연출 설정
                    ss_shield = ss_shield - 10                      # 실드량 감소
                    if ss_shield <= 0:                              # ss_shield 값이 0 이하이면
                        ss_shield = 0                               # ss_shield 값에 0 대입
                        idx = 2                                     # 게임 오버로 이동
                        tmr = 0
                    if ss_muteki == 0:                              # 무적 상태가 아니면
                        ss_muteki = 60                              # 무적 상태로 설정
                    emy_f[i] = False

def set_missile(typ):      # 플레이어 기체 발사 탄환 설정
    global msl_no
    if typ == 0:    # 단발
        msl_f[msl_no] = True    # 탄환 발사 플래그 True 설정
        msl_x[msl_no] = ss_x    # 탄환의 X, Y 좌표 대입(플레이어 기체 앞 끝)
        msl_y[msl_no] = ss_y - 50
        msl_a[msl_no] = 270     # 탄환 발사 각도
        msl_no = (msl_no + 1) % MISSILE_MAX # 다음 설정을 위한 번호 계산
    if typ == 10:   # 탄막
        for a in range(160, 390, 10):
            msl_f[msl_no] = True    # 탄환 발사 플래그 True 설정
            msl_x[msl_no] = ss_x    # 탄환의 X, Y 좌표 대입(플레이어 기체 앞 끝)
            msl_y[msl_no] = ss_y - 50
            msl_a[msl_no] = a       # 탄환 발사 각도
            msl_no = (msl_no + 1) % MISSILE_MAX # 다음 설정을 위한 번호 계산

def move_missile(scrn):     # 탄환 이동
    for i in range(MISSILE_MAX):
        if msl_f[i] == True:            # 탄환이 발사된 상태라면
            msl_x[i] = msl_x[i] + 36 * math.cos(math.radians(msl_a[i]))                                 # X, Y 좌표 계산
            msl_y[i] = msl_y[i] + 36 * math.sin(math.radians(msl_a[i]))
            img_rz = pygame.transform.rotozoom(img_weapon, -90 - msl_a[i], 1.0)                         # 날아가는 각도의 회전 이미지 생성
            scrn.blit(img_rz, [msl_x[i] - img_rz.get_width() / 2, msl_y[i] - img_rz.get_height() / 2])  # 탄환 이미지 그리기
            if msl_y[i] < 0 or msl_x[i] < 0 or msl_x[i] > 960:                                          # 탄환이 화면 밖으로 나가면
                msl_f[i] = False                                                                        # 탄환 삭제

def bring_enemy():      # 적 기체 등장
    if tmr % 30 == 0:   # 이 타이밍에서
        set_enemy(random.randint(20, 940), LINE_T, 90, 1, 6) # 일반 기체 1기 등장

def set_enemy(x, y, a, ty, sp): # 적 기체 설정
    global emy_no
    while True:
        if emy_f[emy_no] == False:  # 비어있는 리스트라면
            emy_f[emy_no] = True    # 플래그 설정
            emy_x[emy_no] = x       # X, Y 좌표 대입
            emy_y[emy_no] = y
            emy_a[emy_no] = a       # 각도 대입
            emy_type[emy_no] = ty   # 적 종류 대입
            emy_speed[emy_no] = sp  # 적 속도 대입
            break
        emy_no = (emy_no + 1) % ENEMY_MAX   # 다음 설정을 위한 번호 계산
    print(emy_no)

def move_enemy(scrn): # 적 기체 이동
    global idx, tmr, score, ss_shield
    for i in range(ENEMY_MAX):
        if emy_f[i] == True:        # 적 기체가 존재한다면
            ang = -90 - emy_a[i]    # ang에 이미지 회전 각도 대입
            png = emy_type[i]       # png에 이미지 번호 대입
            emy_x[i] = emy_x[i] + emy_speed[i] * math.cos(math.radians(emy_a[i]))   # X, Y 좌표 변화
            emy_y[i] = emy_y[i] + emy_speed[i] * math.sin(math.radians(emy_a[i]))
            if emy_type[i] == 1 and emy_y[i] > 360:     # 적 기체의 Y 좌표가 360을 넘었다면
                set_enemy(emy_x[i], emy_y[i], 90, 0, 8) # 탄환 발사
                emy_a[i] = -45      # 방향 변경
                emy_speed[i] = 16   # 속도 변경
            if emy_x[i] < LINE_L or LINE_R < emy_x[i] or emy_y[i] < LINE_T or LINE_B < emy_y[i]:    # 화면 상하좌우에서 벗어났다면
                emy_f[i] = False    # 적 기체 삭제
            if emy_type[i] != EMY_BULLET:               # 플레이어 기체 발사 탄환과 히트 체크
                w = img_enemy[emy_type[i]].get_width()  # 적 기체 이미지 폭(픽셀 수)
                h = img_enemy[emy_type[i]].get_height() # 적 기체 이미지 높이(픽셀 수)
                r = int((w + h) / 4) + 12               # 히트 체크에 사용할 거리 계산
                for n in range(MISSILE_MAX):
                    if msl_f[n] == True and get_dis(emy_x[i], emy_y[i], msl_x[n], msl_y[n]) < r * r: # 플레이어 기체 탄환과 접촉 여부 판단
                        msl_f[n] = False                # 탄환 삭제
                        set_effect(emy_x[i], emy_y[i])  # 폭발 이펙트
                        score = score + 100             # 점수 증가
                        emy_f[i] = False                # 적 기체 삭제
                        if ss_shield < 100:             # 플레이어 실드량이 100 미만이면
                            ss_shield = ss_shield + 1   # 실드량 증가
            img_rz = pygame.transform.rotozoom(img_enemy[png], ang, 1.0)
            scrn.blit(img_rz, [emy_x[i] - img_rz.get_width() / 2, emy_y[i] - img_rz.get_height() / 2])

def set_effect(x, y):   # 폭발 설정
    global eff_no
    eff_p[eff_no] = 1   # 폭발 연출 이미지 번호 대입
    eff_x[eff_no] = x   # 폭발 연출 X, Y 좌표 대입
    eff_y[eff_no] = y
    eff_no = (eff_no + 1) % EFFECT_MAX  # 다음 설정을 위한 번호 계산

def draw_effect(scrn):  # 폭발 연출
    for i in range(EFFECT_MAX):
        if eff_p[i] > 0:            # 폭발 연출 중이면
            scrn.blit(img_explode[eff_p[i]], [eff_x[i] - 48, eff_y[i] - 48]) # 폭발 연출 표시
            eff_p[i] = eff_p[i] + 1 # eff_p 값 1 증가
            if eff_p[i] == 6:       # eff_p가 6이 되었다면
                eff_p[i] = 0        # eff_p에 0 대입 후 연출 종료

def main():
    global idx, tmr, score, bg_y, ss_x, ss_y, ss_d, ss_shield, ss_muteki

    pygame.init()
    pygame.display.set_caption('Galaxy Lancer')
    screen = pygame.display.set_mode((960, 720))
    clock = pygame.time.Clock()

    while True:
        tmr = tmr + 1
        for e in pygame.event.get():
            if e.type == QUIT:
                pygame.quit()
                sys.exit()
            if e.type == KEYDOWN:
                if e.key == K_F1:
                    screen = pygame.display.set_mode((960, 720), pygame.FULLSCREEN)
                if e.key == K_F2 or e.key == K_ESCAPE:
                    screen = pygame.display.set_mode((960, 720))

        # 배경 스크롤
        bg_y = (bg_y + 16) % 720                    # 배경 스크롤 위치 계산
        screen.blit(img_galaxy, [0, bg_y - 720])    # 배경 그리기(위쪽)
        screen.blit(img_galaxy, [0, bg_y])          # 배경 그리기(아래쪽)

        key = pygame.key.get_pressed()              # key에 모든 키 상태를 대입

        if idx == 0:    # 타이틀
            img_rz = pygame.transform.rotozoom(img_title[0], -tmr % 360, 1.0)                   # 로고 뒤에서 회전하는 소용돌이 이미지
            screen.blit(img_rz, [480 - img_rz.get_width() / 2, 280 - img_rz.get_height() / 2])
            screen.blit(img_title[1], [70, 160])
            draw_text(screen, 'Press [SPACE] to start!', 480, 600, 50, SILVER)
            if key[K_SPACE] == 1:   # 스페이스 키를 눌렀다면 게임 관련 변수 초기 값 대입
                idx = 1
                tmr = 0
                score = 0
                score = 0
                ss_x = 480
                ss_y = 600
                ss_d = 0
                ss_shield = 100
                ss_muteki = 0
                for i in range(ENEMY_MAX):
                    emy_f[i] = False
                for i in range(MISSILE_MAX):
                    msl_f[i] = False

        if idx == 1:    # 게임 플레이 중
            move_starship(screen, key)  # 플레이어 기체 이동
            move_missile(screen)        # 플레이어 탄환 이동
            bring_enemy()               # 적 기체 등장
            move_enemy(screen)          # 적 기체 이동
            if tmr == 30 * 60:          # 약 1분 뒤에
                idx = 3                 # 게임 클리어로 이동
                tmr = 0

        if idx == 2:    # 게임 오버
            move_missile(screen)
            move_enemy(screen)
            draw_text(screen, 'GAME OVER', 480, 300, 80, SILVER)
            if tmr == 150:
                idx = 0 # 타이틀 화면으로 이동
                tmr = 0

        if idx == 3:    # 게임 클리어
            move_starship(screen, key)
            move_missile(screen)
            draw_text(screen, 'GAME CLEAR', 480, 300, 80, SILVER)
            if tmr == 150:
                idx = 0 # 타이틀 화면으로 이동
                tmr = 0
        
        draw_effect(screen)
        draw_text(screen, 'SCORE ' + str(score), 200, 30, 50, SILVER)
        if idx != 0: # 타이틀 화면이 아니면
            screen.blit(img_shield, [40, 680])  # 실드를 그림
            pygame.draw.rect(screen, (64, 32, 32), [40 + ss_shield * 4, 680, (100 - ss_shield) * 4, 12])

        pygame.display.update()
        clock.tick(30)

if __name__ == '__main__':
    main()

추가 및 변경된 점 : draw_text() 함수 추가, move_starship() 함수에 게임 플레이 중에만 히트 체크하도록 하고 실드가 0이되면 게임 오버로 이동하는 부분 추가, move_enemy() 함수에 점수 증가 부분 추가, main() 함수에 idx와 tmr을 사용하여 구현

약 1분동안 버티면 게임 클리어, 도중에 실드가 0이되면 게임 오버가 된다.

게임 흐름은 idx와 tmr로 관리한다.