Skip to content

Latest commit

 

History

History
594 lines (522 loc) · 30.1 KB

220114.md

File metadata and controls

594 lines (522 loc) · 30.1 KB

Day 95

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

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)

tmr = 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

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):                         # 반복, COM 차량의
        car_x[i] = random.randint(50, 750)              # 가로방향 좌표 무작위 결정
        car_y[i] = random.randint(200, CMAX - 200)      # 코스 상 위치 무작위 결정
        car_lr[i] = 0                                   # 좌우 움직임 0 대입(정면 향함)
        car_spd[i] = random.randint(100, 200)           # 속도 무작위 결정
    car_x[0] = 400  # 플레이어 차량 가로 방향 화면 중앙
    car_y[i] = 0    # 플레이어 차량 코스 상 위치 초기화
    car_lr[0] = 0   # 플레이어 차량 방향 0 대입
    car_spd[0] = 0  # 플레이어 차량 속도 0 대입

def drive_car(key):
    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

def move_car(cs):   # COM 차량 제어 함수
    for i in range(cs, CAR):                                # 반복해서 모든 차량 처리
        if car_spd[i] < 100:                                    # 속도가 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                       # 방향이 -3 미만이면 -3 대입
            if car_lr[i] > 3: car_lr[i] = 3                         # 방향이 3 초과면 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                                        # 코스 시작으로 되돌림

def draw_text(scrn, txt, x, y, col, fnt):   # 그림자 포함 문자열 표시 함수
    sur = fnt.render(txt, True, BLACK)          # 검은색 문자열을 그릴 Surface 생성
    x -= sur.get_width() / 2                    # 센터링 X 좌표 계산
    y -= sur.get_height() / 2                   # 센터링 Y 좌표 계산
    scrn.blit(sur, [x + 2, y + 2])              # Surface를 화면으로 전송
    sur = fnt.render(txt, True, col)            # 지정색으로 문자열을 그릴 Surface 생성
    scrn.blit(sur, [x, y])                      # Surface를 화면으로 전송

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

    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(), # COM 차량 1 이미지
        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(), # COM 차량 2 이미지
        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()
    ]

    # 도로 판의 기본 형태 계산
    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:                                           # F1 키라면
                    screen = pygame.display.set_mode((800, 600), FULLSCREEN)        # 전체화면 모드
                if event.key == K_F2 or event.key == K_ESCAPE:                  # F2키 혹은 Esc 키라면
                    screen = pygame.display.set_mode((800, 600))                    # 일반 화면 모드
                    tmr += 1                                                        # 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)
            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):     # COM 차량
                if int(car_y[c]) % CMAX == int(car_y[0] + i) % CMAX:    # 해당 판 COM 차량 유무 확인
                    lr = int(4 * (car_x[0] - car_x[c]) / 800)   # 플레이어가 보는 COM 차량 방향 계산
                    if lr < -3: lr = -3     # -3 보다 작으면 -3 대입
                    if lr > 3: lr = 3       # 3 보다 크면 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])   # COM 차량 그리기

            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()
        drive_car(key)
        move_car(1)

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

if __name__ == '__main__':
    main()

실행하면 컴퓨터가 움직이는 여러 차량이 도로 위를 달린다.

def init_car():     # 차량 관리 리스트 초기값 대입 함수
    for i in range(1, CAR):                         # 반복, COM 차량의
        car_x[i] = random.randint(50, 750)              # 가로방향 좌표 무작위 결정
        car_y[i] = random.randint(200, CMAX - 200)      # 코스 상 위치 무작위 결정
        car_lr[i] = 0                                   # 좌우 움직임 0 대입(정면 향함)
        car_spd[i] = random.randint(100, 200)           # 속도 무작위 결정
    car_x[0] = 400  # 플레이어 차량 가로 방향 화면 중앙
    car_y[i] = 0    # 플레이어 차량 코스 상 위치 초기화
    car_lr[0] = 0   # 플레이어 차량 방향 0 대입
    car_spd[0] = 0  # 플레이어 차량 속도 0 대입

init_car() 함수에서 플레이어 차량을 포함한 모든 차량을 관리하는 리스트에 초기값을 대입한다. COM 차량의 가로 방향 좌표, 코스 상 위치, 속도는 무작위로 결정한다.

def move_car(cs):   # COM 차량 제어 함수
    for i in range(cs, CAR):                                # 반복해서 모든 차량 처리
        if car_spd[i] < 100:                                    # 속도가 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                       # 방향이 -3 미만이면 -3 대입
            if car_lr[i] > 3: car_lr[i] = 3                         # 방향이 3 초과면 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                                        # 코스 시작으로 되돌림

move_car() 함수는 COM 차량을 제어한다.

이 함수가 수행하는 작업은 다음과 같다.

  • 속도가 100 미만이면 3씩 증가한다.
  • 일정 시간별로 방향을 무작위로 변경한다.
  • 도로 왼쪽, 혹은 도로 오른쪽으로 너무 많이 가지 않게 한다.
  • 속도에 맞춰 코스 상 위치를 변화시켜 코스 종점을 지나면 코스 시작 위치로 되돌린다.

move_car 함수는 0을 인수로 받으면 0번째 차량도 자동으로 움직이게할 수 있다.
게임 중에는 move_car(1)으로 실행해서 COM 차량을 움직이고, 타이틀 화면에서는 move_car(0)로 실행해 플레이어 차량을 포함한 모든 차량을 자동으로 움직이게 할 수 있다.

for c in range(1, CAR):     # COM 차량
    if int(car_y[c]) % CMAX == int(car_y[0] + i) % CMAX:    # 해당 판 COM 차량 유무 확인
        lr = int(4 * (car_x[0] - car_x[c]) / 800)   # 플레이어가 보는 COM 차량 방향 계산
        if lr < -3: lr = -3     # -3 보다 작으면 -3 대입
        if lr > 3: lr = 3       # 3 보다 크면 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])   # COM 차량 그리기

main() 함수 안의 도로를 그리는 처리를 하는 부분에서 COM 차량을 표시한다.

for c in range(1, CAR)라는 반복과 if int(car_y[c]) % CMAX == int(car_y[0] + i) % CMAX라는 조건 분기로 COM 차량이 도로를 구성하는 판 위에 있는지 확인하고 있다면 플레이어 차량에서 본 COM 차량의 방향을 계산해 이미지를 확대 축소해서 표시하는 draw_obj 함수로 COM 차량을 그린다.
COM 차량의 스케일은 0.05 + BOARD_W[i] / BOARD_W[0]으로 계산한다.
COM 차량의 이미지 번호 지정을 img_car[(c % 3) * 7 + 3 + lr]로 하여 1대씩 색상이 달라지도록 한다.

int(car_y[0] + i) % CMAX는 플레이어가 본 도로 위의 판의 번호이다. 이는 '화면에 판을 표시할 때 해당 판의 위치에 COM 차량이 있는지를 확인하는 식'으로 생각하면 된다.

차량 충돌 판정 추가하기

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)

tmr = 0
se_crash = None # 충돌 시 사용할 효과음 로딩 변수
'''
변경 사항 없음, 생략
'''
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
        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                                              # COM 차량 가로로 이동
            car_spd[0], car_spd[i] = car_spd[i] * 0.3, car_spd[0] * 0.3     # 2개 차량 속도를 서로 바꿔 감속
            se_crash.play()                                                 # 충돌음 출력
'''
변경 사항 없음, 생략
'''
def main():
    global tmr, se_crash  # 전역 변수 선언
    pygame.init()
    pygame.display.set_caption("Python Racer")
    screen = pygame.display.set_mode((800, 600))
    clock = pygame.time.Clock()
    fnt_m = pygame.font.Font(None, 50)

    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)
            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()
        drive_car(key)
        move_car(1)

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

if __name__ == '__main__':
    main()

프로그램을 실행하면 플레이어 차량과 COM 차량이 접촉하면 충돌음이 재생되고 서로 조금씩 튀면서 속도가 줄어든다.

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
        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                                              # COM 차량 가로로 이동
            car_spd[0], car_spd[i] = car_spd[i] * 0.3, car_spd[0] * 0.3     # 2개 차량 속도를 서로 바꿔 감속
            se_crash.play()                                                 # 충돌음 출력

차량 충돌 판정과 충돌했을 때의 처리는 move_car() 함수가 실행한다.

플레이어 차량과 COm 차량의 가로 방향 좌표 차이를 cx에 대입하고 코스 상 위치의 차이를 cy에 대입한다. 이들 값이 -100 <= cx and cx <= 100 and -10 <= cy and cy <= 10 범위에 있다면 충돌한 것으로 판단한다. 이는 사각형 사이에서의 히트 체크이다.

충돌했다면 플레이어 차량의 가로 방향 좌표를 car_x[0] -= cx / 4, COM 차량의 좌표는 car_x[i] += cx / 4로 계산해서 서로 반대 방향으로 이동시킨다.

충돌 시 속도는 car_spd[0], car_spd[i] = car_spd[i] * 0.3, car_spd[0] * 0.3으로 계산한다. 이러면 플레이어 차량 속도는 COM 차량 속도의 30%, COM 차량 속도는 플레이어 차량 속도의 30%가 된다.