Python 갖고 놀아보기 #4 PyCar 프로젝트에 새로운 기능 추가하기

지금까지 로또 프로그램, 진법 계산기, 회원가입 프로그램 3가지 과제를 도와줬고 남은 과제
“PyCar 프로젝트에 새로운 기능 추가하기” 과제를 도와주었다.


PyCar 프로젝트

PyCar 프로젝트는 SuanLab 에서 토이 프로젝트로 만든 간단한 게임 프로젝트인데
여기에 독창적인 기능을 추가하는 것이 과제였다.

추가한 기능

  • 도로 양 옆에 충돌 감지가 가능한 가로수 추가
  • 닉네임을 입력하여 점수 랭킹 시스템

기존에 존재하던 문제점을 해결

  • 플레이어의 차량이 랜덤으로 배정되지 않고 고정되게끔 변경
  • 랜덤하게 생성되는 장애물 차량들이 겹치는 문제 해결

    기존 기능에 맵 어디서든 가로수가 생성되는 기능이 있었는데,
    해당 기능을 활용해서 도로 양 옆에 가로수를 1자로 나열하는 기능을 추가하였고
    기존에 게임 제목과 스코어 UI를 출력해주는 screen.blit을 활용해서 점수 랭킹 시스템도 구현 하였다.
    — 기존의 플레이 방식은 플레이어의 차량이 랜덤으로 배정되지 않고 정해진 차량 이미지로만 게임을 진행할 수 있었다.
    이 방식을 랜덤함수를 사용해서 랜덤함수로 뽑아낸 숫자를 차량 이미지 리스트 image_car 에서 인덱스 주소로
    갖고 있는 차량 이미지를 플레이어의 차량 이미지로 배정될 수 있도록 수정하였고,
    랜덤하게 생성되는 장애물 차량들이 겹치는 오류를 차량 이미지의 크기 값을 고정시키고 겹치지 않게 하여 해결하였다.


PyCar.py

import pygame
import random
from time import sleep

input_rect = pygame.Rect(200, 200, 140, 32)

user_text = ''
#####

WINDOW_WIDTH = 1300
WINDOW_HEIGHT = 800

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (150, 150, 150)
RED = (255, 0, 0)

rank_name=["HwangGiHyeon", "SongDoHun", "Oh Professor"]
rank_score=["1" , "1", "99999"]

class Tree:
    image_tree = 'Tree.png'
    
    def __init__(self, x=0, y=0, dx=0, dy=0):
        self.image = ""
        self.x = x
        self.y = y
        self.dx = dx
        self.dy = dy

    def load_image(self):
        self.image = pygame.image.load(self.image_tree)
        self.width = self.image.get_rect().size[0]
        self.height = self.image.get_rect().size[1]

    def draw_image(self):
        screen.blit(self.image, [self.x, self.y])
        
    def check_out_of_screen(self):
        if self.x+self.width > WINDOW_WIDTH or self.x < 0:
            self.x -= self.dx

    def check_crash(self,tree):
        if (self.x+self.width > tree.x) and (self.x < car.x+tree.width) and (self.y < tree.y+tree.height) and (self.y+self.height > tree.y):
            return True
        else:
            return False

class Car:
    image_car = ['RacingCar01.png', 'RacingCar02.png', 'RacingCar03.png', 'RacingCar04.png', 'RacingCar05.png', \
                 'RacingCar06.png', 'RacingCar07.png', 'RacingCar08.png', 'RacingCar09.png', 'RacingCar10.png', \
                 'RacingCar11.png', 'RacingCar12.png', 'RacingCar13.png', 'RacingCar14.png', 'RacingCar15.png', \
                 'RacingCar16.png', 'RacingCar17.png', 'RacingCar18.png', 'RacingCar19.png', 'RacingCar20.png', ]

    def __init__(self, x=0, y=0, dx=0, dy=0):
        self.image = ""
        self.x = x
        self.y = y
        self.dx = dx
        self.dy = dy
    

    def load_image(self):
        self.image = pygame.image.load(random.choice(self.image_car))
        self.width = self.image.get_rect().size[0]
        self.height = self.image.get_rect().size[1]

    def load_own_image(self):
        self.image = pygame.image.load(self.image_car[18])
        self.width = self.image.get_rect().size[0]
        self.height = self.image.get_rect().size[1]

    def draw_image(self):
        screen.blit(self.image, [self.x, self.y])

    def move_x(self):
        self.x += self.dx

    def move_y(self):
        self.y += self.dy

    def check_out_of_screen(self):
        if self.x+self.width > WINDOW_WIDTH or self.x < 0:
            self.x -= self.dx
        elif self.y+self.height > WINDOW_HEIGHT or self.y < 0:
            self.y -= self.dy

    def check_crash(self,car):
        if (self.x+self.width > car.x) and (self.x < car.x+car.width) and (self.y < car.y+car.height) and (self.y+self.height > car.y):
            return True
        else:
            return False

def draw_main_menu():
    draw_x = (WINDOW_WIDTH / 2) - 200
    draw_y = WINDOW_HEIGHT / 2
    image_intro = pygame.image.load('PyCar.Png')
    screen.blit(image_intro, [draw_x, draw_y - 280])
    font_40 = pygame.font.SysFont("FixedSys", 40, True, False)
    font_30 = pygame.font.SysFont("FixedSys", 30, True, False)
    text_title = font_40.render("Pycar: Racing Car Game", True, BLACK)
    screen.blit(text_title, [draw_x, draw_y])
    score_text = font_40.render("Score: " + str(score), True, WHITE)
    screen.blit(score_text, [draw_x, draw_y + 70])
    text_surface = font_30.render("UserName: " + user_text, True, WHITE)
    screen.blit(text_surface, [draw_x, draw_y + 130])
    for i in range(len(rank_score)):        
        text_rank = font_30.render(rank_name[i] + ": " + rank_score[i], True, BLACK)
        screen.blit(text_rank, [draw_x, draw_y + (150 + (i * 20))])
    text_start = font_30.render("Press Space Key to Start!", True, RED)
    screen.blit(text_start, [draw_x, draw_y + 350])
    pygame.display.flip()

def draw_score():
    font_30 = pygame.font.SysFont("FixedSys", 30, True, False)
    txt_score = font_30.render("Score: " +str(score), True, BLACK)
    screen.blit(txt_score, [15,15])


if __name__=='__main__':

    pygame.init()

    screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
    pygame.display.set_caption("PyCar: Racing Car Game")
    clock = pygame.time.Clock()

    ## 게임 사운드
    pygame.mixer.music.load('race.wav')
    sound_crash = pygame.mixer.Sound('crash.wav')
    sound_engine = pygame.mixer.Sound('engine.wav')

    ##사용자 레이싱 카 생성
    player = Car(WINDOW_WIDTH / 2, (WINDOW_HEIGHT - 150), 0, 0)
    player.load_own_image()

    ##컴퓨터 레이싱 카 생성
    cars = []
    car_count = 6
    for i in range(car_count):
        x = random.randrange(0, WINDOW_WIDTH-55)
        y = random.randrange(-150, -50)
        car = Car(x, random.randrange(-150, -50), 0, random.randint(5, 10))
        car.load_image()
        cars.append(car)

    ##### 왼쪽 나무
    left_trees = []
    left_trees_count = 9
    for i in range(left_trees_count):
        x = 0
        y = 0
        left_tree = Tree(x, y, 0, 10)
        left_tree.load_image()
        left_trees.append(left_tree)
    #####
    
    ##### 오른쪽 나무
    right_trees = []
    right_trees_count = 9
    for i in range(right_trees_count):
        x = 0
        y = 0
        right_tree = Tree(x, y, 0, 10)
        right_tree.load_image()
        right_trees.append(right_tree)
    #####
    
    ##도로 차선 생성
    lanes = []
    lane_width = 10
    lane_height = 80
    lane_margin = 20
    lane_count = 30
    lane_x = (WINDOW_WIDTH - lane_width) / 2
    lane_y = -10
    lane_left_x = (WINDOW_WIDTH - lane_width) / 6
    lane_left_y = -10
    lane_right_x = 995
    lane_right_y = -10
    for i in range(lane_count):
        lanes.append([lane_x, lane_y])
        lane_y += lane_height + lane_margin
        lanes.append([lane_left_x, lane_left_y])
        lane_left_y += lane_height + lane_margin
        lanes.append([lane_right_x, lane_right_y])
        lane_right_y += lane_height + lane_margin
    score = 0
    crash = True
    game_on = True
    while game_on:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_on = False

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_BACKSPACE:
                    user_text = user_text[:-1]
                else:
                    user_text += event.unicode
            
            #게임 다시 시작
            if crash:
                if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
                    if user_text == ' ':
                        user_text = 'Guest'
                    rank_name.append(user_text)
                    crash = False
                    for i in range(car_count):
                        cars[i].x = random.randrange(left_trees[i].width-right_trees[i].width, WINDOW_WIDTH-cars[i].width-left_trees[i].width-right_trees[i].width)
                        cars[i].y = random.randrange(-150, -50)
                        cars[i].load_image()

                    #####
                    for i in range(left_trees_count):
                        left_trees[i].x = 10
                        left_trees[i].y = -20 + (i*100)
                        left_trees[i].load_image()
                    #####

                    #####
                    for i in range(right_trees_count):
                        right_trees[i].x = 1210
                        right_trees[i].y = -20 + (i*100)
                        right_trees[i].load_image()
                    #####
                        
                    player.load_own_image()
                    player.x = WINDOW_WIDTH / 2
                    player.dx = 0
                    player.dy = 0
                    score = 0
                    pygame.mouse.set_visible(False)
                    sound_engine.play()
                    sleep(5)
                    pygame.mixer.music.play(-1)
                                                                                                                                

            if not crash:
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_RIGHT:
                        player.dx = 4
                    elif event.key == pygame.K_LEFT:
                        player.dx = -4
                    elif event.key == pygame.K_UP:
                        player.dy = -4
                    elif event.key == pygame.K_DOWN:
                        player.dy = 4

                if event.type == pygame.KEYUP:
                    if event.key == pygame.K_RIGHT:
                        player.dx = 0
                    elif event.key == pygame.K_LEFT:
                        player.dx = 0
                    elif event.key == pygame.K_UP:
                        player.dy = 0
                    elif event.key == pygame.K_DOWN:
                        player.dy = 0

        #GRAY로 화면 채우기
        screen.fill(GRAY)

        #게임 화면 출력
        if not crash:
            #도로 차선 이동
            for i in range(lane_count):
                pygame.draw.rect(screen, WHITE, [lanes[i][0], lanes[i][1], lane_width, lane_height])
                lanes[i][1] += 10 #도로 차선 속도
                if lanes[i][1] > WINDOW_HEIGHT:
                    lanes[i][1] = -40 - lane_height

            #사용자 레이싱 카
            player.draw_image()
            player.move_x()
            player.move_y()
            player.check_out_of_screen()

            #컴퓨터 레이싱 카
            for i in range(car_count):
                cars[i].draw_image()
                cars[i].y += cars[i].dy
                if cars[i].y > WINDOW_HEIGHT:
                    score +=10
                    cars[i].y = random.randrange(-150, -50)
                    cars[i].x = random.randrange(left_trees[i].width-right_trees[i].width, WINDOW_WIDTH-cars[i].width-left_trees[i].width-right_trees[i].width)
                    cars[i].dy = random.randint(4,9)
                    cars[i].load_image()
            #####
            for i in range(left_trees_count):
                left_trees[i].draw_image()
                left_trees[i].y += left_trees[i].dy
                if left_trees[i].y > WINDOW_HEIGHT:
                    left_trees[i].y = 0
                    left_trees[i].x = 0
                    left_trees[i].dy = 10
                    left_trees[i].load_image()     
            ##### 

            #####
            for i in range(right_trees_count):
                right_trees[i].draw_image()
                right_trees[i].y += right_trees[i].dy
                if right_trees[i].y > WINDOW_HEIGHT:
                    right_trees[i].y = 0
                    right_trees[i].x = 1210
                    right_trees[i].dy = 10
                    right_trees[i].load_image()     
            ##### 

            #레이싱 카 충돌 사고 체크
            for i in range(car_count):
                if player.check_crash(cars[i]):
                    crash = True
                    pygame.mixer.music.stop()
                    sound_crash.play()
                    sleep(2)
                    pygame.mouse.set_visible(True)
                    rank_score.append(str(score))
                    user_text = ''
                    print(rank_name)
                    print(rank_score)
                    break
            #draw_score()
            #pygame.display.flip()

            for i in range(left_trees_count):
                if player.check_crash(left_trees[i]):
                    crash = True
                    pygame.mixer.music.stop()
                    sound_crash.play()
                    sleep(2)
                    pygame.mouse.set_visible(True)
                    rank_score.append(str(score))
                    user_text = ''
                    print(rank_name)
                    print(rank_score)
                    break
                
            for i in range(right_trees_count):
                if player.check_crash(right_trees[i]):
                    crash = True
                    pygame.mixer.music.stop()
                    sound_crash.play()
                    sleep(2)
                    pygame.mouse.set_visible(True)
                    rank_score.append(str(score))
                    user_text = ''
                    print(rank_name)
                    print(rank_score)
                    break
                
            draw_score()
            pygame.display.flip()
        else:
            draw_main_menu()

        clock.tick(60)

    pygame.quit()