You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

291 lines
8.1 KiB

import pygame
import time
import math
from utils import scale_image, blit_rotate_center, blit_text_center
pygame.font.init()
GRASS = scale_image(pygame.image.load("imgs/grass.jpg"), 2.5)
TRACK = scale_image(pygame.image.load("imgs/track.png"), 0.9)
TRACK_BORDER = scale_image(pygame.image.load("imgs/track-border.png"), 0.9)
TRACK_BORDER_MASK = pygame.mask.from_surface(TRACK_BORDER)
FINISH = pygame.image.load("imgs/finish.png")
FINISH_MASK = pygame.mask.from_surface(FINISH)
FINISH_POSITION = (130, 250)
RED_CAR = scale_image(pygame.image.load("imgs/red-car.png"), 0.55)
GREEN_CAR = scale_image(pygame.image.load("imgs/green-car.png"), 0.55)
WIDTH, HEIGHT = TRACK.get_width(), TRACK.get_height()
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Racing Game!")
MAIN_FONT = pygame.font.SysFont("comicsans", 44)
FPS = 60
PATH = [(175, 119), (110, 70), (56, 133), (70, 481), (318, 731), (404, 680), (418, 521), (507, 475), (600, 551), (613, 715), (736, 713),
(734, 399), (611, 357), (409, 343), (433, 257), (697, 258), (738, 123), (581, 71), (303, 78), (275, 377), (176, 388), (178, 260)]
class GameInfo:
LEVELS = 10
def __init__(self, level=1):
self.level = level
self.started = False
self.level_start_time = 0
def next_level(self):
self.level += 1
self.started = False
def reset(self):
self.level = 1
self.started = False
self.level_start_time = 0
def game_finished(self):
return self.level > self.LEVELS
def start_level(self):
self.started = True
self.level_start_time = time.time()
def get_level_time(self):
if not self.started:
return 0
return round(time.time() - self.level_start_time)
class AbstractCar:
def __init__(self, max_vel, rotation_vel):
self.img = self.IMG
self.max_vel = max_vel
self.vel = 0
self.rotation_vel = rotation_vel
self.angle = 0
self.x, self.y = self.START_POS
self.acceleration = 0.1
def rotate(self, left=False, right=False):
if left:
self.angle += self.rotation_vel
elif right:
self.angle -= self.rotation_vel
def draw(self, win):
blit_rotate_center(win, self.img, (self.x, self.y), self.angle)
def move_forward(self):
self.vel = min(self.vel + self.acceleration, self.max_vel)
self.move()
def move_backward(self):
self.vel = max(self.vel - self.acceleration, -self.max_vel/2)
self.move()
def move(self):
radians = math.radians(self.angle)
vertical = math.cos(radians) * self.vel
horizontal = math.sin(radians) * self.vel
self.y -= vertical
self.x -= horizontal
def collide(self, mask, x=0, y=0):
car_mask = pygame.mask.from_surface(self.img)
offset = (int(self.x - x), int(self.y - y))
poi = mask.overlap(car_mask, offset)
return poi
def reset(self):
self.x, self.y = self.START_POS
self.angle = 0
self.vel = 0
class PlayerCar(AbstractCar):
IMG = RED_CAR
START_POS = (180, 200)
def reduce_speed(self):
self.vel = max(self.vel - self.acceleration / 2, 0)
self.move()
def bounce(self):
self.vel = -self.vel
self.move()
class ComputerCar(AbstractCar):
IMG = GREEN_CAR
START_POS = (150, 200)
def __init__(self, max_vel, rotation_vel, path=[]):
super().__init__(max_vel, rotation_vel)
self.path = path
self.current_point = 0
self.vel = max_vel
def draw_points(self, win):
for point in self.path:
pygame.draw.circle(win, (255, 0, 0), point, 5)
def draw(self, win):
super().draw(win)
# self.draw_points(win)
def calculate_angle(self):
target_x, target_y = self.path[self.current_point]
x_diff = target_x - self.x
y_diff = target_y - self.y
if y_diff == 0:
desired_radian_angle = math.pi / 2
else:
desired_radian_angle = math.atan(x_diff / y_diff)
if target_y > self.y:
desired_radian_angle += math.pi
difference_in_angle = self.angle - math.degrees(desired_radian_angle)
if difference_in_angle >= 180:
difference_in_angle -= 360
if difference_in_angle > 0:
self.angle -= min(self.rotation_vel, abs(difference_in_angle))
else:
self.angle += min(self.rotation_vel, abs(difference_in_angle))
def update_path_point(self):
target = self.path[self.current_point]
rect = pygame.Rect(
self.x, self.y, self.img.get_width(), self.img.get_height())
if rect.collidepoint(*target):
self.current_point += 1
def move(self):
if self.current_point >= len(self.path):
return
self.calculate_angle()
self.update_path_point()
super().move()
def next_level(self, level):
self.reset()
self.vel = self.max_vel + (level - 1) * 0.2
self.current_point = 0
def draw(win, images, player_car, computer_car, game_info):
for img, pos in images:
win.blit(img, pos)
level_text = MAIN_FONT.render(
f"Level {game_info.level}", 1, (255, 255, 255))
win.blit(level_text, (10, HEIGHT - level_text.get_height() - 70))
time_text = MAIN_FONT.render(
f"Time: {game_info.get_level_time()}s", 1, (255, 255, 255))
win.blit(time_text, (10, HEIGHT - time_text.get_height() - 40))
vel_text = MAIN_FONT.render(
f"Vel: {round(player_car.vel, 1)}px/s", 1, (255, 255, 255))
win.blit(vel_text, (10, HEIGHT - vel_text.get_height() - 10))
player_car.draw(win)
computer_car.draw(win)
pygame.display.update()
def move_player(player_car):
keys = pygame.key.get_pressed()
moved = False
if keys[pygame.K_a]:
player_car.rotate(left=True)
if keys[pygame.K_d]:
player_car.rotate(right=True)
if keys[pygame.K_w]:
moved = True
player_car.move_forward()
if keys[pygame.K_s]:
moved = True
player_car.move_backward()
if not moved:
player_car.reduce_speed()
def handle_collision(player_car, computer_car, game_info):
if player_car.collide(TRACK_BORDER_MASK) != None:
player_car.bounce()
computer_finish_poi_collide = computer_car.collide(
FINISH_MASK, *FINISH_POSITION)
if computer_finish_poi_collide != None:
blit_text_center(WIN, MAIN_FONT, "You lost!")
pygame.display.update()
pygame.time.wait(5000)
game_info.reset()
player_car.reset()
computer_car.reset()
player_finish_poi_collide = player_car.collide(
FINISH_MASK, *FINISH_POSITION)
if player_finish_poi_collide != None:
if player_finish_poi_collide[1] == 0:
player_car.bounce()
else:
game_info.next_level()
player_car.reset()
computer_car.next_level(game_info.level)
run = True
clock = pygame.time.Clock()
images = [(GRASS, (0, 0)), (TRACK, (0, 0)),
(FINISH, FINISH_POSITION), (TRACK_BORDER, (0, 0))]
player_car = PlayerCar(4, 4)
computer_car = ComputerCar(2, 4, PATH)
game_info = GameInfo()
while run:
clock.tick(FPS)
draw(WIN, images, player_car, computer_car, game_info)
while not game_info.started:
blit_text_center(
WIN, MAIN_FONT, f"Press any key to start level {game_info.level}!")
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
break
if event.type == pygame.KEYDOWN:
game_info.start_level()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
break
move_player(player_car)
computer_car.move()
handle_collision(player_car, computer_car, game_info)
if game_info.game_finished():
blit_text_center(WIN, MAIN_FONT, "You won the game!")
pygame.time.wait(5000)
game_info.reset()
player_car.reset()
computer_car.reset()
pygame.quit()