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.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))

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

View File

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