Finding all collisions within 1 group of sprites (efficiently) - python

I have a method that recieves a Group of sprites. For each sprite, I need to find every other sprite it collides with and call its handle_collision method.
How can I find all the sprites each sprite in the group collides with? Here's my idea for finding all collisions within a group.
#staticmethod
def check_collision_group(most_sprites: pygame.sprite.Group, screen):
for sprite in most_sprites:
most_sprites.remove(sprite)
collided_sprites = pygame.sprite.Group()
for sprite2 in most_sprites:
if pygame.sprite.collide_rect(sprite, sprite2):
collided_sprites.add(sprite2)
sprite.handle_collision(collided_sprites)
most_sprites.add(sprite)
However, this is slow. The amount of collisions this had to check astonished me when I printed them out.
A bit of searching and I've found 2 ideas that are probably a lot faster.
Making the first sprite a Group and using collidegroup from pygame.sprite library.
Only testing the sprites around the sprite you are testing. Idea from https://gamedev.stackexchange.com/questions/45392/how-do-i-detect-multiple-sprite-collisions-when-there-are-10-sprites .
To restate question: What is a fast (but hopefully still relatively simple) way to find every other sprite in the group that each sprite collides with?

To find all collisions between a sprite and everything else in it's group.
def check_collision_group(most_sprites, screen):
single = pygame.sprite.GroupSingle()
for sprite in most_sprites:
most_sprites.remove(sprite)
single.add(sprite)
collided_sprites_dict = pygame.sprite.groupcollide(
single, most_sprites, False, False)
most_sprites.add(sprite)
Note: It's important to use add on the groupsingle, reassignment will keep adding more and more groups to your system and bog down your game. I am not sure why, but changing it to my groupsingle and adding to it fixed the issue.
The speed of this seems to be relatively fast. To calculate all the collisions in a group of ~50 sprites it took about 1.4 milliseconds. I have not tried method 2, which is only checking collisions based on proximity, but I do not see a reason to, as the speed of groupcollide is rather decent.

Related

PyGame: why isn't collision working well?

It detects collision most of times, but sometimes it doesn't. Here is collision checker:
def collide_check(this, object_1):
bullet_rect = this.image.get_rect().move(this.bullet_x, this.bullet_y)
object_1_rect = object_1.image.get_rect().move(object_1.ice_x, object_1.ice_y)
if bullet_rect.colliderect(object_1_rect):
#consequences of collision
Looks like your typical 'Bullet through paper'-problem.
Since .move() doesn't sub-step you are simply not overlapping the other rect, and therefor do not fulfil the colliderect condition.
Simply put: You 'erase' the original rect, and draw it anew in the new position. Same for the other rect. They do not overlap, because the movement was too great, therefore no collision is detected.
If you're calling collide_check() multiple times on the same object, I may have found the reason for your problem.
For unknown reasons, .colliderect() won't detect collisions if it's being called on an object that has already had collisions detected upon it.
For more information, see this similar question.

Barrier shield collision detection for Space Invaders game in Python with pygame

I'm currently making the Space Invaders game in Python with pygame.
I currently have 3 base barriers that are all built with 1 x 1 pixel blocks (that 'extend' the pygame.sprite.Sprite class). For collision detection, I check if a missile has collided with the barrier.
For now, everything works, when I fire and hit one of the barriers, the pixel that was hit is eliminated.
Now the thing that bothers me is that in the original Space Invaders, when the ship's missile (or an alien's missile for that matter)
hits the barrier, it causes an 'explosion' that affects multiple pixels of the barrier. I would like to implement this
in my python/pygame code. How would I go about this?
Here is my collision detection code in my missile class (that 'extends' the pygame.sprite.Sprite):
baseBarrier_hit_list = pygame.sprite.spritecollide(self, all_baseBarrier_group, True)
for pixel in baseBarrier_hit_list:
self.kill() # I kill the missile after collision so that the other barrier pixels are unaffected.
break #so that the other pixels in the column are unaffected.
I thought of 'artificially' adding 'random' pixels to the baseBarrier_hit_list but I'm unable to add elements to the baseBarrier_hit_list.
Here is a video link of the original space invaders to see what I mean:
https://www.youtube.com/watch?v=axlx3o0codc
Here is also a link to a python/pygame version of Space Invaders that shows that only one pixel is affected when collision occurs between missile and base barrier. (Note that this is not MY version of the game). https://www.youtube.com/watch?v=_2yUP3WMDRc
EDIT: (see comment for picture explanation)
EDIT: TemporalWolf's suggestion worked. This is the code I added to my function.
shield_hit_list_random = pygame.sprite.spritecollide(self, all_blockShields_group, False, pygame.sprite.collide_rect_ratio(2))
Passing a False to the function prevents the killing of the sprites. Then, I randomly kill the some sprites in the shield_hit_list_random group like this:
for shield in shield_hit_list_random:
pourcentage =randint(0,3)
if pourcentage == 2:
shield.kill()
I would try using the scaled collision rect or the circle equivalent:
pygame.sprite.collide_rect_ratio() Collision detection between two
sprites, using rects scaled to a ratio.
collide_rect_ratio(ratio) -> collided_callable
A callable class that checks for collisions between
two sprites, using a scaled version of the sprites rects.
Is created with a ratio, the instance is then intended to be passed as
a collided callback function to the *collide functions.
A ratio is a floating point number - 1.0 is the same size, 2.0 is
twice as big, and 0.5 is half the size.
New in pygame 1.8.1
which would be given as the 4th parameter to your spritecollide function

Pygame - How to destroy an asteroid and create smaller ones in its place

I'm making this classic style asteroids game and I've beaten all the challenges that come with it. Right now there's a new one.
When a bullet hits one of the asteroids, the expected behavior is that it disappear, then three or four smaller asteroids appear in its place, each with random velocity so it looks like a kind of explosion. But instead this weird behavior show in this video I just recorded:
https://youtu.be/2ZJvOcB-ZKM
As you can see it does 90% of what it's supposed to do, the asteroid dissapear, then I create four new and smaller asteroids in its pace but then, for some reason they dissapear when they should behave like normal asteroids, since they inherit everything.
The game source code can be found here: https://github.com/JuanBonnett/pysteroids
The classes in charge of handling the collision Bullet->Asteroid are:
asteroids.py Main file creates all the objects, the ship, the asteroids generator and the collision detector object.
physics.py Where the CollisionDetector class is and which detects when they collide and call its dependencies to do stuff with the collision
asteroid.py has an asteroid generator that generates smaller asteroids in the place of the collision, which I call debris.
When an asteroid blows up, you are passing it a pos vector of where to start.
You are applying that same vector instance to all resulting asteroids.
All smaller asteroids have the same self.pos value as their siblings that came from the same parent asteroid.
It looks weird because the self.points property is working just fine and updated/translated correctly and so you're seeing two positions fight each other and give chaotic/unpredictable behavior.
Basically the problem is the pos = _pos line in AsteroidGenerator.generate. It should be making a copy of the vector instead of assigning the reference directly to the new asteroid.
I discovered this by putting print statements in your asteroid render code printing the position: print(self.pos.x, self.pos.y). I noticed all asteroids had the same self.pos but were being given supposedly random velocity vectors, so I figured it was probably an object reference problem and followed where that was being assigned.

Pygame Collision on Rect

I'm a student in Computer Programming at my high school and really need help to make the square an obstacle that ships can't enter and bullets disappear once you shoot at the square also for the circle the same thing. And both ships die if they collide with each other. Please help I know my question isn't the best worded but i really need help.
the link to the code is here https://docs.google.com/file/d/0B-Pb_T-Vgr3-TnhRY0lvVjJHX0U/edit?usp=sharing
I would suggest that you create a GameObject class which extends pygame.sprite.Sprite and both Ship and Bullet classes which extend GameObject. This allows you to then easily add properties that both will need, such as velocity and acceleration, and you can create a collide method that is overridden by both for specific behavior. The added bonus here is that pygame.sprite.collide_rect will work properly as such:
if (pygame.sprite.collide_rect(sprite1, sprite2)):
# sprite1 and sprite2 are colliding!
# do something, such as calling sprite1.collide(sprite2)
# and sprite2.collide(sprite1)
So, pygame.sprite.collide_rect checks if two sprites collide using the Sprite.rect property of each. This functionality could also be recapitulated using rect.colliderect:
sprite1.rect.colliderect(sprite2.rect)

Using colliderect in Pygame

How do I tell python to detect whether two objects/ images touch each other? For example when an image of pacman touches an image of a ghost?
http://www.pygame.org/docs/ref/rect.html#pygame.Rect.colliderect
colliderect()
test if two rectangles overlap
colliderect(Rect) -> bool
Returns true if any portion of either rectangle overlap (except the
top+bottom or left+right edges).
If the only collision detection between sprites is between pac-man and other objects, then just call colliderect on every combination of pacman's collision rectangle and every other collision rectangle.
If every single combination of collisions can be meaningful, then produce a big list of all of them and colliderect each rectangle with each rectangle further along in the list.
Every collision that occurs, you can choose to do something - you could even call both objects, passing the other object that collided, and thereby allowing the logic to be contained within one or both of the objects.
I'm assuming you're using Sprites for your pacman and ghost? If so you want one of the sprite collision functions: http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollide
Otherwise, use the Rect collision Patashu links.

Categories

Resources