from pygame.surface import Surface from sprite.DynamicSprite import DynamicSprite from sprite.PositionScale import PositionScale from sprite.Sprite import Sprite from sprite.StaticSprite import StaticSprite MOTION_STEPS = 10 TOLERANCE = 1 class PhysicsElementsHandler: def __init__(self): self.sprites: list[Sprite] = [] def add_sprite(self, sprite: Sprite): self.sprites.append(sprite) def remove_sprite(self, sprite: Sprite): self.sprites.remove(sprite) def tick(self, dt: float): for sprite in self.sprites: sprite.tick(dt) # handle motion and collisions. To do this: # 1. Find all sprites that have collision enabled and store them in a list # 2. Create a list of all sprites that are dynamic sprites # 3. Sort the sprites by their y position # 4. For each sprite: # 4.1. Divide the motion into MOTION_STEPS steps # 4.2. For each step: # 4.2.1. Check if the sprite collides with any other sprite # 4.2.2. If it does, move the sprite back to the previous position and stop the motion # 4.2.3. If it doesn't, move the sprite to the new position colliders = [sprite for sprite in self.sprites if sprite.is_collider and isinstance(sprite, StaticSprite)] dynamic_sprites = [sprite for sprite in self.sprites if isinstance(sprite, DynamicSprite)] sorted_dynamic_sprites = sorted(dynamic_sprites, key=lambda spr: spr.position_scale.position[1]) for sprite in sorted_dynamic_sprites: self.attempt_move(sprite, colliders, MOTION_STEPS) def attempt_move(self, sprite: DynamicSprite, colliders: list[StaticSprite], motion_steps: int) -> bool: total_motion = sprite.motion motion_step = (total_motion[0] / motion_steps, total_motion[1] / motion_steps) # print(motion_step) for i in range(motion_steps): sprite.reset_touches() sprite.position_scale.position = ( sprite.position_scale.position[0] + motion_step[0], sprite.position_scale.position[1] ) if self.check_collides(sprite, colliders): sprite.position_scale.position = ( sprite.position_scale.position[0] - motion_step[0], sprite.position_scale.position[1] ) if sprite.motion[0] > 0: sprite.set_touches_right(True) if sprite.motion[0] < 0: sprite.set_touches_left(True) sprite.motion = (0, sprite.motion[1]) return False sprite.position_scale.position = ( sprite.position_scale.position[0], sprite.position_scale.position[1] + motion_step[1] ) if self.check_collides(sprite, colliders): sprite.position_scale.position = ( sprite.position_scale.position[0], sprite.position_scale.position[1] - motion_step[1] ) if sprite.motion[1] > 0: sprite.set_touches_bottom(True) if sprite.motion[1] < 0: sprite.set_touches_top(True) sprite.motion = (sprite.motion[0], 0) return False return True def check_collides(self, sprite: StaticSprite, colliders: list[StaticSprite]) -> bool: for collider in colliders: if sprite is not collider and sprite.collides_with(collider, TOLERANCE): return True return False def draw(self, screen: Surface, screen_transform: PositionScale): for sprite in self.sprites: sprite.draw(screen, screen_transform)