Skip to content

Latest commit

 

History

History
1219 lines (1074 loc) · 50.1 KB

220115.md

File metadata and controls

1219 lines (1074 loc) · 50.1 KB

Day 96

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

Chapter 11

시작부터 골까지의 흐름

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

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
YELLOW = (255, 224, 0)
GREEN = (0, 255, 0) # 색 정의(초록색)

idx = 0 # 인덱스 변수
tmr = 0
se_crash = None

DATA_LR = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2,
3, 2, 1, 0, 2, 4, 2, 4, 2, 0, 0, 0, -2, -2, 
-4, -4, -2, -1, 0, 0, 0, 0, 0, 0, 0]
DATA_UD = [0, 0, 1, 2, 3, 2, 1, 0, -2, -4, -4, 0, 0,
0, 0, 0, -1, -2, -3, -4, -3, -2, -1, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, -3, 3, 0, -6, 6, 0]
CLEN = len(DATA_LR)

BOARD = 120
CMAX = BOARD * CLEN
curve = [0] * CMAX
updown = [0] * CMAX
object_left = [0] * CMAX
object_right = [0] * CMAX

CAR = 30            
car_x = [0] * CAR
car_y = [0] * CAR
car_lr = [0] * CAR
car_spd = [0] * CAR
PLCAR_Y = 10

def make_course():
    for i in range(CLEN):
        lr1 = DATA_LR[i]
        lr2 = DATA_LR[(i + 1) % CLEN]
        ud1 = DATA_UD[i]
        ud2 = DATA_UD[(i + 1) % CLEN]
        for j in range(BOARD):
            pos = j + BOARD * i
            curve[pos] = lr1 * (BOARD - j) / BOARD + lr2 * j / BOARD
            updown[pos] = ud1 * (BOARD - j) / BOARD + ud2 * j / BOARD
            if j == 60:
                object_right[pos] = 1
            if i % 8 < 7:
                if j % 12 == 0:
                    object_left[pos] = 2
            else:
                if j % 20 == 0:
                    object_left[pos] = 3
            if j % 12 == 6:
                object_left[pos] = 9

def draw_obj(bg, img, x, y, sc):
    img_rz = pygame.transform.rotozoom(img, 0, sc)
    w = img_rz.get_width()
    h = img_rz.get_height()
    bg.blit(img_rz, [x - w / 2, y - h])

def draw_shadow(bg, x, y, siz):
    shadow = pygame.Surface([siz, siz / 4])
    shadow.fill(RED)
    shadow.set_colorkey(RED)
    shadow.set_alpha(128)
    pygame.draw.ellipse(shadow, BLACK, [0, 0, siz, siz / 4])
    bg.blit(shadow, [x - siz / 2, y - siz / 4])

def init_car():
    for i in range(1, CAR):
        car_x[i] = random.randint(50, 750)
        car_y[i] = random.randint(200, CMAX - 200)
        car_lr[i] = 0
        car_spd[i] = random.randint(100, 200)
    car_x[0] = 400
    car_y[i] = 0
    car_lr[0] = 0
    car_spd[0] = 0

def drive_car(key):
    global idx, tmr # 전역 변수 선언
    if key[K_LEFT] == 1:
        if car_lr[0] > -3:
            car_lr[0] -= 1
        car_x[0] = car_x[0] + (car_lr[0] - 3) * car_spd[0] / 100 - 5
    elif key[K_RIGHT] == 1:
        if car_lr[0] < 3:
            car_lr[0] += 1
        car_x[0] = car_x[0] + (car_lr[0] + 3) * car_spd[0] / 100 + 5
    else:
        car_lr[0] = int(car_lr[0] * 0.9)

    if key[K_a] == 1:
        car_spd[0] += 3
    elif key[K_z] == 1:
        car_spd[0] -= 10
    else:
        car_spd[0] -= 0.25

    if car_spd[0] < 0:
        car_spd[0] = 0
    if car_spd[0] > 320:
        car_spd[0] = 320

    car_x[0] -= car_spd[0] * curve[int(car_y[0] + PLCAR_Y) % CMAX] / 50
    if car_x[0] < 0:
        car_x[0] = 0
        car_spd[0] *= 0.9
    if car_x[0] > 800:
        car_x[0] = 800
        car_spd[0] *= 0.9

    car_y[0] = car_y[0] + car_spd[0] / 100
    if car_y[0] > CMAX - 1:
        car_y[0] -= CMAX
        idx = 3 # idx에 3 대입
        tmr = 0 # tmr에 0 대입

def move_car(cs):
    for i in range(cs, CAR):
        if car_spd[i] < 100:
            car_spd[i] += 3
        if i == tmr % 120:
            car_lr[i] += random.choice([-1, 0, 1])
            if car_lr[i] < -3: car_lr[i] = -3
            if car_lr[i] > 3: car_lr[i] = 3
        car_x[i] = car_x[i] + car_lr[i] * car_spd[i] / 100
        if car_x[i] < 50:
            car_x[i] = 50
            car_lr[i] = int(car_lr[i] * 0.9)
        if car_x[i] > 750:
            car_x[i] = 750
            car_lr[i] = int(car_lr[i] * 0.9)
        car_y[i] += car_spd[i] / 100
        if car_y[i] > CMAX - 1:
            car_y[i] -= CMAX
        if idx == 2:    # 레이스 중이면 히트 체크
            cx = car_x[i] - car_x[0]
            cy = car_y[i] - (car_y[0] + PLCAR_Y) % CMAX
            if -100 <= cx and cx <= 100 and -10 <= cy and cy <= 10:
                car_x[0] -= cx / 4
                car_x[i] += cx / 4
                car_spd[0], car_spd[i] = car_spd[i] * 0.3, car_spd[0] * 0.3
                se_crash.play()

def draw_text(scrn, txt, x, y, col, fnt):
    sur = fnt.render(txt, True, BLACK)
    x -= sur.get_width() / 2
    y -= sur.get_height() / 2
    scrn.blit(sur, [x + 2, y + 2])
    sur = fnt.render(txt, True, col)
    scrn.blit(sur, [x, y])

def main():
    global idx, tmr, se_crash   # 전역 변수 선언
    pygame.init()
    pygame.display.set_caption("Python Racer")
    screen = pygame.display.set_mode((800, 600))
    clock = pygame.time.Clock()
    fnt_s = pygame.font.Font(None, 40)  # 폰트 객체 생성, 작은 크기 문자
    fnt_m = pygame.font.Font(None, 50)
    fnt_l = pygame.font.Font(None, 120) # 폰트 객체 생성, 큰 크기 문자

    img_title = pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/title.png').convert_alpha()  # 타이틀 로고 이미지 로딩 변수
    img_bg = pygame.image.load("Python_workspace/python_game/Chapter11/image_pr/bg.png").convert()
    img_sea = pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/sea.png').convert_alpha()
    img_obj = [
        None,
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/board.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/yashi.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/yacht.png').convert_alpha()
    ]
    img_car = [
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car00.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car01.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car02.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car03.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car04.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car05.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car06.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car10.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car11.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car12.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car13.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car14.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car15.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car16.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car20.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car21.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car22.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car23.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car24.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car25.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car26.png').convert_alpha()
    ]

    se_crash = pygame.mixer.Sound('Python_workspace/python_game/Chapter11/sound_pr/crash.ogg')

    # 도로 판의 기본 형태 계산
    BOARD_W = [0] * BOARD
    BOARD_H = [0] * BOARD
    BOARD_UD = [0] * BOARD
    for i in range(BOARD):
        BOARD_W[i] = 10 + (BOARD - i) * (BOARD - i) / 12
        BOARD_H[i] = 3.4 * (BOARD - i) / BOARD
        BOARD_UD[i] = 2 * math.sin(math.radians(i * 1.5))

    make_course()
    init_car()

    vertical = 0

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

        # 화면에 그릴 도로 X 좌표와 높낮이 계산
        di = 0
        ud = 0
        board_x = [0] * BOARD
        board_ud = [0] * BOARD
        for i in range(BOARD):
            di += curve[int(car_y[0] + i) % CMAX]
            ud += updown[int(car_y[0] + i) % CMAX]
            board_x[i] = 400 - BOARD_W[i] * car_x[0] / 800 + di / 2
            board_ud[i] = ud / 30

        horizon = 400 + int(ud / 3)
        sy = horizon


        vertical = vertical - int(car_spd[0] * di / 8000)
        if vertical < 0:
            vertical += 800
        if vertical >= 800:
            vertical -= 800

        # 필드 그리기
        screen.fill((0, 56, 255))
        screen.blit(img_bg, [vertical - 800, horizon - 400])
        screen.blit(img_bg, [vertical, horizon - 400])
        screen.blit(img_sea, [board_x[BOARD - 1] - 780, sy])

        # 그리기 데이터를 기초로 도로 그리기
        for i in range(BOARD - 1, 0, -1):
            ux = board_x[i]
            uy = sy - BOARD_UD[i] * board_ud[i]
            uw = BOARD_W[i]
            sy = sy + BOARD_H[i] * (600 - horizon) / 200
            bx = board_x[i - 1]
            by = sy - BOARD_UD[i - 1] * board_ud[i - 1]
            bw = BOARD_W[i - 1]
            col = (160, 160, 160)
            if int(car_y[0] + i) % CMAX == PLCAR_Y + 10:    # 골 위치라면
                col = (192, 0, 0)                               # 빨간색 선 값 대입
            pygame.draw.polygon(screen, col, [[ux, uy], [ux + uw, uy], [bx + bw, by], [bx, by]])

            if int(car_y[0] + i) % 10 <= 4:
                pygame.draw.polygon(screen, YELLOW, [[ux, uy], [ux + uw * 0.02, uy], [bx + bw * 0.02, by], [bx, by]])
                pygame.draw.polygon(screen, YELLOW, [[ux + uw * 0.98, uy], [ux + uw, uy], [bx + bw, by], [bx + bw * 0.98, by]])
            
            if int(car_y[0] + i) % 20 <= 10:
                pygame.draw.polygon(screen, WHITE, [[ux + uw * 0.24, uy], [ux + uw * 0.26, uy], [bx + bw * 0.26, by], [bx + bw * 0.24, by]])
                pygame.draw.polygon(screen, WHITE, [[ux + uw * 0.49, uy], [ux + uw * 0.51, uy], [bx + bw * 0.51, by], [bx + bw * 0.49, by]])
                pygame.draw.polygon(screen, WHITE, [[ux + uw * 0.74, uy], [ux + uw * 0.76, uy], [bx + bw * 0.76, by], [bx + bw * 0.74, by]])

            scale  = 1.5 * BOARD_W[i] / BOARD_W[0]
            obj_l = object_left[int(car_y[0] + i) % CMAX]
            if obj_l == 2:
                draw_obj(screen, img_obj[obj_l], ux - uw * 0.05, uy, scale)
            if obj_l == 3:
                draw_obj(screen, img_obj[obj_l], ux - uw * 0.5, uy, scale)
            if obj_l == 9:
                screen.blit(img_sea, [ux - uw * 0.5 - 780, uy])

            obj_r = object_right[int(car_y[0] + i) % CMAX]
            if obj_r == 1:
                draw_obj(screen, img_obj[obj_r], ux + uw * 1.3, uy, scale)

            for c in range(1, CAR):
                if int(car_y[c]) % CMAX == int(car_y[0] + i) % CMAX:
                    lr = int(4 * (car_x[0] - car_x[c]) / 800)
                    if lr < -3: lr = -3
                    if lr > 3: lr = 3
                    draw_obj(screen, img_car[(c % 3) * 7 + 3 + lr], ux + car_x[c] * BOARD_W[i] / 800, uy, 0.05 + BOARD_W[i] / BOARD_W[0])

            if i == PLCAR_Y:
                draw_shadow(screen, ux + car_x[0] * BOARD_W[i] / 800, uy, 200 * BOARD_W[i] / BOARD_W[0])
                draw_obj(screen, img_car[3 + car_lr[0]], ux + car_x[0] * BOARD_W[i] / 800, uy, 0.05 + BOARD_W[i] / BOARD_W[0])

        draw_text(screen, str(int(car_spd[0])) + 'km/h', 680, 30, RED, fnt_m)
        key = pygame.key.get_pressed()

        if idx == 0:        # 타이틀 화면
            screen.blit(img_title, [120, 120])  # 타이틀 로고 표시
            draw_text(screen, '[A] Start game', 400, 320, WHITE, fnt_m)
            move_car(0)         # 모든 차량 이동
            if key[K_a] != 0:   # [A] 키를 눌렀다면
                init_car()          # 모든 차량 초기 위치로
                idx = 1
                tmr = 0
            
        if idx == 1:        # 카운트 다운
            n = 3 - int(tmr / 60)   # 카운트 다운 수 계산 대입
            draw_text(screen, str(n), 400, 240, YELLOW, fnt_l)  # 해당 수 표시
            if tmr == 179:  # tmr이 179가 되면
                pygame.mixer.music.load('Python_workspace/python_game/Chapter11/sound_pr/bgm.ogg')  # BGM 로딩
                pygame.mixer.music.play(-1) # 무한 반복 출력
                idx = 2
                tmr = 0
        
        if idx == 2:        # 레이싱 중
            if tmr < 60:    # 60 프레임 이내
                draw_text(screen, 'Go!', 400, 240, RED, fnt_l)  # GO! 표시
            drive_car(key)  # 플레이어 차량 조작
            move_car(1)     # COM 차량 이동

        if idx == 3:        # 골
            if tmr == 1:    # tmr 값이 1이면
                pygame.mixer.music.stop()   # BGM 중지
            if tmr == 30:   # tmr 값이 30이면
                pygame.mixer.music.load('Python_workspace/python_game/Chapter11/sound_pr/goal.ogg') # 징글 로딩
                pygame.mixer.music.play(0)  # 징글 1회 출력
            draw_text(screen, 'GOAL!', 400, 240, GREEN, fnt_l)  # GOAL! 표시
            car_spd[0] = car_spd[0] * 0.96          # 플레이어 차량 속도 감속
            car_y[0] = car_y[0] + car_spd[0] / 100  # 코스 위를 이동
            move_car(1)         # COM 차량 이동
            if tmr > 60 * 8:    # 8초가 경과하면 타이틀 화면으로 이동
                idx = 0

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

if __name__ == '__main__':
    main()

타이틀 화면이 나오고 [A] 키를 누르면 카운트 다운을 하고 출발해서 한바퀴 돌아서 골로 들어오는 것으로 게임이 끝난다.

각 인덱스 값에 따른 처리는 다음과 같다.

idx 값 처리
0 타이틀 화면
- [A] 키를 누르면 차량 관리 리스트 초기값 대입 후 idx에 1 대입
1 시작 전 카운트다운 수행 화면
- 3, 2, 1을 표시한 뒤, idx에 2 대입
2 레이스 중 화면(게임 플레이 중 화면)
- 최초 2초간 'GO!' 표시
- 플레이어 차량을 움직이는 함수로 골인지 판단 후, 골이면 idx에 3 대입
3 골 화면
- 'GOAL!' 표시
- 약 8초간 대기한 후, idx에 0 대입, 타이틀 화면으로 복귀
col = (160, 160, 160)
if int(car_y[0] + i) % CMAX == PLCAR_Y + 10:    # 골 위치라면
    col = (192, 0, 0)                               # 빨간색 선 값 대입
pygame.draw.polygon(screen, col, [[ux, uy], [ux + uw, uy], [bx + bw, by], [bx, by]])

골 지점을 쉽게 알 수 있도록 그 위치에 빨간색 가로 선을 그리는 부분이다.
조건식에서 int(car_y[0] + i) % CMAX는 플레이어가 보는 도로를 구성하는 판의 번호이다. 도로는 끝점과 시작점을 연결해서 반복하고 있으므로 % CMAX를 이용한다. int(car_y[0] + i) % CMAX 값과 PLCAR_Y + 10번 판을 빨간색으로 함으로써 골 라인을 그릴 수 있다. PLCAR_Y + 10의 실제 값은 20이다.

랩 타임 추가하기

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

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
YELLOW = (255, 224, 0)
GREEN = (0, 255, 0)

idx = 0
tmr = 0
laps = 0    # 주행 바퀴 관리 변수
rec = 0     # 주행 시간 측정 변수
recbk = 0   # 랩 타임 계산용 변수
se_crash = None

DATA_LR = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2,
3, 2, 1, 0, 2, 4, 2, 4, 2, 0, 0, 0, -2, -2, 
-4, -4, -2, -1, 0, 0, 0, 0, 0, 0, 0]
DATA_UD = [0, 0, 1, 2, 3, 2, 1, 0, -2, -4, -4, 0, 0,
0, 0, 0, -1, -2, -3, -4, -3, -2, -1, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, -3, 3, 0, -6, 6, 0]
CLEN = len(DATA_LR)

BOARD = 120
CMAX = BOARD * CLEN
curve = [0] * CMAX
updown = [0] * CMAX
object_left = [0] * CMAX
object_right = [0] * CMAX

CAR = 30            
car_x = [0] * CAR
car_y = [0] * CAR
car_lr = [0] * CAR
car_spd = [0] * CAR
PLCAR_Y = 10

LAPS = 3                        # 골까지의 바퀴 수
laptime = ["0'00.00"] * LAPS    # 랩 타임 표시용 리스트

def make_course():
    for i in range(CLEN):
        lr1 = DATA_LR[i]
        lr2 = DATA_LR[(i + 1) % CLEN]
        ud1 = DATA_UD[i]
        ud2 = DATA_UD[(i + 1) % CLEN]
        for j in range(BOARD):
            pos = j + BOARD * i
            curve[pos] = lr1 * (BOARD - j) / BOARD + lr2 * j / BOARD
            updown[pos] = ud1 * (BOARD - j) / BOARD + ud2 * j / BOARD
            if j == 60:
                object_right[pos] = 1
            if i % 8 < 7:
                if j % 12 == 0:
                    object_left[pos] = 2
            else:
                if j % 20 == 0:
                    object_left[pos] = 3
            if j % 12 == 6:
                object_left[pos] = 9

def time_str(val):  # **'**.** 시간 문자 생성 함수
    sec = int(val)              # 인수를 초 단위 정수로 변환해 sec에 대입
    ms = int((val - sec) * 100) # 소수점 이하 값을 ms에 대입
    mi = int(sec / 60)          # 분을 mi에 대입
    return "{}'{:02}.{:02}".format(mi, sec % 60, ms)    #  **'**.** 문자열 반환

def draw_obj(bg, img, x, y, sc):
    img_rz = pygame.transform.rotozoom(img, 0, sc)
    w = img_rz.get_width()
    h = img_rz.get_height()
    bg.blit(img_rz, [x - w / 2, y - h])

def draw_shadow(bg, x, y, siz):
    shadow = pygame.Surface([siz, siz / 4])
    shadow.fill(RED)
    shadow.set_colorkey(RED)
    shadow.set_alpha(128)
    pygame.draw.ellipse(shadow, BLACK, [0, 0, siz, siz / 4])
    bg.blit(shadow, [x - siz / 2, y - siz / 4])

def init_car():
    for i in range(1, CAR):
        car_x[i] = random.randint(50, 750)
        car_y[i] = random.randint(200, CMAX - 200)
        car_lr[i] = 0
        car_spd[i] = random.randint(100, 200)
    car_x[0] = 400
    car_y[i] = 0
    car_lr[0] = 0
    car_spd[0] = 0

def drive_car(key):
    global idx, tmr, laps, recbk    # 전역 변수 선언
    if key[K_LEFT] == 1:
        if car_lr[0] > -3:
            car_lr[0] -= 1
        car_x[0] = car_x[0] + (car_lr[0] - 3) * car_spd[0] / 100 - 5
    elif key[K_RIGHT] == 1:
        if car_lr[0] < 3:
            car_lr[0] += 1
        car_x[0] = car_x[0] + (car_lr[0] + 3) * car_spd[0] / 100 + 5
    else:
        car_lr[0] = int(car_lr[0] * 0.9)

    if key[K_a] == 1:
        car_spd[0] += 3
    elif key[K_z] == 1:
        car_spd[0] -= 10
    else:
        car_spd[0] -= 0.25

    if car_spd[0] < 0:
        car_spd[0] = 0
    if car_spd[0] > 320:
        car_spd[0] = 320

    car_x[0] -= car_spd[0] * curve[int(car_y[0] + PLCAR_Y) % CMAX] / 50
    if car_x[0] < 0:
        car_x[0] = 0
        car_spd[0] *= 0.9
    if car_x[0] > 800:
        car_x[0] = 800
        car_spd[0] *= 0.9

    car_y[0] = car_y[0] + car_spd[0] / 100
    if car_y[0] > CMAX - 1:
        car_y[0] -= CMAX
        laptime[laps] = time_str(rec - recbk)   # 랩 타임 계산 후 대입
        recbk = rec         # 현재 타임 저장
        laps += 1           # 완주 횟수 1 증가
        if laps == LAPS:    # 완주 횟수가 LAPS와 같다면
            idx = 3             # idx에 3 대입
            tmr = 0             # tmr에 0 대입

def move_car(cs):
    for i in range(cs, CAR):
        if car_spd[i] < 100:
            car_spd[i] += 3
        if i == tmr % 120:
            car_lr[i] += random.choice([-1, 0, 1])
            if car_lr[i] < -3: car_lr[i] = -3
            if car_lr[i] > 3: car_lr[i] = 3
        car_x[i] = car_x[i] + car_lr[i] * car_spd[i] / 100
        if car_x[i] < 50:
            car_x[i] = 50
            car_lr[i] = int(car_lr[i] * 0.9)
        if car_x[i] > 750:
            car_x[i] = 750
            car_lr[i] = int(car_lr[i] * 0.9)
        car_y[i] += car_spd[i] / 100
        if car_y[i] > CMAX - 1:
            car_y[i] -= CMAX
        if idx == 2:
            cx = car_x[i] - car_x[0]
            cy = car_y[i] - (car_y[0] + PLCAR_Y) % CMAX
            if -100 <= cx and cx <= 100 and -10 <= cy and cy <= 10:
                car_x[0] -= cx / 4
                car_x[i] += cx / 4
                car_spd[0], car_spd[i] = car_spd[i] * 0.3, car_spd[0] * 0.3
                se_crash.play()

def draw_text(scrn, txt, x, y, col, fnt):
    sur = fnt.render(txt, True, BLACK)
    x -= sur.get_width() / 2
    y -= sur.get_height() / 2
    scrn.blit(sur, [x + 2, y + 2])
    sur = fnt.render(txt, True, col)
    scrn.blit(sur, [x, y])

def main():
    global idx, tmr, laps, rec, recbk, se_crash # 전역 변수 선언
    pygame.init()
    pygame.display.set_caption("Python Racer")
    screen = pygame.display.set_mode((800, 600))
    clock = pygame.time.Clock()
    fnt_s = pygame.font.Font(None, 40)
    fnt_m = pygame.font.Font(None, 50)
    fnt_l = pygame.font.Font(None, 120)

    img_title = pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/title.png').convert_alpha()
    img_bg = pygame.image.load("Python_workspace/python_game/Chapter11/image_pr/bg.png").convert()
    img_sea = pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/sea.png').convert_alpha()
    img_obj = [
        None,
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/board.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/yashi.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/yacht.png').convert_alpha()
    ]
    img_car = [
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car00.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car01.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car02.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car03.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car04.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car05.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car06.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car10.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car11.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car12.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car13.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car14.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car15.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car16.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car20.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car21.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car22.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car23.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car24.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car25.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car26.png').convert_alpha()
    ]

    se_crash = pygame.mixer.Sound('Python_workspace/python_game/Chapter11/sound_pr/crash.ogg')

    # 도로 판의 기본 형태 계산
    BOARD_W = [0] * BOARD
    BOARD_H = [0] * BOARD
    BOARD_UD = [0] * BOARD
    for i in range(BOARD):
        BOARD_W[i] = 10 + (BOARD - i) * (BOARD - i) / 12
        BOARD_H[i] = 3.4 * (BOARD - i) / BOARD
        BOARD_UD[i] = 2 * math.sin(math.radians(i * 1.5))

    make_course()
    init_car()

    vertical = 0

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

        # 화면에 그릴 도로 X 좌표와 높낮이 계산
        di = 0
        ud = 0
        board_x = [0] * BOARD
        board_ud = [0] * BOARD
        for i in range(BOARD):
            di += curve[int(car_y[0] + i) % CMAX]
            ud += updown[int(car_y[0] + i) % CMAX]
            board_x[i] = 400 - BOARD_W[i] * car_x[0] / 800 + di / 2
            board_ud[i] = ud / 30

        horizon = 400 + int(ud / 3)
        sy = horizon


        vertical = vertical - int(car_spd[0] * di / 8000)
        if vertical < 0:
            vertical += 800
        if vertical >= 800:
            vertical -= 800

        # 필드 그리기
        screen.fill((0, 56, 255))
        screen.blit(img_bg, [vertical - 800, horizon - 400])
        screen.blit(img_bg, [vertical, horizon - 400])
        screen.blit(img_sea, [board_x[BOARD - 1] - 780, sy])

        # 그리기 데이터를 기초로 도로 그리기
        for i in range(BOARD - 1, 0, -1):
            ux = board_x[i]
            uy = sy - BOARD_UD[i] * board_ud[i]
            uw = BOARD_W[i]
            sy = sy + BOARD_H[i] * (600 - horizon) / 200
            bx = board_x[i - 1]
            by = sy - BOARD_UD[i - 1] * board_ud[i - 1]
            bw = BOARD_W[i - 1]
            col = (160, 160, 160)
            if int(car_y[0] + i) % CMAX == PLCAR_Y + 10:
                col = (192, 0, 0)
            pygame.draw.polygon(screen, col, [[ux, uy], [ux + uw, uy], [bx + bw, by], [bx, by]])

            if int(car_y[0] + i) % 10 <= 4:
                pygame.draw.polygon(screen, YELLOW, [[ux, uy], [ux + uw * 0.02, uy], [bx + bw * 0.02, by], [bx, by]])
                pygame.draw.polygon(screen, YELLOW, [[ux + uw * 0.98, uy], [ux + uw, uy], [bx + bw, by], [bx + bw * 0.98, by]])
            
            if int(car_y[0] + i) % 20 <= 10:
                pygame.draw.polygon(screen, WHITE, [[ux + uw * 0.24, uy], [ux + uw * 0.26, uy], [bx + bw * 0.26, by], [bx + bw * 0.24, by]])
                pygame.draw.polygon(screen, WHITE, [[ux + uw * 0.49, uy], [ux + uw * 0.51, uy], [bx + bw * 0.51, by], [bx + bw * 0.49, by]])
                pygame.draw.polygon(screen, WHITE, [[ux + uw * 0.74, uy], [ux + uw * 0.76, uy], [bx + bw * 0.76, by], [bx + bw * 0.74, by]])

            scale  = 1.5 * BOARD_W[i] / BOARD_W[0]
            obj_l = object_left[int(car_y[0] + i) % CMAX]
            if obj_l == 2:
                draw_obj(screen, img_obj[obj_l], ux - uw * 0.05, uy, scale)
            if obj_l == 3:
                draw_obj(screen, img_obj[obj_l], ux - uw * 0.5, uy, scale)
            if obj_l == 9:
                screen.blit(img_sea, [ux - uw * 0.5 - 780, uy])

            obj_r = object_right[int(car_y[0] + i) % CMAX]
            if obj_r == 1:
                draw_obj(screen, img_obj[obj_r], ux + uw * 1.3, uy, scale)

            for c in range(1, CAR):
                if int(car_y[c]) % CMAX == int(car_y[0] + i) % CMAX:
                    lr = int(4 * (car_x[0] - car_x[c]) / 800)
                    if lr < -3: lr = -3
                    if lr > 3: lr = 3
                    draw_obj(screen, img_car[(c % 3) * 7 + 3 + lr], ux + car_x[c] * BOARD_W[i] / 800, uy, 0.05 + BOARD_W[i] / BOARD_W[0])

            if i == PLCAR_Y:
                draw_shadow(screen, ux + car_x[0] * BOARD_W[i] / 800, uy, 200 * BOARD_W[i] / BOARD_W[0])
                draw_obj(screen, img_car[3 + car_lr[0]], ux + car_x[0] * BOARD_W[i] / 800, uy, 0.05 + BOARD_W[i] / BOARD_W[0])

        draw_text(screen, str(int(car_spd[0])) + 'km/h', 680, 30, RED, fnt_m)
        draw_text(screen, 'lap {}/{}'.format(laps, LAPS), 100, 30, WHITE, fnt_m)    # 주행 횟수 표시
        draw_text(screen, 'time ' + time_str(rec), 100, 80, GREEN, fnt_s)           # 시간 표시
        for i in range(LAPS):
            draw_text(screen, laptime[i], 80, 130 + 40 * i, YELLOW, fnt_s)          # 랩 타임 표시
        key = pygame.key.get_pressed()

        if idx == 0:        # 타이틀 화면
            screen.blit(img_title, [120, 120])
            draw_text(screen, '[A] Start game', 400, 320, WHITE, fnt_m)
            move_car(0)
            if key[K_a] != 0:
                init_car()
                idx = 1
                tmr = 0
                laps = 0                    # 주행 횟수에 0 대입
                rec = 0                     # 주행 시간에 0 대입
                recbk = 0                   # 랩 타임 계산 변수 0 대입
                for i in range(LAPS):
                    laptime[i] = "0'00.00"  # 랩 타임 "0'00.00" 초기화
            
        if idx == 1:        # 카운트 다운
            n = 3 - int(tmr / 60)
            draw_text(screen, str(n), 400, 240, YELLOW, fnt_l)
            if tmr == 179:
                pygame.mixer.music.load('Python_workspace/python_game/Chapter11/sound_pr/bgm.ogg')
                pygame.mixer.music.play(-1)
                idx = 2
                tmr = 0
        
        if idx == 2:        # 레이싱 중
            if tmr < 60:
                draw_text(screen, 'Go!', 400, 240, RED, fnt_l)
            rec = rec + 1 / 60  # 주행 시간 카운트
            drive_car(key)
            move_car(1)

        if idx == 3:        # 골
            if tmr == 1:
                pygame.mixer.music.stop()
            if tmr == 30:
                pygame.mixer.music.load('Python_workspace/python_game/Chapter11/sound_pr/goal.ogg')
                pygame.mixer.music.play(0)
            draw_text(screen, 'GOAL!', 400, 240, GREEN, fnt_l)
            car_spd[0] = car_spd[0] * 0.96
            car_y[0] = car_y[0] + car_spd[0] / 100
            move_car(1)
            if tmr > 60 * 8:
                idx = 0

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

if __name__ == '__main__':
    main()

화면 왼쪽 위에 랩 수, 현재 시간, 랩 타임을 표시한다.

def time_str(val):  # **'**.** 시간 문자 생성 함수
    sec = int(val)              # 인수를 초 단위 정수로 변환해 sec에 대입
    ms = int((val - sec) * 100) # 소수점 이하 값을 ms에 대입
    mi = int(sec / 60)          # 분을 mi에 대입
    return "{}'{:02}.{:02}".format(mi, sec % 60, ms)    #  **'**.** 문자열 반환

time_str() 함수는 시간 값에서 문자열을 만드는 함수다. 초를 정수 부분과 소수 부분으로 나눠(변수 sec와 ms에 대입) 초를 분 값(변수 mi에 대입)으로 계산한 뒤 문자열을 반환한다.

시간 측정은 변수 rec, recbk로 수행하고, 리스트 laptime으로 랩 타임을 관리한다.

rec = rec + 1 / 60  # 주행 시간 카운트

이 프로그램은 초당 60프레임을 처리하므로 1/60초를 더하는 것이다.

if car_y[0] > CMAX - 1:
    car_y[0] -= CMAX
    laptime[laps] = time_str(rec - recbk)   # 랩 타임 계산 후 대입
    recbk = rec         # 현재 타임 저장
    laps += 1           # 완주 횟수 1 증가
    if laps == LAPS:    # 완주 횟수가 LAPS와 같다면
        idx = 3             # idx에 3 대입
        tmr = 0             # tmr에 0 대입

drive_car() 함수의 일부다. 플레이어 차량은 한 바퀴를 돌 때마다 랩 타임을 기록하고 세 바퀴를 돌면 완주하도록 한다.

플레이어 차량의 코스 상 위치를 시작저믕로 되돌리는 타이밍에서 laptime[laps] = time_str(rec - recbk)를 계산해 그 바퀴의 랩 타임을 리스트에 대입한다. 그리고 다음 랩 타임을 측정하기 위해 recbk = rec으로 recbk에 해당 시간을 대입한다. 이렇게 한 바퀴를 돌 때마다 시간을 측정한다. 또한, 몇 바퀴를 주행했는지 센 뒤, 세 바퀴를 돌았다면 완주 처리를 실행한다.

차종 선택 추가하기

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

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
YELLOW = (255, 224, 0)
GREEN = (0, 255, 0)

idx = 0
tmr = 0
laps = 0
rec = 0
recbk = 0
se_crash = None
mycar = 0   # 차종 선택 관리 변수

DATA_LR = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2,
3, 2, 1, 0, 2, 4, 2, 4, 2, 0, 0, 0, -2, -2, 
-4, -4, -2, -1, 0, 0, 0, 0, 0, 0, 0]
DATA_UD = [0, 0, 1, 2, 3, 2, 1, 0, -2, -4, -4, 0, 0,
0, 0, 0, -1, -2, -3, -4, -3, -2, -1, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, -3, 3, 0, -6, 6, 0]
CLEN = len(DATA_LR)

BOARD = 120
CMAX = BOARD * CLEN
curve = [0] * CMAX
updown = [0] * CMAX
object_left = [0] * CMAX
object_right = [0] * CMAX

CAR = 30            
car_x = [0] * CAR
car_y = [0] * CAR
car_lr = [0] * CAR
car_spd = [0] * CAR
PLCAR_Y = 10

LAPS = 3
laptime = ["0'00.00"] * LAPS

def make_course():
    for i in range(CLEN):
        lr1 = DATA_LR[i]
        lr2 = DATA_LR[(i + 1) % CLEN]
        ud1 = DATA_UD[i]
        ud2 = DATA_UD[(i + 1) % CLEN]
        for j in range(BOARD):
            pos = j + BOARD * i
            curve[pos] = lr1 * (BOARD - j) / BOARD + lr2 * j / BOARD
            updown[pos] = ud1 * (BOARD - j) / BOARD + ud2 * j / BOARD
            if j == 60:
                object_right[pos] = 1
            if i % 8 < 7:
                if j % 12 == 0:
                    object_left[pos] = 2
            else:
                if j % 20 == 0:
                    object_left[pos] = 3
            if j % 12 == 6:
                object_left[pos] = 9

def time_str(val):
    sec = int(val)
    ms = int((val - sec) * 100)
    mi = int(sec / 60)
    return "{}'{:02}.{:02}".format(mi, sec % 60, ms)

def draw_obj(bg, img, x, y, sc):
    img_rz = pygame.transform.rotozoom(img, 0, sc)
    w = img_rz.get_width()
    h = img_rz.get_height()
    bg.blit(img_rz, [x - w / 2, y - h])

def draw_shadow(bg, x, y, siz):
    shadow = pygame.Surface([siz, siz / 4])
    shadow.fill(RED)
    shadow.set_colorkey(RED)
    shadow.set_alpha(128)
    pygame.draw.ellipse(shadow, BLACK, [0, 0, siz, siz / 4])
    bg.blit(shadow, [x - siz / 2, y - siz / 4])

def init_car():
    for i in range(1, CAR):
        car_x[i] = random.randint(50, 750)
        car_y[i] = random.randint(200, CMAX - 200)
        car_lr[i] = 0
        car_spd[i] = random.randint(100, 200)
    car_x[0] = 400
    car_y[i] = 0
    car_lr[0] = 0
    car_spd[0] = 0

def drive_car(key):
    global idx, tmr, laps, recbk
    if key[K_LEFT] == 1:
        if car_lr[0] > -3:
            car_lr[0] -= 1
        car_x[0] = car_x[0] + (car_lr[0] - 3) * car_spd[0] / 100 - 5
    elif key[K_RIGHT] == 1:
        if car_lr[0] < 3:
            car_lr[0] += 1
        car_x[0] = car_x[0] + (car_lr[0] + 3) * car_spd[0] / 100 + 5
    else:
        car_lr[0] = int(car_lr[0] * 0.9)

    if key[K_a] == 1:
        car_spd[0] += 3
    elif key[K_z] == 1:
        car_spd[0] -= 10
    else:
        car_spd[0] -= 0.25

    if car_spd[0] < 0:
        car_spd[0] = 0
    if car_spd[0] > 320:
        car_spd[0] = 320

    car_x[0] -= car_spd[0] * curve[int(car_y[0] + PLCAR_Y) % CMAX] / 50
    if car_x[0] < 0:
        car_x[0] = 0
        car_spd[0] *= 0.9
    if car_x[0] > 800:
        car_x[0] = 800
        car_spd[0] *= 0.9

    car_y[0] = car_y[0] + car_spd[0] / 100
    if car_y[0] > CMAX - 1:
        car_y[0] -= CMAX
        laptime[laps] = time_str(rec - recbk)
        recbk = rec
        laps += 1
        if laps == LAPS:
            idx = 3
            tmr = 0

def move_car(cs):
    for i in range(cs, CAR):
        if car_spd[i] < 100:
            car_spd[i] += 3
        if i == tmr % 120:
            car_lr[i] += random.choice([-1, 0, 1])
            if car_lr[i] < -3: car_lr[i] = -3
            if car_lr[i] > 3: car_lr[i] = 3
        car_x[i] = car_x[i] + car_lr[i] * car_spd[i] / 100
        if car_x[i] < 50:
            car_x[i] = 50
            car_lr[i] = int(car_lr[i] * 0.9)
        if car_x[i] > 750:
            car_x[i] = 750
            car_lr[i] = int(car_lr[i] * 0.9)
        car_y[i] += car_spd[i] / 100
        if car_y[i] > CMAX - 1:
            car_y[i] -= CMAX
        if idx == 2:
            cx = car_x[i] - car_x[0]
            cy = car_y[i] - (car_y[0] + PLCAR_Y) % CMAX
            if -100 <= cx and cx <= 100 and -10 <= cy and cy <= 10:
                car_x[0] -= cx / 4
                car_x[i] += cx / 4
                car_spd[0], car_spd[i] = car_spd[i] * 0.3, car_spd[0] * 0.3
                se_crash.play()

def draw_text(scrn, txt, x, y, col, fnt):
    sur = fnt.render(txt, True, BLACK)
    x -= sur.get_width() / 2
    y -= sur.get_height() / 2
    scrn.blit(sur, [x + 2, y + 2])
    sur = fnt.render(txt, True, col)
    scrn.blit(sur, [x, y])

def main():
    global idx, tmr, laps, rec, recbk, se_crash, mycar # 전역 변수 선언
    pygame.init()
    pygame.display.set_caption("Python Racer")
    screen = pygame.display.set_mode((800, 600))
    clock = pygame.time.Clock()
    fnt_s = pygame.font.Font(None, 40)
    fnt_m = pygame.font.Font(None, 50)
    fnt_l = pygame.font.Font(None, 120)

    img_title = pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/title.png').convert_alpha()
    img_bg = pygame.image.load("Python_workspace/python_game/Chapter11/image_pr/bg.png").convert()
    img_sea = pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/sea.png').convert_alpha()
    img_obj = [
        None,
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/board.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/yashi.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/yacht.png').convert_alpha()
    ]
    img_car = [
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car00.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car01.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car02.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car03.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car04.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car05.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car06.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car10.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car11.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car12.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car13.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car14.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car15.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car16.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car20.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car21.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car22.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car23.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car24.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car25.png').convert_alpha(),
        pygame.image.load('Python_workspace/python_game/Chapter11/image_pr/car26.png').convert_alpha()
    ]

    se_crash = pygame.mixer.Sound('Python_workspace/python_game/Chapter11/sound_pr/crash.ogg')

    # 도로 판의 기본 형태 계산
    BOARD_W = [0] * BOARD
    BOARD_H = [0] * BOARD
    BOARD_UD = [0] * BOARD
    for i in range(BOARD):
        BOARD_W[i] = 10 + (BOARD - i) * (BOARD - i) / 12
        BOARD_H[i] = 3.4 * (BOARD - i) / BOARD
        BOARD_UD[i] = 2 * math.sin(math.radians(i * 1.5))

    make_course()
    init_car()

    vertical = 0

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

        # 화면에 그릴 도로 X 좌표와 높낮이 계산
        di = 0
        ud = 0
        board_x = [0] * BOARD
        board_ud = [0] * BOARD
        for i in range(BOARD):
            di += curve[int(car_y[0] + i) % CMAX]
            ud += updown[int(car_y[0] + i) % CMAX]
            board_x[i] = 400 - BOARD_W[i] * car_x[0] / 800 + di / 2
            board_ud[i] = ud / 30

        horizon = 400 + int(ud / 3)
        sy = horizon


        vertical = vertical - int(car_spd[0] * di / 8000)
        if vertical < 0:
            vertical += 800
        if vertical >= 800:
            vertical -= 800

        # 필드 그리기
        screen.fill((0, 56, 255))
        screen.blit(img_bg, [vertical - 800, horizon - 400])
        screen.blit(img_bg, [vertical, horizon - 400])
        screen.blit(img_sea, [board_x[BOARD - 1] - 780, sy])

        # 그리기 데이터를 기초로 도로 그리기
        for i in range(BOARD - 1, 0, -1):
            ux = board_x[i]
            uy = sy - BOARD_UD[i] * board_ud[i]
            uw = BOARD_W[i]
            sy = sy + BOARD_H[i] * (600 - horizon) / 200
            bx = board_x[i - 1]
            by = sy - BOARD_UD[i - 1] * board_ud[i - 1]
            bw = BOARD_W[i - 1]
            col = (160, 160, 160)
            if int(car_y[0] + i) % CMAX == PLCAR_Y + 10:
                col = (192, 0, 0)
            pygame.draw.polygon(screen, col, [[ux, uy], [ux + uw, uy], [bx + bw, by], [bx, by]])

            if int(car_y[0] + i) % 10 <= 4:
                pygame.draw.polygon(screen, YELLOW, [[ux, uy], [ux + uw * 0.02, uy], [bx + bw * 0.02, by], [bx, by]])
                pygame.draw.polygon(screen, YELLOW, [[ux + uw * 0.98, uy], [ux + uw, uy], [bx + bw, by], [bx + bw * 0.98, by]])
            
            if int(car_y[0] + i) % 20 <= 10:
                pygame.draw.polygon(screen, WHITE, [[ux + uw * 0.24, uy], [ux + uw * 0.26, uy], [bx + bw * 0.26, by], [bx + bw * 0.24, by]])
                pygame.draw.polygon(screen, WHITE, [[ux + uw * 0.49, uy], [ux + uw * 0.51, uy], [bx + bw * 0.51, by], [bx + bw * 0.49, by]])
                pygame.draw.polygon(screen, WHITE, [[ux + uw * 0.74, uy], [ux + uw * 0.76, uy], [bx + bw * 0.76, by], [bx + bw * 0.74, by]])

            scale  = 1.5 * BOARD_W[i] / BOARD_W[0]
            obj_l = object_left[int(car_y[0] + i) % CMAX]
            if obj_l == 2:
                draw_obj(screen, img_obj[obj_l], ux - uw * 0.05, uy, scale)
            if obj_l == 3:
                draw_obj(screen, img_obj[obj_l], ux - uw * 0.5, uy, scale)
            if obj_l == 9:
                screen.blit(img_sea, [ux - uw * 0.5 - 780, uy])

            obj_r = object_right[int(car_y[0] + i) % CMAX]
            if obj_r == 1:
                draw_obj(screen, img_obj[obj_r], ux + uw * 1.3, uy, scale)

            for c in range(1, CAR):
                if int(car_y[c]) % CMAX == int(car_y[0] + i) % CMAX:
                    lr = int(4 * (car_x[0] - car_x[c]) / 800)
                    if lr < -3: lr = -3
                    if lr > 3: lr = 3
                    draw_obj(screen, img_car[(c % 3) * 7 + 3 + lr], ux + car_x[c] * BOARD_W[i] / 800, uy, 0.05 + BOARD_W[i] / BOARD_W[0])

            if i == PLCAR_Y:
                draw_shadow(screen, ux + car_x[0] * BOARD_W[i] / 800, uy, 200 * BOARD_W[i] / BOARD_W[0])
                draw_obj(screen, img_car[3 + car_lr[0] + mycar * 7], ux + car_x[0] * BOARD_W[i] / 800, uy, 0.05 + BOARD_W[i] / BOARD_W[0])  # 플레이어 차량 그리기

        draw_text(screen, str(int(car_spd[0])) + 'km/h', 680, 30, RED, fnt_m)
        draw_text(screen, 'lap {}/{}'.format(laps, LAPS), 100, 30, WHITE, fnt_m)
        draw_text(screen, 'time ' + time_str(rec), 100, 80, GREEN, fnt_s)
        for i in range(LAPS):
            draw_text(screen, laptime[i], 80, 130 + 40 * i, YELLOW, fnt_s)
        key = pygame.key.get_pressed()

        if idx == 0:        # 타이틀 화면
            screen.blit(img_title, [120, 120])
            draw_text(screen, '[A] Start game', 400, 320, WHITE, fnt_m)
            draw_text(screen, '[S] Select your car', 400, 400, WHITE, fnt_m)    # [S] Select your car 표시
            move_car(0)
            if key[K_a] != 0:
                init_car()
                idx = 1
                tmr = 0
                laps = 0
                rec = 0
                recbk = 0
                for i in range(LAPS):
                    laptime[i] = "0'00.00"
            if key[K_s] != 0:   # [S] 키를 눌렀다면
                idx = 4         # idx에 4 대입, 차종 선택
            
        if idx == 1:        # 카운트 다운
            n = 3 - int(tmr / 60)
            draw_text(screen, str(n), 400, 240, YELLOW, fnt_l)
            if tmr == 179:
                pygame.mixer.music.load('Python_workspace/python_game/Chapter11/sound_pr/bgm.ogg')
                pygame.mixer.music.play(-1)
                idx = 2
                tmr = 0
        
        if idx == 2:        # 레이싱 중
            if tmr < 60:
                draw_text(screen, 'Go!', 400, 240, RED, fnt_l)
            rec = rec + 1 / 60
            drive_car(key)
            move_car(1)

        if idx == 3:        # 골
            if tmr == 1:
                pygame.mixer.music.stop()
            if tmr == 30:
                pygame.mixer.music.load('Python_workspace/python_game/Chapter11/sound_pr/goal.ogg')
                pygame.mixer.music.play(0)
            draw_text(screen, 'GOAL!', 400, 240, GREEN, fnt_l)
            car_spd[0] = car_spd[0] * 0.96
            car_y[0] = car_y[0] + car_spd[0] / 100
            move_car(1)
            if tmr > 60 * 8:
                idx = 0

        if idx == 4:        # 차종 선택
            move_car(0)     # 모든 차량 이동
            draw_text(screen, 'Select your car', 400, 160, WHITE, fnt_m)    # 'Select your car' 표시
            for i in range(3):
                x = 160 + 240 * i       # 선택용 사각형 X 좌표 대입
                y = 300                 # 선택용 사각형 Y 좌표 대입
                col = BLACK             # col에 검은색 대입
                if i == mycar:          # 선택한 차량이라면
                    col = (0, 128, 255) # col에 밝은 파란색 대입
                pygame.draw.rect(screen, col, [x - 100, y - 80, 200, 160])          # 사각형 테두리 색상 그림
                draw_text(screen, '[' + str(i + 1) + ']', x, y - 50, WHITE, fnt_m)  # [n]의 문자 표시
                screen.blit(img_car[3 + i * 7], [x - 100, y - 20])      # 차량 표시
            draw_text(screen, '[Enter] OK!', 400, 440, GREEN, fnt_m)    # [Enter] OK! 출력
            if key[K_1] == 1:       # [1] 키를 눌렀다면
                mycar = 0           # mycar에 0 대입(빨간색 차)
            if key[K_2] == 1:       # [2] 키를 눌렀다면
                mycar = 1           # mycar에 1 대입(파란색 차)
            if key[K_3] == 1:       # [3] 키를 눌렀다면
                mycar = 2           # mycar에 2 대입(노란색 차)
            if key[K_RETURN] == 1:  # [Enter] 키를 눌렀다면
                idx = 0             # idx에 0 대입, 타이틀 화면 복귀

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

if __name__ == '__main__':
    main()

차량 선택은 main() 함수내에서 처리한다.

if idx == 4:        # 차종 선택
    move_car(0)     # 모든 차량 이동
    draw_text(screen, 'Select your car', 400, 160, WHITE, fnt_m)    # 'Select your car' 표시
    for i in range(3):
        x = 160 + 240 * i       # 선택용 사각형 X 좌표 대입
        y = 300                 # 선택용 사각형 Y 좌표 대입
        col = BLACK             # col에 검은색 대입
        if i == mycar:          # 선택한 차량이라면
            col = (0, 128, 255) # col에 밝은 파란색 대입
        pygame.draw.rect(screen, col, [x - 100, y - 80, 200, 160])          # 사각형 테두리 색상 그림
        draw_text(screen, '[' + str(i + 1) + ']', x, y - 50, WHITE, fnt_m)  # [n]의 문자 표시
        screen.blit(img_car[3 + i * 7], [x - 100, y - 20])      # 차량 표시
    draw_text(screen, '[Enter] OK!', 400, 440, GREEN, fnt_m)    # [Enter] OK! 출력
    if key[K_1] == 1:       # [1] 키를 눌렀다면
        mycar = 0           # mycar에 0 대입(빨간색 차)
    if key[K_2] == 1:       # [2] 키를 눌렀다면
        mycar = 1           # mycar에 1 대입(파란색 차)
    if key[K_3] == 1:       # [3] 키를 눌렀다면
        mycar = 2           # mycar에 2 대입(노란색 차)
    if key[K_RETURN] == 1:  # [Enter] 키를 눌렀다면
        idx = 0             # idx에 0 대입, 타이틀 화면 복귀

[1], [2], [3]키를 누르면 mycar 값이 변경된다.

draw_obj(screen, img_car[3 + car_lr[0] + mycar * 7], ux + car_x[0] * BOARD_W[i] / 800, uy, 0.05 + BOARD_W[i] / BOARD_W[0])  # 플레이어 차량 그리기

플레이어 차량은 이와 같이 mycar 값을 사용해 차 이미지 번호를 바꾸어서 차량 종류를 변경한다.