Загрузка данных
import math
import random
import sys
import pygame
# =============================================================================
# НАСТРОЙКИ И ЦВЕТА КИБЕРПАНКА
# =============================================================================
SCREEN_WIDTH = 1000
SCREEN_HEIGHT = 700
FPS = 60
COLOR_BG = (10, 10, 26)
COLOR_GRID = (0, 220, 255)
COLOR_NEON_PINK = (255, 20, 147)
COLOR_NEON_YELLOW = (255, 230, 0)
COLOR_WHITE = (245, 245, 255)
# =============================================================================
# ЧАСТИЦЫ И ЭФФЕКТЫ
# =============================================================================
class Particle:
def __init__(self, x, y, vx, vy, color, size, life):
self.x = float(x)
self.y = float(y)
self.vx = float(vx)
self.vy = float(vy)
self.color = color
self.size = float(size)
self.life = float(life)
self.max_life = float(life)
def update(self, dt):
self.x += self.vx * (dt * 60.0)
self.y += self.vy * (dt * 60.0)
self.life -= 1.0 * (dt * 60.0)
def draw(self, surface):
if self.life <= 0:
return
alpha_scale = self.life / self.max_life
cur_size = max(1, int(self.size * alpha_scale))
pygame.draw.circle(surface, self.color, (int(self.x), int(self.y)), cur_size)
# =============================================================================
# ТРАФИК И ПРЕПЯТСТВИЯ
# =============================================================================
class TrafficCar:
def __init__(self, lane, speed, color):
self.lane = lane # Индекс полосы: 0, 1, 2, 3
self.lane_x = 0.0
self.y = -120.0
self.speed = speed
self.width = 54
self.height = 96
self.color = color
self.passed = False
def update(self, dt, player_speed, lane_positions):
self.lane_x = lane_positions[self.lane]
# Движение относительно игрока
rel_speed = player_speed - self.speed
self.y += rel_speed * dt
def get_rect(self):
return pygame.Rect(int(self.lane_x - self.width // 2), int(self.y), self.width, self.height)
def draw(self, surface):
rx = int(self.lane_x - self.width // 2)
ry = int(self.y)
# Неоновое свечение (тень)
glow_rect = pygame.Rect(rx - 4, ry - 4, self.width + 8, self.height + 8)
pygame.draw.rect(surface, (self.color[0] // 3, self.color[1] // 3, self.color[2] // 3), glow_rect, border_radius=8)
# Корпус
pygame.draw.rect(surface, self.color, (rx, ry, self.width, self.height), border_radius=6)
# Стекла
pygame.draw.rect(surface, (20, 25, 40), (rx + 8, ry + 24, self.width - 16, 44), border_radius=4)
# Фары спереди (светят вперед) и габариты сзади
pygame.draw.rect(surface, (255, 255, 200), (rx + 6, ry + 2, 10, 6))
pygame.draw.rect(surface, (255, 255, 200), (rx + self.width - 16, ry + 2, 10, 6))
pygame.draw.rect(surface, (255, 0, 50), (rx + 6, ry + self.height - 8, 12, 6))
pygame.draw.rect(surface, (255, 0, 50), (rx + self.width - 18, ry + self.height - 8, 12, 6))
# =============================================================================
# ГОРОДСКИЕ НЕОНОВЫЕ БАШНИ ПО БОКАМ
# =============================================================================
class Building:
def __init__(self, x, y, w, h, is_left):
self.x = x
self.y = y
self.w = w
self.h = h
self.is_left = is_left
self.color = random.choice([(25, 20, 45), (15, 30, 50), (35, 15, 45)])
self.edge_color = random.choice([COLOR_GRID, COLOR_NEON_PINK])
def update(self, dt, speed):
self.y += speed * dt
if self.y > SCREEN_HEIGHT + 100:
self.y = -self.h - random.randint(20, 80)
def draw(self, surface):
pygame.draw.rect(surface, self.color, (self.x, int(self.y), self.w, self.h))
# Неоновые грани
line_x = self.x + self.w if self.is_left else self.x
pygame.draw.line(surface, self.edge_color, (line_x, int(self.y)), (line_x, int(self.y + self.h)), 2)
# =============================================================================
# ОСНОВНОЙ КЛАСС ИГРЫ
# =============================================================================
class CyberHighwayRacer:
def __init__(self):
pygame.init()
pygame.font.init()
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Cyber Highway: Neon Drift")
self.clock = pygame.time.Clock()
self.font_big = pygame.font.SysFont("impact", 54)
self.font_mid = pygame.font.SysFont("impact", 32)
self.font_ui = pygame.font.SysFont("monospace", 18, bold=True)
# Конфигурация шоссе
self.road_left = 260
self.road_right = 740
self.road_width = self.road_right - self.road_left
self.lanes_count = 4
self.lane_step = self.road_width // self.lanes_count
self.lane_x = [self.road_left + self.lane_step // 2 + i * self.lane_step for i in range(self.lanes_count)]
# Игрок
self.player_x = float(self.lane_x[1])
self.player_y = float(SCREEN_HEIGHT - 150)
self.player_w = 50
self.player_h = 92
self.player_vx = 0.0
self.speed = 400.0
self.base_speed = 550.0
self.max_speed = 1050.0
self.nitro = 100.0
self.nitro_active = False
# Игровые показатели
self.score = 0
self.near_miss_streak = 0
self.distance = 0.0
self.game_over = False
self.road_offset = 0.0
# Мир
self.traffic = []
self.particles = []
self.buildings = self._spawn_city()
self.spawn_timer = 0.0
def _spawn_city(self):
b_list = []
for i in range(7):
# Левая сторона
b_list.append(Building(random.randint(10, 40), i * 160, random.randint(140, 200), random.randint(100, 150), True))
# Правая сторона
b_list.append(Building(SCREEN_WIDTH - random.randint(180, 240), i * 160, random.randint(140, 200), random.randint(100, 150), False))
return b_list
def spawn_traffic_car(self):
occupied_lanes = [c.lane for c in self.traffic if c.y < 150]
free_lanes = [idx for idx in range(self.lanes_count) if idx not in occupied_lanes]
if not free_lanes:
return
lane = random.choice(free_lanes)
spd = random.uniform(180.0, 320.0)
cols = [
(255, 60, 0), (0, 255, 200), (255, 200, 0),
(180, 50, 255), (0, 150, 255)
]
self.traffic.append(TrafficCar(lane, spd, random.choice(cols)))
def handle_input(self, dt):
if self.game_over:
return
keys = pygame.key.get_pressed()
# Руление
target_vx = 0.0
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
target_vx -= 650.0
if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
target_vx += 650.0
# Плавная смена полосы (инерция)
self.player_vx += (target_vx - self.player_vx) * 12.0 * dt
self.player_x += self.player_vx * dt
# Ограничения дороги
self.player_x = max(self.road_left + 25, min(self.road_right - 25, self.player_x))
# Нитро и скорость
self.nitro_active = (keys[pygame.K_UP] or keys[pygame.K_w] or keys[pygame.K_SPACE]) and self.nitro > 0
if self.nitro_active:
self.speed += 600.0 * dt
self.nitro = max(0.0, self.nitro - 28.0 * dt)
# Огонь из выхлопа
for _ in range(2):
self.particles.append(Particle(
self.player_x + random.uniform(-14, 14),
self.player_y + self.player_h,
random.uniform(-15, 15), random.uniform(80, 160),
COLOR_GRID if random.random() < 0.5 else COLOR_NEON_PINK,
random.randint(4, 7), 18
))
elif keys[pygame.K_DOWN] or keys[pygame.K_s]:
self.speed -= 800.0 * dt
# Регенерация нитро при торможении
self.nitro = min(100.0, self.nitro + 15.0 * dt)
else:
self.speed += (self.base_speed - self.speed) * 3.0 * dt
self.nitro = min(100.0, self.nitro + 8.0 * dt)
self.speed = max(200.0, min(self.max_speed, self.speed))
def update(self, dt):
if self.game_over:
return
self.handle_input(dt)
self.distance += (self.speed * dt) * 0.05
self.score += int(self.speed * dt * 0.2)
self.road_offset = (self.road_offset + self.speed * dt) % 80
# Фон города
for b in self.buildings:
b.update(dt, self.speed * 0.6)
# Генерация соперников
self.spawn_timer += dt
spawn_rate = max(0.45, 1.2 - (self.speed / self.max_speed) * 0.5)
if self.spawn_timer >= spawn_rate:
self.spawn_traffic_car()
self.spawn_timer = 0.0
p_rect = pygame.Rect(int(self.player_x - self.player_w // 2), int(self.player_y), self.player_w, self.player_h)
# Обновление трафика
for car in self.traffic[:]:
car.update(dt, self.speed, self.lane_x)
c_rect = car.get_rect()
# Столкновение
if p_rect.colliderect(c_rect):
self.game_over = True
for _ in range(45):
self.particles.append(Particle(
self.player_x, self.player_y + 40,
random.uniform(-200, 200), random.uniform(-200, 200),
COLOR_NEON_YELLOW if random.random() < 0.5 else COLOR_NEON_PINK,
random.randint(5, 12), 40
))
# Опасный обгон (Near Miss Bonus)
if not car.passed and car.y > self.player_y:
dist = abs(car.lane_x - self.player_x)
if dist < 85:
self.near_miss_streak += 1
bonus = 150 * self.near_miss_streak
self.score += bonus
self.nitro = min(100.0, self.nitro + 12.0)
car.passed = True
# Удаление уехавших
if car.y > SCREEN_HEIGHT + 150:
self.traffic.remove(car)
# Частицы
for p in self.particles[:]:
p.update(dt)
if p.life <= 0:
self.particles.remove(p)
def draw_road(self):
# Асфальт
pygame.draw.rect(self.screen, (16, 16, 28), (self.road_left, 0, self.road_width, SCREEN_HEIGHT))
# Неоновые отбойники
glow_color = COLOR_NEON_PINK if self.nitro_active else COLOR_GRID
pygame.draw.line(self.screen, glow_color, (self.road_left, 0), (self.road_left, SCREEN_HEIGHT), 5)
pygame.draw.line(self.screen, glow_color, (self.road_right, 0), (self.road_right, SCREEN_HEIGHT), 5)
# Бегущие неоновые разделительные полосы
for lane_idx in range(1, self.lanes_count):
lx = self.road_left + lane_idx * self.lane_step
y = -80 + self.road_offset
while y < SCREEN_HEIGHT:
pygame.draw.line(self.screen, (0, 180, 220), (lx, int(y)), (lx, int(y + 40)), 3)
y += 80
def draw_player(self):
px = int(self.player_x - self.player_w // 2)
py = int(self.player_y)
# Наклон при маневре
tilt = int(self.player_vx * 0.015)
# Неоновое подсвечивание днища
underglow = COLOR_GRID if not self.nitro_active else (255, 0, 120)
pygame.draw.ellipse(self.screen, underglow, (px - 10, py + 10, self.player_w + 20, self.player_h))
# Корпус киберкара
car_points = [
(px + 8 + tilt, py),
(px + self.player_w - 8 + tilt, py),
(px + self.player_w, py + self.player_h),
(px, py + self.player_h)
]
pygame.draw.polygon(self.screen, (230, 235, 245), car_points)
# Кабина
windshield = [
(px + 12 + tilt, py + 22),
(px + self.player_w - 12 + tilt, py + 22),
(px + self.player_w - 10, py + 55),
(px + 10, py + 55)
]
pygame.draw.polygon(self.screen, (20, 20, 30), windshield)
# Неоновые полосы на кузове
pygame.draw.line(self.screen, COLOR_NEON_PINK, (px + self.player_w // 2 + tilt, py + 4), (px + self.player_w // 2, py + self.player_h - 4), 3)
# Задние фонари
pygame.draw.rect(self.screen, (255, 20, 40), (px + 4, py + self.player_h - 6, 14, 5))
pygame.draw.rect(self.screen, (255, 20, 40), (px + self.player_w - 18, py + self.player_h - 6, 14, 5))
def render(self):
self.screen.fill(COLOR_BG)
# Башни города
for b in self.buildings:
b.draw(self.screen)
self.draw_road()
# Трафик
for car in self.traffic:
car.draw(self.screen)
# Частицы
for p in self.particles:
p.draw(self.screen)
if not self.game_over:
self.draw_player()
# HUD / Интерфейс
kmh = int(self.speed * 0.38)
spd_text = self.font_big.render(f"{kmh}", True, COLOR_NEON_YELLOW if self.nitro_active else COLOR_WHITE)
unit_text = self.font_ui.render("KM/H", True, (160, 160, 180))
score_text = self.font_mid.render(f"SCORE: {self.score}", True, COLOR_WHITE)
dist_text = self.font_ui.render(f"DIST: {self.distance:.1f} KM", True, (140, 220, 255))
self.screen.blit(spd_text, (30, SCREEN_HEIGHT - 90))
self.screen.blit(unit_text, (135, SCREEN_HEIGHT - 65))
self.screen.blit(score_text, (30, 30))
self.screen.blit(dist_text, (30, 75))
# Шкала Nitro (N2O)
pygame.draw.rect(self.screen, (30, 30, 50), (SCREEN_WIDTH - 220, SCREEN_HEIGHT - 50, 180, 20), border_radius=4)
nitro_w = int(1.76 * self.nitro)
if nitro_w > 0:
pygame.draw.rect(self.screen, COLOR_GRID, (SCREEN_WIDTH - 218, SCREEN_HEIGHT - 48, nitro_w, 16), border_radius=3)
nitro_lbl = self.font_ui.render("NITRO [W / UP]", True, (200, 200, 220))
self.screen.blit(nitro_lbl, (SCREEN_WIDTH - 220, SCREEN_HEIGHT - 75))
if self.game_over:
overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 190))
self.screen.blit(overlay, (0, 0))
over_surf = self.font_big.render("CRASHED!", True, (255, 30, 60))
final_surf = self.font_mid.render(f"FINAL SCORE: {self.score}", True, COLOR_WHITE)
restart_surf = self.font_ui.render("PRESS 'R' TO RESTART", True, COLOR_GRID)
self.screen.blit(over_surf, (SCREEN_WIDTH // 2 - over_surf.get_width() // 2, SCREEN_HEIGHT // 2 - 80))
self.screen.blit(final_surf, (SCREEN_WIDTH // 2 - final_surf.get_width() // 2, SCREEN_HEIGHT // 2))
self.screen.blit(restart_surf, (SCREEN_WIDTH // 2 - restart_surf.get_width() // 2, SCREEN_HEIGHT // 2 + 60))
pygame.display.flip()
def reset_game(self):
self.player_x = float(self.lane_x[1])
self.player_vx = 0.0
self.speed = 400.0
self.nitro = 100.0
self.score = 0
self.near_miss_streak = 0
self.distance = 0.0
self.game_over = False
self.traffic.clear()
self.particles.clear()
def run(self):
while True:
dt = self.clock.tick(FPS) / 1000.0
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN and event.key == pygame.K_r and self.game_over:
self.reset_game()
self.update(dt)
self.render()
if __name__ == "__main__":
game = CyberHighwayRacer()
game.run()