Mi2dRPGamEng/engine/camera.py

43 lines
1.7 KiB
Python

from __future__ import annotations
class OrthoCamera:
"""Top-down orthographic camera; world space is tile units with y increasing downward."""
def __init__(self, viewport_w: int, viewport_h: int, tiles_visible_y: float = 12.0):
self.x = 0.0
self.y = 0.0
self.viewport_w = viewport_w
self.viewport_h = viewport_h
self.tiles_visible_y = tiles_visible_y
def set_target(self, x: float, y: float) -> None:
self.x, self.y = x, y
def resize(self, viewport_w: int, viewport_h: int) -> None:
self.viewport_w, self.viewport_h = viewport_w, viewport_h
def _world_bounds(self) -> tuple[float, float, float, float]:
half_h = self.tiles_visible_y / 2
aspect = self.viewport_w / max(self.viewport_h, 1)
half_w = half_h * aspect
return self.x - half_w, self.x + half_w, self.y - half_h, self.y + half_h
def scale_offset(self) -> tuple[tuple[float, float], tuple[float, float]]:
"""Returns (scale, offset) such that ndc = world * scale + offset."""
left, right, top, bottom = self._world_bounds()
scale_x = 2.0 / (right - left)
scale_y = -2.0 / (bottom - top)
offset_x = -left * scale_x - 1.0
offset_y = 1.0 - top * scale_y
return (scale_x, scale_y), (offset_x, offset_y)
def screen_to_world(self, px: float, py: float) -> tuple[float, float]:
"""Unprojects a canvas logical-pixel position (0,0 top-left) into world tile coords."""
left, right, top, bottom = self._world_bounds()
wx = left + (px / max(self.viewport_w, 1)) * (right - left)
wy = top + (py / max(self.viewport_h, 1)) * (bottom - top)
return wx, wy