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
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[:] = []
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.
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.
<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.