diff --git a/buildconfig/stubs/pygame/sprite.pyi b/buildconfig/stubs/pygame/sprite.pyi index e29ebfc183..008145cb1c 100644 --- a/buildconfig/stubs/pygame/sprite.pyi +++ b/buildconfig/stubs/pygame/sprite.pyi @@ -276,7 +276,8 @@ def spritecollide( group: AbstractGroup[_TSprite], dokill: bool, collided: Optional[Callable[[_TSprite, _TSprite2], Any]] = None, -) -> list[_TSprite]: ... + ignore_self: bool, +) -> List[_TSprite]: ... def groupcollide( groupa: AbstractGroup[_TSprite], groupb: AbstractGroup[_TSprite2], @@ -288,4 +289,5 @@ def spritecollideany( sprite: _HasRect, group: AbstractGroup[_TSprite], collided: Optional[Callable[[_TSprite, _TSprite2], Any]] = None, + ignore_self: bool, ) -> Optional[_TSprite]: ... diff --git a/docs/reST/ref/sprite.rst b/docs/reST/ref/sprite.rst index a1e96a71c9..dc690f0797 100644 --- a/docs/reST/ref/sprite.rst +++ b/docs/reST/ref/sprite.rst @@ -644,7 +644,7 @@ Sprites are not thread safe. So lock them yourself if using threads. .. function:: spritecollide | :sl:`Find sprites in a group that intersect another sprite.` - | :sg:`spritecollide(sprite, group, dokill, collided = None) -> Sprite_list` + | :sg:`spritecollide(sprite, group, dokill, collided = None, ignore_self = False) -> Sprite_list` Return a list containing all Sprites in a Group that intersect with another Sprite. Intersection is determined by comparing the ``Sprite.rect`` @@ -659,6 +659,10 @@ Sprites are not thread safe. So lock them yourself if using threads. sprites must have a "rect" value, which is a rectangle of the sprite area, which will be used to calculate the collision. + The ignore_self argument is a bool. If set to True, the sprite will not + register a collision with itself, even if it is contained within the group + being tested. + collided callables: :: @@ -825,8 +829,8 @@ Sprites are not thread safe. So lock them yourself if using threads. .. function:: spritecollideany | :sl:`Simple test if a sprite intersects anything in a group.` - | :sg:`spritecollideany(sprite, group, collided = None) -> Sprite` Collision with the returned sprite. - | :sg:`spritecollideany(sprite, group, collided = None) -> None` No collision + | :sg:`spritecollideany(sprite, group, collided = None, ignore_self = False) -> Sprite` Collision with the returned sprite. + | :sg:`spritecollideany(sprite, group, collided = None, ignore_self = False) -> None` No collision If the sprite collides with any single sprite in the group, a single sprite from the group is returned. On no collision None is returned. @@ -840,6 +844,10 @@ Sprites are not thread safe. So lock them yourself if using threads. sprites must have a "rect" value, which is a rectangle of the sprite area, which will be used to calculate the collision. + The ignore_self argument is a bool. If set to True, the sprite will not + register a collision with itself, even if it is contained within the group + being tested. + .. ## pygame.sprite.spritecollideany ## .. ## ## diff --git a/src_py/sprite.py b/src_py/sprite.py index 6f7f9e32dd..b345ff645d 100644 --- a/src_py/sprite.py +++ b/src_py/sprite.py @@ -1664,10 +1664,10 @@ def collide_mask(left, right): return leftmask.overlap(rightmask, (xoffset, yoffset)) -def spritecollide(sprite, group, dokill, collided=None): +def spritecollide(sprite, group, dokill, collided=None, ignore_self=False): """find Sprites in a Group that intersect another Sprite - pygame.sprite.spritecollide(sprite, group, dokill, collided=None): + pygame.sprite.spritecollide(sprite, group, dokill, collided=None, ignore_self=False): return Sprite_list Return a list containing all Sprites in a Group that intersect with another @@ -1683,6 +1683,9 @@ def spritecollide(sprite, group, dokill, collided=None): sprites must have a "rect" value, which is a rectangle of the sprite area, which will be used to calculate the collision. + The ignore_self argument is a bool. If set to True, the sprite will not register + a collision with itself, even if it is contained within the group being tested. + """ # pull the default collision function in as a local variable outside # the loop as this makes the loop run faster @@ -1695,25 +1698,39 @@ def spritecollide(sprite, group, dokill, collided=None): for group_sprite in group.sprites(): if collided is not None: if collided(sprite, group_sprite): - group_sprite.kill() - append(group_sprite) + if not ignore_self or sprite != group_sprite: + group_sprite.kill() + append(group_sprite) else: if default_sprite_collide_func(group_sprite.rect): - group_sprite.kill() - append(group_sprite) + if not ignore_self or sprite != group_sprite: + group_sprite.kill() + append(group_sprite) return crashed if collided is not None: + if ignore_self: + return [ + group_sprite for group_sprite in group if collided(sprite, group_sprite) and sprite != group_sprite + ] + else: + return [ + group_sprite for group_sprite in group if collided(sprite, group_sprite) + ] + + if ignore_self: return [ - group_sprite for group_sprite in group if collided(sprite, group_sprite) + group_sprite + for group_sprite in group + if default_sprite_collide_func(group_sprite.rect) and sprite != group_sprite + ] + else: + return [ + group_sprite + for group_sprite in group + if default_sprite_collide_func(group_sprite.rect) ] - - return [ - group_sprite - for group_sprite in group - if default_sprite_collide_func(group_sprite.rect) - ] def groupcollide(groupa, groupb, dokilla, dokillb, collided=None): @@ -1752,7 +1769,7 @@ def groupcollide(groupa, groupb, dokilla, dokillb, collided=None): return crashed -def spritecollideany(sprite, group, collided=None): +def spritecollideany(sprite, group, collided=None, ignore_self=False): """finds any sprites in a group that collide with the given sprite pygame.sprite.spritecollideany(sprite, group): return sprite @@ -1770,6 +1787,8 @@ def spritecollideany(sprite, group, collided=None): sprites must have a "rect" value, which is a rectangle of the sprite area, which will be used to calculate the collision. + The ignore_self argument is a bool. If set to True, the sprite will not register + a collision with itself, even if it is contained within the group being tested. """ # pull the default collision function in as a local variable outside @@ -1779,10 +1798,12 @@ def spritecollideany(sprite, group, collided=None): if collided is not None: for group_sprite in group: if collided(sprite, group_sprite): - return group_sprite + if not ignore_self or sprite != group_sprite: + return group_sprite else: # Special case old behaviour for speed. for group_sprite in group: if default_sprite_collide_func(group_sprite.rect): - return group_sprite + if not ignore_self or sprite != group_sprite: + return group_sprite return None diff --git a/test/sprite_test.py b/test/sprite_test.py index 25415fce19..8000ece1a0 100644 --- a/test/sprite_test.py +++ b/test/sprite_test.py @@ -491,6 +491,28 @@ def test_collide_rect(self): self.assertFalse(pygame.sprite.collide_rect(self.s1, self.s3)) self.assertFalse(pygame.sprite.collide_rect(self.s3, self.s1)) + def test_sprites_ignoring_themselves_for_collisions(self): + # spritecollide(): Test that sprite collides with itself in the same group by default. + self.assertEqual( + sprite.spritecollide(self.s1, self.ag, dokill=False, collided=None), + [self.s1], + ) + # spritecollide(): Test that sprite does not collide with itself in the same group when ignore_self is passed. + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag, dokill=False, collided=None, ignore_self=True + ), + [], + ) + # spritecollideany(): Test that sprite collides with itself in the same group by default. + self.assertEqual( + sprite.spritecollideany(self.s1, self.ag, collided=None), self.s1 + ) + # spritecollideany(): Test that sprite does not collide with itself in the same group when ignore_self is passed. + self.assertIsNone( + sprite.spritecollideany(self.s1, self.ag, collided=None, ignore_self=True) + ) + ################################################################################