phantomcastle use overlap

This commit is contained in:
Dmitry Belyaev 2024-04-04 13:30:04 +03:00
parent f1b7b2e860
commit b8ecdd8f23
2 changed files with 50 additions and 16 deletions

View File

@ -262,11 +262,29 @@ class DrawableGameObject(ABC):
self.rect = pygame.Rect(coords, coords) self.rect = pygame.Rect(coords, coords)
self.assets = assets or (parent.assets if parent else None) self.assets = assets or (parent.assets if parent else None)
self._surface = None self._surface = None
self._mask = None
def __str__(self):
return f"{self.__class__.__name__}({self.coords})"
@property @property
def surface(self) -> pygame.Surface | None: def surface(self) -> pygame.Surface | None:
return self._surface or (self.parent.surface if self.parent else None) return self._surface or (self.parent.surface if self.parent else None)
@property
def mask(self) -> pygame.Mask | None:
if not self._mask:
self._mask = pygame.mask.from_surface(self.surface.convert_alpha())
return self._mask
def overlap(self, rect: pygame.Rect, mask: pygame.Mask) -> bool:
if not self.rect.colliderect(rect):
return False
offset = Coords(*self.rect.topleft) - Coords(*rect.topleft)
overlap = mask.overlap(self.mask, offset)
return overlap is not None
@property @property
def scene(self): def scene(self):
return self.parent.scene if self.parent else self return self.parent.scene if self.parent else self
@ -321,10 +339,9 @@ class Hero(DrawableGameObject, EventHandler):
def _check_collision(self, coords): def _check_collision(self, coords):
"""Проверка пересечения со стенами""" """Проверка пересечения со стенами"""
new_rect = self.surface.get_bounding_rect() new_rect = self.rect.copy()
new_rect.topleft = coords new_rect.topleft = coords
new_rect.scale_by_ip(0.99, 0.99) return self.scene.walls.check_collision(new_rect, self.mask)
return self.scene.walls.check_collision(new_rect)
@property @property
def speed(self): def speed(self):
@ -378,7 +395,7 @@ class Hero(DrawableGameObject, EventHandler):
def move(self, direction: Direction, step: int = 1): def move(self, direction: Direction, step: int = 1):
self.update_direction(direction) self.update_direction(direction)
self.coords += direction.as_coords() * step * self.speed // 3 self.coords += direction.as_coords() * step * self.speed // 3
self.scene.coins.collect(self.rect) self.scene.coins.collect(self)
def handle_event(self, event: pygame.event.Event): def handle_event(self, event: pygame.event.Event):
if not self.active: if not self.active:
@ -449,9 +466,9 @@ class Walls(DrawableGameObject):
for block in self.blocks: for block in self.blocks:
block.draw() block.draw()
def check_collision(self, rect: pygame.Rect) -> bool: def check_collision(self, rect: pygame.Rect, mask: pygame.Mask) -> bool:
for block in self.blocks: for block in self.blocks:
if block.rect.colliderect(rect): if block.overlap(rect, mask):
return True return True
return False return False
@ -552,8 +569,8 @@ class Coins(DrawableGameObject):
coin.coords = last_pos coin.coords = last_pos
self.collected_coins.append(coin) self.collected_coins.append(coin)
def collect(self, rect: pygame.Rect): def collect(self, actor: DrawableGameObject):
mined = [*filter(lambda coin: coin.bounding_rect.colliderect(rect), self.coins)] mined = [*filter(lambda coin: coin.overlap(actor.rect, actor.mask), self.coins)]
for coin in mined: for coin in mined:
self.coins.remove(coin) self.coins.remove(coin)
self.add_to_collected(coin) self.add_to_collected(coin)

View File

@ -177,11 +177,29 @@ class DrawableGameObject(ABC):
self.rect = pygame.Rect(coords, coords) self.rect = pygame.Rect(coords, coords)
self.assets = assets or (parent.assets if parent else None) self.assets = assets or (parent.assets if parent else None)
self._surface = None self._surface = None
self._mask = None
def __str__(self):
return f"{self.__class__.__name__}({self.coords})"
@property @property
def surface(self) -> pygame.Surface | None: def surface(self) -> pygame.Surface | None:
return self._surface or (self.parent.surface if self.parent else None) return self._surface or (self.parent.surface if self.parent else None)
@property
def mask(self) -> pygame.Mask | None:
if not self._mask:
self._mask = pygame.mask.from_surface(self.surface.convert_alpha())
return self._mask
def overlap(self, rect: pygame.Rect, mask: pygame.Mask) -> bool:
if not self.rect.colliderect(rect):
return False
offset = Coords(*self.rect.topleft) - Coords(*rect.topleft)
overlap = mask.overlap(self.mask, offset)
return overlap is not None
@property @property
def scene(self): def scene(self):
return self.parent.scene if self.parent else self return self.parent.scene if self.parent else self
@ -236,10 +254,9 @@ class Hero(DrawableGameObject, KeysHandler):
def _check_collision(self, coords): def _check_collision(self, coords):
"""Проверка пересечения со стенами""" """Проверка пересечения со стенами"""
new_rect = self.surface.get_bounding_rect() new_rect = self.rect.copy()
new_rect.topleft = coords new_rect.topleft = coords
new_rect.scale_by_ip(0.99, 0.99) return self.scene.walls.check_collision(new_rect, self.mask)
return self.scene.walls.check_collision(new_rect)
@property @property
def speed(self): def speed(self):
@ -293,7 +310,7 @@ class Hero(DrawableGameObject, KeysHandler):
def move(self, direction: Direction, step: int = 1): def move(self, direction: Direction, step: int = 1):
self.update_direction(direction) self.update_direction(direction)
self.coords += direction.as_coords() * step * self.speed // 3 self.coords += direction.as_coords() * step * self.speed // 3
self.scene.coins.collect(self.rect) self.scene.coins.collect(self)
def handle_keys(self, keys_pressed): def handle_keys(self, keys_pressed):
if not self.active: if not self.active:
@ -362,9 +379,9 @@ class Walls(DrawableGameObject):
for block in self.blocks: for block in self.blocks:
block.draw() block.draw()
def check_collision(self, rect: pygame.Rect) -> bool: def check_collision(self, rect: pygame.Rect, mask: pygame.Mask) -> bool:
for block in self.blocks: for block in self.blocks:
if block.rect.colliderect(rect): if block.overlap(rect, mask):
return True return True
return False return False
@ -463,8 +480,8 @@ class Coins(DrawableGameObject):
coin.coords = last_pos coin.coords = last_pos
self.collected_coins.append(coin) self.collected_coins.append(coin)
def collect(self, rect: pygame.Rect): def collect(self, actor: DrawableGameObject):
mined = [*filter(lambda coin: coin.bounding_rect.colliderect(rect), self.coins)] mined = [*filter(lambda coin: coin.overlap(actor.rect, actor.mask), self.coins)]
for coin in mined: for coin in mined:
self.coins.remove(coin) self.coins.remove(coin)
self.add_to_collected(coin) self.add_to_collected(coin)