Reputation: 39
I'm making a game where you throw snowballs at snowmen. Here are the definitions for the Snowman
class and Snowball
class:
class Snowman:
def __init__(self,start,end,a,b):
self.x = random.randrange(0,310)
self.y = 0
self.r = pygame.Rect(self.x,self.y,a,b)
class Snowball:
def __init__(self,a,b):
self.x = mousex
self.y = mousey
self.r = pygame.Rect(self.x,self.y,a,b)
As you can see, the r
attribute for the classes is their rectangle.
Below is a script that controls the snowman AI.
for s in snowmen:
s.y += 0.5
#above_makes_the_snowmen_approach_the_bottom_of_the_screen.
#below_is_supposed_to_check_for_snowball_collision.
for b in bullets:
if s.r.colliderect(b.r):
snowmen.remove(s)
bullets.remove(b)
print(b.r.colliderect(s.r))
#above_is_a_debugger_that_reports_the_collision.
if s.y < 640:
blit(asnowman,(50,78),(s.x,s.y))
else:
snowmen.remove(s)
The problem is when I throw a snowball at one of the snowmen, the debugger reports False
and the snowman doesn't die. How do I fix this?
Upvotes: 1
Views: 98
Reputation: 211136
See How do I detect collision in pygame?. You need to update the position of the rectangles before the collision test:
s.r.topleft = round(s.x), round(s.y)
b.r.topleft = round(b.x), round(b.y)
if s.r.colliderect(b.r):
Note, you cannot get rid of the x
and y
attribute, because pygame.Rect
is supposed to represent an area on the screen and a pygame.Rect
object can only store integral data.
The coordinates for Rect objects are all integers. [...]
Also read How to remove items from a list while iterating?. A possible solution could be to iterate through copies of the list:
for s in snowmen[:]:
s.y += 0.5
s.r.topleft = round(s.x), round(s.y)
for b in bullets[:]:
b.r.topleft = round(b.x), round(b.y)
if s.r.colliderect(b.r):
snowmen.remove(s)
bullets.remove(b)
break
Upvotes: 2
Reputation: 104762
While you initialize the Rect
object in each of your classes to match the x
and y
attributes when the object is created, they do not remain in sync. That means that you only ever collide the rectangles at their initial positions, not where the objects have moved to later on.
The easiest way to solve this is probably to add a few lines of code to update s.r
and/or b.r
, but you could also start using pygame.sprite.Sprite
, which will update its rect
attribute to match its x
and y
attributes (and vice versa, I think). If you wanted to make your own classes behave a little bit like a Sprite
without going all the way, I'd suggest making x
and y
into property
descriptors, which reference the Rect
, which becomes the single source of truth:
class Snowman:
def __init__(self,start,end,a,b):
self.r = pygame.Rect(random.randrange(0,310),0,a,b)
@property
def x(self):
return self.r.left
@x.setter
def x(self, value):
self.r.left = value
@property
def y(self):
return self.r.top
@y.setter
def y(self, value):
self.r.top = value
Upvotes: 2