Pygame deleting an object in a list - python

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.

Related

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: Sorting sprites in group based upon position on screen

I'm currently creating a space invaders game using Pygame.
I create the Alien enemies and add them to a group called "aliens".
I have multiple rows of invaders, created using the following code:
yPos = self.top_boundary
for r in range(self.total_rows):
xPos = self.left_boundary
for i in range(self.invaders_per_row):
self.alien = Invader(xPos, yPos)
self.aliens.add(self.alien)
xPos += 32
yPos += 40
Now, in order to reverse the direction of the invaders when they're on screen, I need to determine which invader is the "left most" invader and check if it has reached the left hand side of the screen.
My problem is, they're not stored in the group in any particular order. How can I determine which invader has the left most position?
An alternative method I could use would be to use lists to store the objects instead. Then I could easily iterate through the lists to determine which invader is the left most. Would this be a better option?
You can use the OrderedUpdates class which maintains the order in which the Sprites were added to the Group for rendering.
Assuming you have a collection of Invader objects which have an attribute for their x-Position, you can just use min to find the one with the smallest x-Position. Minimal example:
class Invader(object):
def __init__(self, xpos, ypos):
self.xpos = xpos
self.ypos = ypos
def __str__(self):
return str((self.xpos, self.ypos))
invaders = [Invader(3, 2), Invader(4, 9), Invader(0, 100)]
print(min(invaders, key=lambda x: x.xpos)) # prints '(0, 100)'
Having a collection of all your invaders seems natural to me for a game like this.

If player comes within a radius of enemy,change state

So I've been teaching myself some pygame for the past few months, doing a spaceship game.
I have multiple enemy spaceships that guard a certain area. If the player comes within a certain radius of them, I want to change the enemy's state. What's the best way to do this for a beginner, without using vectors?
You can create a method in your program called is_close and then call it for each player and enemy comparison. It would look something like this:
def is_close(object1, object2, distance):
return math.hypot(object2.x-object1.x, object2.y-object1.y) < float(distance)
... #rest of your code
while True: #This is your main while loop
... #rest of your code
for enemy in enemies:
if is_close(player, enemy, 25):
enemy.state = new_state #change state, this may simply be a color change

spritecollide and killing sprites

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.

Categories

Resources