spritecollide and killing sprites - python

I have two sprite groups, ship_list has 20 ship sprites, and all_sprites has those 20 sprites, plus the player sprite. In the main loop, when a collision is detected between the player and anything in ships_list, I understand that the ship sprite that collided with player is removed from ships_list. When I run the program, all sprites appear and by moving the player into a ship sprite it vanishes. All well and good, except.. I don't understand why they are vanishing. The reason being that though I know the ships are removed from ships_list after a collision, It is the all_sprites list that is actually being redrawn each frame, and I haven't explicitly removed anything from it at any point, so is it the case that the collision is also removing the ship sprite from all_sprites?
ship_list = pygame.sprite.Group() # just ship sprites
all_sprites = pygame.sprite.Group() # ship sprites + player sprite
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT or score == 20:
done = True
screen.fill(BLACK)
pos = pygame.mouse.get_pos()
player.rect.x = pos[0]
player.rect.y = pos[1]
**# is this line removing sprites from all_sprites??**
ships_hit_list = pygame.sprite.spritecollide(player, ship_list, True) # detect collisions
for ship in ships_hit_list:
score += 1
print score
all_sprites.draw(screen) # seems to lose sprites when ships_list does..
ship_list.update() # updating the position
pygame.display.flip()
clock.tick(24)
# properly quit
pygame.quit()
From https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollide
pygame.sprite.spritecollide()
Find sprites in a group that intersect another sprite.
spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
Return a list containing all Sprites in a Group that intersect with
another Sprite. Intersection is determined by comparing the
Sprite.rect attribute of each Sprite.
The dokill argument is a bool. If set to True, all Sprites that
collide will be removed from the Group. (It doesn't mention it removing it from any other group..)

If you look at what is shown when a sprite is printed, you will see that it shows in how many groups the sprite exists.
A Sprite has a method called kill:
remove the Sprite from all Groups
kill() -> None
The Sprite is removed from all the Groups that contain it. This won’t
change anything about the state of the Sprite. It is possible to
continue to use the Sprite after this method has been called,
including adding it to Groups.
It seems that what sprite_collide does, it just calls kill() on the sprite if a collision has taken place.

Related

why does this code only register certain collisions and not all?

Hi i have a code for a game where there are multiple fruits falling from the sky and the frog at the bottom has to try and catch them. When he catches one the score goes up. This only happens when the frog collides with some of the fruit and not all of them. And the score randomly starts increasing unstoppably for no reason after a certain point. Here is most of the code as im not sure where the error would be :
import pygame, sys, time, random
from pygame.locals import *
from math import fabs
######### constants ##########
jumpvel=20
fallingspeed=1
running= True
blue= [129,183 ,253]
pink=[255,174,201]
textcolour= [255,255,255]
x=700//2
y=1000//2
score=0
thingylist= ['fruit1.bmp','fruit2.bmp','fruit3.bmp','fruit4.bmp','fruit5.bmp','fruit1.bmp','fruit2.bmp','fruit3.bmp','fruit4.bmp','fruit5.bmp','naughty1.bmp','naughty2.bmp','naughty3.bmp',]
all_things=[]
for i in range (12):
new_thing_image=pygame.image.load(thingylist[(random.randrange(0,12))])
new_thing_image.set_colorkey(pink)
new_thing_rect=new_thing_image.get_rect()
new_thing_rect.x=random.randrange(0,950)
new_thing_rect.y=-random.randrange(50,500)
all_things.append([new_thing_image,new_thing_rect])
def checkCollision (frog_rect,all_things,score):
collides_with=None
for thing_image, thing_rect in all_things:
if frog_rect.colliderect(thing_rect):
collides_with=True
if collides_with == True:
score= score+100
return collides_with,score
######## initialising screen#########
pygame.init()
gamedisplay=pygame.display.set_mode((1000,600)) #making the screen
pygame.display.set_caption('frog')
clock=pygame.time.Clock()# frames per second
bg=pygame.image.load('actual clouds.bmp').convert()
############ initialising sprites##############
frog= pygame.image.load('actual frog.bmp')
frog.set_colorkey(blue)
frog_rect=frog.get_rect()
frog_rect.centerx=(x)
frog_rect.centery=(y)
##########drawing things#############
def drawThings (all_things):
for item in all_things:
new_thing_image, new_thing_rect= item
gamedisplay.blit(new_thing_image, (new_thing_rect.x, new_thing_rect.y))
#########update display function###########
def update(x,y,all_things,score):
gamedisplay.blit(bg,[0,0])
gamedisplay.blit(frog,(x,y))
for thing in range (len(all_things)):
new_thing_rect=all_things[i][1]
#thing_rect.y=thing_rect.y+fallingspeed
new_thing_rect.y+= fallingspeed
drawThings(all_things)
label=font.render("score "+ str(score) ,1,textcolour)
gamedisplay.blit(label,(750,10))
gamedisplay.blit(heart1,(750,50))
gamedisplay.blit(heart2,(850,50))
gamedisplay.blit(heart2,(800,50))
pygame.display.update()
pygame.time.delay(50)
while running == True:
gamedisplay.blit(bg,[0,0])
gamedisplay.blit(frog,(x,y))
drawThings(all_things)
label=font.render("score "+ str(score) ,1,textcolour)
gamedisplay.blit(label,(750,10))
pygame.display.flip()
pygame.event.pump()
key=pygame.key.get_pressed()
for item in all_things:
new_thing_image, new_thing_rect= item
new_thing_rect.y+= fallingspeed
if new_thing_rect.y >450:
new_thing_rect.x=random.randrange(0,950)
new_thing_rect.y=-random.randrange(50,500)
############collision detection##########
detect,score =checkCollision (frog_rect, all_things,score)
update(x,y,all_things,score)
The score should increase every time it collides with any of the falling friuts not just certain ones and not just start increasing randomly non-stop. Any help would be appreciated thankyou !
Based on the code snippet - which does not include the frog-position updating code, I would guess that the problems of:
Score randomly increasing
Collisions not always working
Are caused by the collision rectangle being defined once for the frog's starting position, but then never being updated with changes in the frog's position.
############ initialising sprites##############
frog= pygame.image.load('actual frog.bmp')
frog.set_colorkey(blue)
frog_rect=frog.get_rect()
frog_rect.centerx=(x) # <-- rect only ever updated here!
frog_rect.centery=(y)
This would lead to the described symptoms, since scoring-objects (fruit?) falling through the collision rectangle would add to the score (seemingly randomly), and when the frog was still over it's original position, collision would work perfectly. If the frog was partially over the rect it would somewhat work, and not at all once the frog moved away that start-position rect.
The solution to the problem is to update the co-ordinates of the rectangle frog_rect whenever the x & y of the frog's position is updated. This can be achieved by setting the frog_rect.centerx and frog_rect.centery inside the update() function:
def update(x, y, all_things, score ):
frog_rect.x = x
frog_rect.y = y
The frog's rect is initialised with .centerx, .centery co-ordinates, but the frog is draw at x,y, so the collision rect begins a little off-centre too. Thus it would be best to update the initialisation function as well:
frog_rect=frog.get_rect()
frog_rect.x = x
frog_rect.y = y

VPython 6 - Object won't delete

I have a 3D elastic collision simulation. I'd like to clear all of the sphere objects from my program from a button press. The documentation indicates that I should do the following:
def clear_balls():
for ball in balls:
ball.visible = False
del ball
This successfully makes the balls invisible in the scene, but they still take up memory and collide with balls that still exist. I want it completely removed. Trying this with a unique ball name this isn't part of a list as some have suggested still results in the same issue.
del ball is not doing what you think because balls still holds a reference to the object. You need to empty the balls list:
def clear_balls():
for ball in balls:
ball.visible = False
balls[:] = []

'NoneType' has no attribute 'colliderect'

my first question here and begginer in python
i want to make simple shooter game(Chicken Invaders type). List of rectangles(enemies) and smaller rectangles(ammo) who should 'kill' enemies on collision. I created list of rectangles on random positions on screen.
enemies = []
for i in range(ENEMYCOUNT):
enemySize = random.randint(ENEMYMINSIZE, ENEMYMAXSIZE)
newEnemy = pygame.Rect(random.randint(0, WINDOWWIDTH - enemySize),
random.randint(0, WINDOWWIDTH-200), enemySize, enemySize)
enemies.append(newEnemy)
My 'ammo' are list of invisible rectangles who appear on mouse click and then they move up
ammo = []
for i in range(1, 5):
ammo.append(pygame.Rect(0, 0, 0, 0))
I did for single enemy and single ammo and it is working as intended.
Now i want to make for more enemies and check if any of enemy is hit by ammo
for e in enemies:
for a in ammo:
if e.colliderect(a):
enemies.remove(e)
But i keep getting
if e.colliderect(a):
AttributeError: 'NoneType' object has no attribute 'colliderect'
I am studying from 'inventwithpython' book and copying parts of code that i need but i could not solve this. I cant see the problem since both of my lists are made of Rect objects.
Any help would be appreciated
Problem can be because you remove from list which you use in for statement.
Solution: create new list with not-removed elements and later assign it to enemies
But Pygame has class Group to keep Sprites and
pygame.sprite.spritecollide(sprite, group, dokill, collided = None)
which:
Return a list containing all Sprites in a Group that intersect with another Sprite. Intersection is determined by comparing the Sprite.rect attribute of each Sprite.
The dokill argument is a bool. If set to True, all Sprites that collide will be removed from the Group.

Pygame deleting an object in a list

I just started programming in OOP style last week when deciding to make a small game. But now I seem to be stuck. I'll explain what I have so far:
When the player hits a button, a bullet object is created next to the player and the bullet object is added to the bullets[] list. The bullet then travels horizontally across the screen in the designated direction. If the bullet collides with a player or a wall, it is removed from the bullets[] list. So far, so good.
Now I just cant seem to figure out how to remove the bullet from the bullets[] list when it leaves the screen (screen is defined between 0 and xmax). Also, after I remove the bullet from the list, should I also remove the object itsself, or is this done automatically?
Code so far:
class BULLET(object):
#Constructor for the bullet, bullets are stored into array 'bullets'
# The direction is stored to keep track of which player fired the bullet
def __init__(self,location,direction,color):
self.rect = pg.Rect(location[0],location[1],xmax/160,xmax/160)
self.bullet_type="normal"
self.direction=direction
self.color=color
bullets.append(self)
#Moves the bullet horizontally across the screen, in the specified direction
# The move function also checks for collision with any walls or players
# The move function removes the bullet object from the list and destroys it
# when it leaves the left or right side of the screen
def move(self,bullet_speed):
self.rect.x += bullet_speed
for wall in walls:
if self.rect.colliderect(wall.rect):
index=wall.rect.collidelist(bullets)
del bullets[index]
#Do I need to delete the object too? or just the list item?
for player in players:
if self.rect.colliderect(player.rect):
index=player.rect.collidelist(bullets)
if player.immune_timer <= 0:
del bullets[index]
player.immunity(500)
player.life -= 1
if self.rect.centerx > xmax or self.rect.centerx <0:
#This is where I would like this instance of the bullet object to be deleted
# and to have the object removed from the bullets[] list
What I suggest you do is in your main loop:
bullets = [bullet for bullet in bullets if 0 < bullet.rect.centerx < xmax]
This will only keep the items that should be in the list.

How can I just draw a specific sprite in a Group with pygame draw()?

<Group(4 sprites)>
I have 4 sprites in a group they all ready to draw with the line,
stuff_to_draw.draw(screen)
They all currently draw at the same time.
Is it possible to do something like this
stuff_to_draw[0].draw(screen)
I've looked at documentation for pygame Groups, but can not find any helpful information about selecting a specific element in the group.
What am I trying to achieve?
The sprite group currently holds a answer sprite which consists of a location (x,y) and a image of the letter to blit.
Currently across all maps the answer is blitted instead of on the current question. So I would like to be able to say on question one stuff_to_draw[0] then question two stuff_to_draw[1].
First, add a self.type to your each of your sprites. Give it a value, such as self.type = 1. Then call this when you want to draw a certain sprite or group of sprites:
for sprite in group: #Replace group with your sprite group
if sprite.type == 1: #Or whatever type you want
sprite.draw(screen)
elif sprite.type == 2:
sprite.draw(screen)
#Etc...
Next, in your sprite class make sure you inherit from pygame.sprite.Sprite:
class my_sprite_class(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__()
#Etc...
#Add this draw function so we can draw individual sprites
def draw(self, screen):
screen.blit(self.image, self.rect)
I forgot that individual sprites don't have a draw function, just the groups. #pmoleri #jackdh my bad
Please read this post here to get an idea of what the class can do.
If you have any more questions just comment below.
You could blit any individual sprite, but I don't recommend it. This would be the way:
sprite = stuff_to_draw.sprites()[0]
screen.blit(sprite.image, sprite.rect)
My recommendation is to keep in the Sprites Group what you want to draw at a moment, and keep the rest in a separate list or group.

Categories

Resources