diff --git a/assets/fonts/Ubuntu-M.ttf b/assets/fonts/Ubuntu-M.ttf new file mode 100644 index 0000000..ca9c03a Binary files /dev/null and b/assets/fonts/Ubuntu-M.ttf differ diff --git a/assets/fonts/Ubuntu-R.ttf b/assets/fonts/Ubuntu-R.ttf new file mode 100644 index 0000000..d748728 Binary files /dev/null and b/assets/fonts/Ubuntu-R.ttf differ diff --git a/src/game.py b/src/game.py index b91608f..5f0f16b 100644 --- a/src/game.py +++ b/src/game.py @@ -2,7 +2,8 @@ import pygame from src.camera import Camera from src.car import Car -from src.track import Road, Track +from src.track import Track +from src.utils import ROOT from src.vec import Vec @@ -10,6 +11,7 @@ class Game: DEFAULT_SIZE = (1280, 720) BACKGROUND_COLOR = (80, 80, 80) MAX_FPS = 60 + FPS_COLOR = (255, 0, 0) def __init__(self) -> None: pygame.init() @@ -23,6 +25,10 @@ class Game: self.camera: Camera = Camera() self.clock: pygame.time.Clock = pygame.time.Clock() + self.font: pygame.font.Font = pygame.font.Font( + str(ROOT / "assets" / "fonts" / "Ubuntu-M.ttf"), 20 + ) + self.show_fps: bool = True def mainloop(self): while self.running: @@ -53,29 +59,13 @@ class Game: def render(self): self.win.fill(self.BACKGROUND_COLOR) - self.render_track() + self.track.render(self.win, self.camera) self.render_car() + if self.show_fps: + self.render_fps() pygame.display.flip() - def render_track(self): - road: Road = self.track.objects[0] # type: ignore - - side1: list[Vec] = [] - side2: list[Vec] = [] - - for i, pt in enumerate(road.pts): - p1: Vec = pt.pos - p2: Vec = p1 + pt.normal * pt.width - p3: Vec = p1 - pt.normal * pt.width - side1.append(self.camera.world2screen(p2)) - side2.append(self.camera.world2screen(p3)) - col: tuple[float, float, float] = (i * 10 + 150, 100, 100) - pygame.draw.circle(self.win, col, self.camera.world2screen(p1), 5) - - pygame.draw.lines(self.win, (255, 255, 255), True, side1) - pygame.draw.lines(self.win, (255, 255, 255), True, side2) - def render_car(self): u: Vec = self.car.direction * 0.3 v: Vec = self.car.direction.perp * 0.2 @@ -107,3 +97,9 @@ class Game: self.car.left = False elif event.key == pygame.K_d: self.car.right = False + + def render_fps(self): + txt: pygame.Surface = self.font.render( + f"{self.clock.get_fps():.1f}", True, self.FPS_COLOR + ) + self.win.blit(txt, (self.win.get_width() - txt.get_width(), 0)) diff --git a/src/objects/__init__.py b/src/objects/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/road.py b/src/objects/road.py new file mode 100644 index 0000000..03e1ee8 --- /dev/null +++ b/src/objects/road.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +import pygame + +from src.camera import Camera +from src.track_object import TrackObject, TrackObjectType +from src.vec import Vec + + +class Road(TrackObject): + type = TrackObjectType.Road + + def __init__(self, pts: list[RoadPoint]) -> None: + super().__init__() + self.pts: list[RoadPoint] = pts + + @classmethod + def load(cls, data: dict) -> Road: + return Road([RoadPoint.load(pt) for pt in data["pts"]]) + + def render(self, surf: pygame.Surface, camera: Camera): + side1: list[Vec] = [] + side2: list[Vec] = [] + + for i, pt in enumerate(self.pts): + p1: Vec = pt.pos + p2: Vec = p1 + pt.normal * pt.width + p3: Vec = p1 - pt.normal * pt.width + side1.append(camera.world2screen(p2)) + side2.append(camera.world2screen(p3)) + col: tuple[float, float, float] = (i * 10 + 150, 100, 100) + pygame.draw.circle(surf, col, camera.world2screen(p1), 5) + + n: int = len(self.pts) + for i in range(n): + pygame.draw.polygon( + surf, + (100, 100, 100), + [side1[i], side1[(i + 1) % n], side2[(i + 1) % n], side2[i]], + ) + + pygame.draw.lines(surf, (255, 255, 255), True, side1) + pygame.draw.lines(surf, (255, 255, 255), True, side2) + + +class RoadPoint: + def __init__(self, pos: Vec, normal: Vec, width: float) -> None: + self.pos: Vec = pos + self.normal: Vec = normal.normalized + self.width: float = width + + @staticmethod + def load(data: list[float]) -> RoadPoint: + return RoadPoint(Vec(data[0], data[1]), Vec(data[2], data[3]), data[4]) diff --git a/src/track.py b/src/track.py index c6b179f..eed9f09 100644 --- a/src/track.py +++ b/src/track.py @@ -2,10 +2,15 @@ from __future__ import annotations import json -from src.track_object import TrackObject, TrackObjectType +import pygame + +from src.camera import Camera +from src.track_object import TrackObject from src.utils import ROOT from src.vec import Vec +TrackObject.init() + class Track: TRACKS_DIRECTORY = ROOT / "assets" / "tracks" @@ -36,26 +41,8 @@ class Track: self.objects = [] for obj_data in data: - if obj_data["type"] == "road": - self.objects.append(Road.load(obj_data)) + self.objects.append(TrackObject.load(obj_data)) - -class RoadPoint: - def __init__(self, pos: Vec, normal: Vec, width: float) -> None: - self.pos: Vec = pos - self.normal: Vec = normal.normalized - self.width: float = width - - @staticmethod - def load(data: list[float]) -> RoadPoint: - return RoadPoint(Vec(data[0], data[1]), Vec(data[2], data[3]), data[4]) - - -class Road(TrackObject): - def __init__(self, pts: list[RoadPoint]) -> None: - super().__init__(TrackObjectType.Road) - self.pts: list[RoadPoint] = pts - - @staticmethod - def load(data: dict) -> Road: - return Road([RoadPoint.load(pt) for pt in data["pts"]]) + def render(self, surf: pygame.Surface, camera: Camera): + for object in self.objects: + object.render(surf, camera) diff --git a/src/track_object.py b/src/track_object.py index 3e9a162..abbf82e 100644 --- a/src/track_object.py +++ b/src/track_object.py @@ -1,13 +1,42 @@ +import importlib +import pkgutil from enum import StrEnum +from typing import Optional, Self + +import pygame + +import src.objects +from src.camera import Camera class TrackObjectType(StrEnum): Road = "road" + Unknown = "unknown" + class TrackObject: - def __init__( - self, - type: TrackObjectType, - ) -> None: - self.type: TrackObjectType = type + REGISTRY = {} + type: TrackObjectType = TrackObjectType.Unknown + + @staticmethod + def init(): + package = src.objects + for _, modname, _ in pkgutil.walk_packages( + package.__path__, package.__name__ + "." + ): + importlib.import_module(modname) + + def __init_subclass__(cls, **kwargs) -> None: + super().__init_subclass__(**kwargs) + TrackObject.REGISTRY[cls.type] = cls + + @classmethod + def load(cls, data: dict) -> Self: + obj_type: Optional[TrackObjectType] = data.get("type") + if obj_type not in cls.REGISTRY: + raise ValueError(f"Unknown object tyoe: {obj_type}") + return cls.REGISTRY[obj_type].load(data) + + def render(self, surf: pygame.Surface, camera: Camera): + pass