refactor: improve rendering process

This commit is contained in:
2025-10-18 15:10:13 +02:00
parent 6805e69509
commit 2b20582b87
7 changed files with 114 additions and 48 deletions

BIN
assets/fonts/Ubuntu-M.ttf Normal file

Binary file not shown.

BIN
assets/fonts/Ubuntu-R.ttf Normal file

Binary file not shown.

View File

@@ -2,7 +2,8 @@ import pygame
from src.camera import Camera from src.camera import Camera
from src.car import Car 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 from src.vec import Vec
@@ -10,6 +11,7 @@ class Game:
DEFAULT_SIZE = (1280, 720) DEFAULT_SIZE = (1280, 720)
BACKGROUND_COLOR = (80, 80, 80) BACKGROUND_COLOR = (80, 80, 80)
MAX_FPS = 60 MAX_FPS = 60
FPS_COLOR = (255, 0, 0)
def __init__(self) -> None: def __init__(self) -> None:
pygame.init() pygame.init()
@@ -23,6 +25,10 @@ class Game:
self.camera: Camera = Camera() self.camera: Camera = Camera()
self.clock: pygame.time.Clock = pygame.time.Clock() 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): def mainloop(self):
while self.running: while self.running:
@@ -53,29 +59,13 @@ class Game:
def render(self): def render(self):
self.win.fill(self.BACKGROUND_COLOR) self.win.fill(self.BACKGROUND_COLOR)
self.render_track() self.track.render(self.win, self.camera)
self.render_car() self.render_car()
if self.show_fps:
self.render_fps()
pygame.display.flip() 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): def render_car(self):
u: Vec = self.car.direction * 0.3 u: Vec = self.car.direction * 0.3
v: Vec = self.car.direction.perp * 0.2 v: Vec = self.car.direction.perp * 0.2
@@ -107,3 +97,9 @@ class Game:
self.car.left = False self.car.left = False
elif event.key == pygame.K_d: elif event.key == pygame.K_d:
self.car.right = False 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))

0
src/objects/__init__.py Normal file
View File

54
src/objects/road.py Normal file
View File

@@ -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])

View File

@@ -2,10 +2,15 @@ from __future__ import annotations
import json 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.utils import ROOT
from src.vec import Vec from src.vec import Vec
TrackObject.init()
class Track: class Track:
TRACKS_DIRECTORY = ROOT / "assets" / "tracks" TRACKS_DIRECTORY = ROOT / "assets" / "tracks"
@@ -36,26 +41,8 @@ class Track:
self.objects = [] self.objects = []
for obj_data in data: for obj_data in data:
if obj_data["type"] == "road": self.objects.append(TrackObject.load(obj_data))
self.objects.append(Road.load(obj_data))
def render(self, surf: pygame.Surface, camera: Camera):
class RoadPoint: for object in self.objects:
def __init__(self, pos: Vec, normal: Vec, width: float) -> None: object.render(surf, camera)
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"]])

View File

@@ -1,13 +1,42 @@
import importlib
import pkgutil
from enum import StrEnum from enum import StrEnum
from typing import Optional, Self
import pygame
import src.objects
from src.camera import Camera
class TrackObjectType(StrEnum): class TrackObjectType(StrEnum):
Road = "road" Road = "road"
Unknown = "unknown"
class TrackObject: class TrackObject:
def __init__( REGISTRY = {}
self, type: TrackObjectType = TrackObjectType.Unknown
type: TrackObjectType,
) -> None: @staticmethod
self.type: TrackObjectType = type 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