ValueError: <__main__.projectile object at 0x0000024699C2C670> is not in list - pygame

I've been trying to create a game with PyGame but sometimes it crashes due to this error:
ValueError: <main.projectile object at 0x0000024699C2C670> is not in list
Do you know any possible fixes?
As I've seen from other people it might be that I am modifying the bullets list while I'm iterating it but i can't find a solution even reading to this thread.
PS. I know that I can use a function to spawn the goblins and not just copy and paste but this is a quick project that I am trying to get over. This is the code:
def enemy():
def __init__(self, x, y, width, height, end):
self.x = x
self.y = y
self.width = width
self.height = height
self.end = end
self.path = [self.x, self.end]
self.walkCount = 0
self.vel = 3
self.hitbox = (self.x + 17, self.y + 2, 31, 57)
self.health = 10
self.visible = True
bullets = []
goblin = enemy(100, 410, 64, 64, 450)
# mainloop
while run:
if not goblin.visible:
goblin.hitbox = 0
for bullet in bullets:
if 0 < bullet.x < 500:
bullet.x += bullet.vel
else:
if len(bullets) != 0:
bullets.pop(bullets.index(bullet))

The problem here seems to be that you iterate through the bullets in a for loop, and remove a bullet halfway through. The following line
for bullet in bullets:
will, in practice, go through the bullets 1 by 1 from the first to the last one, by index. That is, if your list is bullets = [bullet1, bullet2, bullet3] it will execute for bullets[0], then for bullets[1] and lastly for bullets[2]. The problem here appears when you remove a bullet from the list. Say bullet2 (which is bullets[1] gets removed in your loop. Now index will still jump up to 2, and bullets[2] will be accessed. However, there are now only two bullets, bullets = [bullet1, bullet3], and an error occurs.
If, on the other hand, bullet1 gets removed, bullets is now bullets = [bullet2, bullet3]. The loop continues to index 1, but this skips bullet2 altogether.
This is a very common problem in many programming languages. In many languages iterating the list from the last item to the first item resolves this, but this is not straightforward to do in python.
Have a look at this StackOverflow answer to get a better idea of your options.
A solution
One way to resolve the issue, which is not the most efficient but relatively easy to understand, is to construct a new list of bullets, while evaluating the bullets. Instead of removing bullets you'll be adding all bullets that persist in the new bullet list.
newBullets = []
for bullet in bullets:
if 0 < bullet.x < 500:
bullet.x += bullet.vel
# append bullet to keep it
newBullets.append(bullet)
# else don't do anything
# after the loop, update the bullets list
# which now contains only bullets that have an x between 0 and 500
bullets = newBullets

Related

How to set the position of an image in pygame?

I created an object called "alien" in my game. Uploaded the image for "alien" and used "get_rect()" to set its rect attributes.
Now I want to change the x-coordinate value of "alien". Which of the following two ways is correct?
alien.x = ...... or alien.rect.x = ......
I saw in a textbook that the following codes are used:
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
Why didn't the author directly use alien.rect.x to change the x-coordinate value of "alien"? Like:
alien.rect.x = alien_width + 2 * alien_width * alien_number
Why does there have to be alien.x?
Unfortunately the answer is "it depends". Some people's code maintains the position of the object at an internal x and y, using a rect to handle collisions. Other code just maintains the rect, using the rect.x and rect.y if a single position is (ever) needed.
It's up to you really, but my advice is to keep it all inside a PyGame Rect, as this has benefits of easy collision detection, should you wish to use that down the track.
class Alien:
def __init__( self, x, y, image ):
self.image = image
self.rect = image.get_rect() # copy the image dimensions
self.rect.x = x
self.rect.y = y # move to location
def draw( self, window ):
window.blit( self.image, self.rect ) # paint it
When it's time to move the Alien, you can just as easy adjust the rectangle as an x and y
class Alien:
...
def moveBy( self, by_x, by_y ):
self.rect.move_ip( by_x, by_y )
def moveTo( self, x, y ):
self.rect.x = x
self.rect.y = y
Perhaps the author thought that having a separate x and y made the code easier to understand. This is a paramount reason that effects programming style. Program code is read many times more often than it is written, so often extra variables are included to illustrate the program flow better.
For example, like checking a mouse-click-event:
for event in pygame.event.get( ):
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONUP:
handleGameClick( mouse2Board( event.pos ) ) # <-- HERE
Adding extra variables documents what's going on:
elif event.type == pygame.MOUSEBUTTONUP:
mouse_click_coord = event.pos
game_board_coord = mouse2Board( mouse_click_coord )
handleGameClick( game_board_coord )
Here it tells the reader that event.pos is a co-ordinate (so probably a pair of values), and is from the mouse. Then it reenforces that the co-ordinate is then converted into a game-board space before being passed out to handleGameClick() for processing.
The two pieces of code have exactly the same outcome (and probably execution speed), but the second is much easier to follow.
IMHO one should ideally write code such that someone unfamiliar with the language (but still a programmer) can understand it without too much trouble. This is why in my answers you wont see much "pythonic" loop-in-list creation like:
[[col +1 for col in row] for row in a] # Taken from 10 vote answer
Because unless your very familiar with python syntax, it's unreadable.

Previous sprites staying when changing y_positions to move

I'm making a game where the player controls a sprite that catches falling food.
However, I'm having trouble with the falling food part.
import pygame
import random
import time
d_w = 800
d_h = 600
fall_speed = 5
screen = pygame.display.set_mode((d_w, d_h))
clock = pygame.time.Clock()
BGCOLOR = (0, 155, 0)
goodFood = pygame.image.load('burg.png')
def randFoodGen():
x_food = d_w/2
y_food = 0
return x_food,y_food
def BurgGen():
x_food, y_food = randFoodGen()
screen.fill(BGCOLOR)
while True:
y_food = y_food + fall_speed
if y_food > d_w:
x_food = (random.randrange(0, d_w))
y_food = -30
screen.blit(goodFood, [x_food, y_food])
pygame.display.update()
clock.tick(30)
BurgGen()
It ends up keeping the previous position of the falling food sprite, like this:
Anger
Your screen.fill(BGCOLOR) needs to be within the while loop.
def BurgGen():
x_food, y_food = randFoodGen()
while True:
screen.fill(BGCOLOR)
y_food = y_food + fall_speed
if y_food > d_w:
x_food = (random.randrange(0, d_w))
y_food = -30
screen.blit(goodFood, [x_food, y_food])
pygame.display.update()
clock.tick(30)
That will at least solve the persisting burger problem. The reason is that before you draw a new burger, you have to wipe the screen of the burger you drew previously.
Also, if you are going to have multiple falling objects, you might want to make a pygame.Rect to represent each falling object. You might also want to make a python list of those pygame.Rect objects.
falling_objects = [] # create empty list
Whenever you need to create a new falling object, create a pygame.Rect and .append it to the list.
new_object = pygame.Rect(x, y, width, height)
falling_objects.append(new_object)
Then, to update the objects positions, you loop through the list and update the Rects
for obj_rect in falling_objects:
obj_rect.move_ip(0, fall_speed)
Use a similar for-loop to blit the image at each location
for obj_rect in falling_objects:
screen.blit(food_image, obj_rect.top_left)
A bit in-depth but I hope that helps a bit.

How to create a delete function for deleting squares in pygame

I am trying to implement the diamond dash game using pygame. To be more specific, when you clicking with mouse on 3 squares with same colour in a row or column, then these squares must deleted and after new squares must take their position, randomly. My program can find the specific coordinates of a square but i am struggle on how to delete those specific squares.
Can you help me please? thank you.
import random, time, pygame, sys, copy
from pygame.locals import *
black = ( 0, 0, 0)
white = ( 255, 255, 255)
green = ( 0, 255, 0)
red = ( 255, 0, 0)
size = [700, 485]
screen=pygame.display.set_mode(size)
# This sets the width and height of each grid location
width = 64
height = 64
# This sets the margin between each cell
margin = 5
# Create a 2 dimensional array. A two dimesional
# array is simply a list of lists.
grid = []
for row in range(7):
# Add an empty array that will hold each cell
# in this row
grid.append([])
for column in range(80):
grid[row].append(0) # Append a cell
imgnum = 7
imglist = []
for i in range(1, imgnum+1):
dimge = pygame.image.load('imge%s.png' % i)
imglist.append(dimge)
grid[1][5] = 1
pygame.init()
pygame.display.set_caption("dimond dash")
done = False
for row in range(7):
for column in range(8):
screen.blit(random.choice(imglist), [(margin+width)*column+margin,
(margin+height)*row+margin,
width,
height])
while done == False:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True # Flag that we are done so we exit this loop
elif event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
column = pos[0] // (width + margin)
row = pos[1] // (height + margin)
grid[row][column] = 1
print("Click ", pos, "Grid coordinates: ", row, column)
pygame.display.flip()
pygame.quit()
You need some sort of a data structure to store which cells will be deleted.
You can try this:
Generate your grid.
For each cell that is not in a group:
i. create a new group
ii. get the color of the cell
iii. if any of the neighbors have the same color, repeat the algorithm, but add the new cell to the group.
At the end you will have groups of cells.
Now when a player clicks on a cell, you look up the group list, and find the group that has been clicked. Don't know the rules, but here you can check if the group is big enough, or simply generate new cells in their places.
Now for each new cell, do a similar algorithm:
Check all neighbors - There are two possibilities.
i. If only one neighbor is the same color, add the new color to the neighbors
group.
ii. If there are more, check if they both are in the same
group, if so, add this cell, if not, you need to merge both groups and
add the cell.
EDIT:
There are 2 possibilities for getting the color
you images are one color - you can get the color at a point:
link
If you store your cells as sprites and checking the sprite image would tell you if they are the same color.
Actually now that I see it, your bliting loop makes you lose information about the cells.
It would be better if you store the results of random choice, and then blit them. This way you will know which cell is which.

Random Number Functions Python

I am making a little game where when events happen, rectangles get spawned at a random x and y point and I am having some trouble implementing functions into this. Here is some basic code:
xran = random.randint(5, 485)
yran = random.randint(5, 485)
xran1 = random.randint(5, 450)
yran1 = random.randint(5, 400)
def allRand():
#This REGENERATES those randoms making it 'spawn' in a new location.
xran = random.randint(0, 485)
yran = random.randint(0, 485)
xran1 = random.randint(5, 450)
yran1 = random.randint(5, 400)
char = pygame.draw.rect(screen, black, (x,y,15,15), 0)
food = pygame.draw.rect(screen, green, (xran,yran,10,10), 0)
badGuy = pygame.draw.rect(screen, red, (xran1,yran1,50,100), 0)
if char.colliderect(food):
score += 1
print "Your score is:",score
allRand()
Does calling a function that regenerates random numbers work for any of you? I know it regenerates them because I have had it print back the variables, for some reason my rects don't do there though.
Note: This is just snippet of my code it was just meant to give an idea of what I am trying to do.
Thanks!
You need to declare xran, etc. with global inside the allRand() function. Otherwise, it's just creating new variables inside function scope, assigning them random values, then throwing them away when the function returns.
Your allRand() method doesn't have any code. You must indent the lines you want in that function.
It's kinda working because you still call those statements below your def. But it's not because you're calling the function.
To add to Lee Daniel Crocker's answer, when you create variables in a function they exist only in that function. If you want them to exist outside you can either make them global variables as he suggested, you are can return them and catch them:
>>> def square(number):
squared = number*number
return squared
>>> square(3)
9
>>> ninesquared = square(3)
>>> ninesquared
9
>>>
Read more
It looks like you need to master your basics. I suggest doing that first before trying things in pygame.
EDIT:
If you define variables outside of the function, they will not effect any variables you define in the function either.
>>> x = 5
>>> def rais():
x = 10
>>> x
5
>>> rais()
>>> x
5
>>>
Notice how rais did nothing?
If we change the line in rais to be x = x + 1 then python will give us back an error that x is not defined.
If you want you're variables to get into the function you need to pass them in as parameters, but once again, they won't effect anything outside of the function unless you return and capture them. And once again, you can declare them as global variables and that will also work.

Pygame - Limiting instances of sprites

I'm using an example regarding sprite generation for a space scroller shootup that I'm developing. By slowly trying to understand how it works, I've managed to get multiple sprites to transverse across the screen. However, there are many sprites that are generated.
So what I'm having trouble with is limiting the initial number of sprites instead of the multitude that the code produces. I thought of using if sprites.__len__ < 10: sprites.add(drone) but when I tried that, it didn't work.
My thinking was that each time it looped, it would check the number of sprites in the group and if it was less then 10, add a sprite to the group until it hit 10. That way if it went off screen or is destroyed, then it would keep doing the check and keeping it constant.
This is the player class:
class Player(pygame.sprite.Sprite):
def __init__(self, *groups):
super(Player, self).__init__(*groups)
self.image = pygame.image.load('player.png')
self.rect = pygame.rect.Rect((screen_width, (random.randrange(0,screen_height))), self.image.get_size())
self.dx = -10
self.pos = random.randrange(0,screen_height)
def update(self):
self.rect.centerx += self.dx
if self.rect.right < 0:
self.kill()
and this is the section regarding the adding of the sprite.
sprites.update()
screen.fill((200, 200, 200))
sprites.draw(screen)
drone = Player()
self.y = random.randrange(0,screen_height)
sprites.add(drone)
pygame.display.flip()
It's probably obvious, but I'm still learning so guidance would be great.
Second question - More of a confirmation of thought. If I don't want the sprite to be half drawn on the bottom of the screen. Do I basically say that if self.rect.bottom > screen_height, then position the sprite # screen_height
Full source: http://pastebin.com/PLRVHtxz
EDIT - I think I've solved it, just need to make the sprites run smoother.
while 1:
clock.tick(40)
numberAlien = 5
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
return
sprites.update()
screen.fill((200, 200, 200))
sprites.draw(screen)
drone = Player()
if sprites.__len__() < numberAlien:
self.y = random.randrange(0,screen_height)
sprites.add(drone)
pygame.display.flip()
You could subclass SpriteGroup, add a new field of the total number of sprites, and check in the add method to see if it can be added.
You shouldn't test check any variables with __.
As for the movement, i believe, you do not see a smooth movement because of clock.tick(40).
It waits for 40ms until it resumes running. You could reduce tick to 10, and tune the dx you change for the sprites, or try a more universal approach.
A call to clock.tick() returns amount of ms since the last call. This will be your time delta. You can then have a global SPEED. The amount of pixels to be moved would be calculated from SPEED * delta.