From b8ecdd8f2335e0b53e3d5aa78458f12d951b825f Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 4 Apr 2024 13:30:04 +0300 Subject: [PATCH] phantomcastle use overlap --- mod_pygame/phantom_castle.py | 33 +++++++++++++++++++++++-------- pygame-wasm/phantomcastle/main.py | 33 +++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/mod_pygame/phantom_castle.py b/mod_pygame/phantom_castle.py index 5b16b7e..4b91c6e 100644 --- a/mod_pygame/phantom_castle.py +++ b/mod_pygame/phantom_castle.py @@ -262,11 +262,29 @@ class DrawableGameObject(ABC): self.rect = pygame.Rect(coords, coords) self.assets = assets or (parent.assets if parent else None) self._surface = None + self._mask = None + + def __str__(self): + return f"{self.__class__.__name__}({self.coords})" @property def surface(self) -> pygame.Surface | 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 def scene(self): return self.parent.scene if self.parent else self @@ -321,10 +339,9 @@ class Hero(DrawableGameObject, EventHandler): def _check_collision(self, coords): """Проверка пересечения со стенами""" - new_rect = self.surface.get_bounding_rect() + new_rect = self.rect.copy() new_rect.topleft = coords - new_rect.scale_by_ip(0.99, 0.99) - return self.scene.walls.check_collision(new_rect) + return self.scene.walls.check_collision(new_rect, self.mask) @property def speed(self): @@ -378,7 +395,7 @@ class Hero(DrawableGameObject, EventHandler): def move(self, direction: Direction, step: int = 1): self.update_direction(direction) 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): if not self.active: @@ -449,9 +466,9 @@ class Walls(DrawableGameObject): for block in self.blocks: 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: - if block.rect.colliderect(rect): + if block.overlap(rect, mask): return True return False @@ -552,8 +569,8 @@ class Coins(DrawableGameObject): coin.coords = last_pos self.collected_coins.append(coin) - def collect(self, rect: pygame.Rect): - mined = [*filter(lambda coin: coin.bounding_rect.colliderect(rect), self.coins)] + def collect(self, actor: DrawableGameObject): + mined = [*filter(lambda coin: coin.overlap(actor.rect, actor.mask), self.coins)] for coin in mined: self.coins.remove(coin) self.add_to_collected(coin) diff --git a/pygame-wasm/phantomcastle/main.py b/pygame-wasm/phantomcastle/main.py index b9bc98e..1ea8966 100644 --- a/pygame-wasm/phantomcastle/main.py +++ b/pygame-wasm/phantomcastle/main.py @@ -177,11 +177,29 @@ class DrawableGameObject(ABC): self.rect = pygame.Rect(coords, coords) self.assets = assets or (parent.assets if parent else None) self._surface = None + self._mask = None + + def __str__(self): + return f"{self.__class__.__name__}({self.coords})" @property def surface(self) -> pygame.Surface | 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 def scene(self): return self.parent.scene if self.parent else self @@ -236,10 +254,9 @@ class Hero(DrawableGameObject, KeysHandler): def _check_collision(self, coords): """Проверка пересечения со стенами""" - new_rect = self.surface.get_bounding_rect() + new_rect = self.rect.copy() new_rect.topleft = coords - new_rect.scale_by_ip(0.99, 0.99) - return self.scene.walls.check_collision(new_rect) + return self.scene.walls.check_collision(new_rect, self.mask) @property def speed(self): @@ -293,7 +310,7 @@ class Hero(DrawableGameObject, KeysHandler): def move(self, direction: Direction, step: int = 1): self.update_direction(direction) 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): if not self.active: @@ -362,9 +379,9 @@ class Walls(DrawableGameObject): for block in self.blocks: 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: - if block.rect.colliderect(rect): + if block.overlap(rect, mask): return True return False @@ -463,8 +480,8 @@ class Coins(DrawableGameObject): coin.coords = last_pos self.collected_coins.append(coin) - def collect(self, rect: pygame.Rect): - mined = [*filter(lambda coin: coin.bounding_rect.colliderect(rect), self.coins)] + def collect(self, actor: DrawableGameObject): + mined = [*filter(lambda coin: coin.overlap(actor.rect, actor.mask), self.coins)] for coin in mined: self.coins.remove(coin) self.add_to_collected(coin)