from __future__ import annotations
from typing import Optional, TYPE_CHECKING
import pygame
import snecs
from snecs.typedefs import EntityID
from nqp.world_elements.entity_components import Position
if TYPE_CHECKING:
pass
[docs]class Camera:
"""
Basic math for getting view of game world
Attributes:
zoom: positive numbers "zoom out", negative numbers "zoom in"
* World units are equal to pixels at zoom level of 0.0
"""
[docs] def __init__(self, size: pygame.Vector2):
self._pos = pygame.Vector2()
self._size = pygame.Vector2(size)
self._target_position: Optional[pygame.Vector2] = pygame.Vector2()
self._target_entity: Optional[EntityID] = None
self.zoom: float = 0.0
[docs] def centre(self, pos: pygame.Vector2):
"""
Immediately centre the camera on a world point
"""
self._pos.update(pos)
[docs] def move(self, x: int = 0, y: int = 0):
"""
Immediately move the camera by relative amount
"""
self._pos.x += x
self._pos.y += y
[docs] def set_target_position(self, pos: Optional[pygame.Vector2]):
"""
Move camera to position over time. Pass None to clear.
"""
if pos is None:
self._target_position = pos
else:
self._target_position = pygame.Vector2(pos)
[docs] def set_target_entity(self, entity: Optional[EntityID]):
"""
Smoothly track a game entity. Pass None to clear.
"""
self._target_entity = entity
[docs] def get_centre(self) -> pygame.Vector2:
"""
Get copy of the position
"""
return pygame.Vector2(self._pos)
[docs] def update(self, delta_time: float):
"""
Update camera movements
"""
if self._target_entity:
pos = snecs.entity_component(self._target_entity, Position)
self.set_target_position(pos.pos)
if self._target_position:
centre = self.get_centre()
offset = (self._target_position - centre) / 10 * (delta_time * 60)
self.centre(centre + offset)
[docs] def render_offset(self) -> pygame.Vector2:
"""
Return vector for drawing offset
"""
return self._origin() * -1
[docs] def clamp(self, rect: pygame.Rect):
"""
Move camera so that it is contained in the rect
Has same behaviour as `pygame.Rect.clamp`
"""
clamped = self.get_rect().clamp(rect)
self._pos.update(clamped.center)
[docs] def get_size(self) -> pygame.Vector2:
"""
Return vector of the size after zoom is applied
"""
size = self._size + (self._size * self.zoom)
return pygame.Vector2(round(abs(size[0])), round(abs(size[1])))
[docs] def get_rect(self) -> pygame.Rect:
"""
Return Pygame Rect representing the visible area of the camera
"""
size = self.get_size()
size = round(size.x), round(size.y)
pos = self._origin()
return pygame.Rect(pos, size)
[docs] def reset_movement(self):
"""
Immediately move camera to tracked position
"""
if self._target_entity:
pos = snecs.entity_component(self._target_entity, Position)
self.set_target_position(pos.pos)
if self._target_position:
self.centre(self._target_position)
def _origin(self) -> pygame.Vector2:
"""
Return vector representing top left corner of the view
"""
size = self._size + (self._size * self.zoom)
size.x = abs(size.x)
size.y = abs(size.y)
offset = self._pos - (size / 2)
return pygame.Vector2(round(offset[0]), round(offset[1]))