From ff76268348a2693ffa14e1b5ad71743282aef76f Mon Sep 17 00:00:00 2001 From: celeritydesign Date: Sun, 27 Oct 2024 12:14:26 +0000 Subject: [PATCH 1/4] Optional arg to avoid a sprite registering collision with themself --- buildconfig/stubs/pygame/sprite.pyi | 2 ++ src_py/sprite.py | 50 ++++++++++++++++++++--------- test/sprite_test.py | 22 +++++++++++++ 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/buildconfig/stubs/pygame/sprite.pyi b/buildconfig/stubs/pygame/sprite.pyi index 8220c36ba9..e702e1d504 100644 --- a/buildconfig/stubs/pygame/sprite.pyi +++ b/buildconfig/stubs/pygame/sprite.pyi @@ -281,6 +281,7 @@ def spritecollide( group: AbstractGroup[_TSprite], dokill: bool, collided: Optional[Callable[[_TSprite, _TSprite2], Any]] = None, + ignore_self: bool, ) -> List[_TSprite]: ... def groupcollide( groupa: AbstractGroup[_TSprite], @@ -293,4 +294,5 @@ def spritecollideany( sprite: _HasRect, group: AbstractGroup[_TSprite], collided: Optional[Callable[[_TSprite, _TSprite2], Any]] = None, + ignore_self: bool, ) -> Optional[_TSprite]: ... diff --git a/src_py/sprite.py b/src_py/sprite.py index 6f7f9e32dd..a3d742c75e 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 @@ -1695,25 +1695,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 +1766,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 +1784,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. + ignore_self is used to avoid a sprite colliding with itself if it exists + in the group being checked against. """ # pull the default collision function in as a local variable outside @@ -1779,10 +1795,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) + ) + ################################################################################ From a304f847140e4301b822d98a01dc7e410cd9cdf3 Mon Sep 17 00:00:00 2001 From: celeritydesign Date: Sun, 27 Oct 2024 12:33:45 +0000 Subject: [PATCH 2/4] Added comments for optional arg 'ignore_self' --- src_py/sprite.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src_py/sprite.py b/src_py/sprite.py index a3d742c75e..b345ff645d 100644 --- a/src_py/sprite.py +++ b/src_py/sprite.py @@ -1683,6 +1683,9 @@ def spritecollide(sprite, group, dokill, collided=None, ignore_self=False): 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 @@ -1784,8 +1787,8 @@ def spritecollideany(sprite, group, collided=None, ignore_self=False): sprites must have a "rect" value, which is a rectangle of the sprite area, which will be used to calculate the collision. - ignore_self is used to avoid a sprite colliding with itself if it exists - in the group being checked against. + 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 From 2b6f846bdd9c3a02e78e6f64e72f54f27a6d5553 Mon Sep 17 00:00:00 2001 From: celeritydesign Date: Sun, 27 Oct 2024 16:13:00 +0000 Subject: [PATCH 3/4] Docs update for optional collide arg 'ignore_self' --- docs/reST/ref/sprite.rst | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/reST/ref/sprite.rst b/docs/reST/ref/sprite.rst index 268727a1d0..e7fc720d83 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,14 +829,14 @@ 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. If you don't need all the features of the ``pygame.sprite.spritecollide()`` function, this - function will be a bit quicker. + function will be a bit quicker.. The collided argument is a callback function used to calculate if two sprites are colliding. It should take two sprites as values and return a bool value @@ -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 ## .. ## ## From 7866505ae1d23b1191b18c30c10551ff3ff00e0b Mon Sep 17 00:00:00 2001 From: Dan Lawrence Date: Tue, 31 Dec 2024 14:57:36 +0000 Subject: [PATCH 4/4] removing extra . --- docs/reST/ref/sprite.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reST/ref/sprite.rst b/docs/reST/ref/sprite.rst index 6c02355e73..dc690f0797 100644 --- a/docs/reST/ref/sprite.rst +++ b/docs/reST/ref/sprite.rst @@ -836,7 +836,7 @@ Sprites are not thread safe. So lock them yourself if using threads. sprite from the group is returned. On no collision None is returned. If you don't need all the features of the ``pygame.sprite.spritecollide()`` function, this - function will be a bit quicker.. + function will be a bit quicker. The collided argument is a callback function used to calculate if two sprites are colliding. It should take two sprites as values and return a bool value