list_zombies = pygame.sprite.Group()
walls = pygame.sprite.Group()
class Zombie(pygame.sprite.Sprite):
def __init__(self, x, y, speed):
pygame.sprite.Sprite.__init__(self, list_zombies)
self.x = x
self.y = y
self.speed = zombie_speed
self.image = pygame.image.load("Zombie.png").convert()
self.rect = self.image.get_rect()
self.pos1 = self.x
self.pos2 = self.y
def update(self):
self.y += zombie_speed
def main(self, display):
self.update()
display.blit(zombie_image_copy, (self.x, self.y))
class Wall(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self, walls)
self.x = x
self.y = y
self.image = wall_image.convert()
self.rect = self.image.get_rect()
def main(display):
display.blit(wall_image, (-100, 950))
main loop:
for zombie in list_zombies:
zombie.rect.topleft = (zombie.x, zombie.y)
if pygame.sprite.groupcollide(list_zombies, walls, 0, 0, collided= None):
zombie.kill()
These are my classes and i want to kill the zombie if it hits the wall. I have used groupcollide since spritecollide wouldn't work and I would get the error that Wall doesn't have the attribute rect. Somehow it is not working but also not giving me an error. Maybe you can help me.
See How do I detect collision in pygame? and read the documentation! pygame.sprite.Group.draw() and pygame.sprite.Group.update() are methods which are provided by pygame.sprite.Group.
The latter delegates to the update method of the contained pygame.sprite.Sprites — you have to implement the method. See pygame.sprite.Group.update():
Calls the update() method on all Sprites in the Group. [...]
The former uses the image and rect attributes of the contained pygame.sprite.Sprites to draw the objects — you have to ensure that the pygame.sprite.Sprites have the required attributes. See pygame.sprite.Group.draw():
Draws the contained Sprites to the Surface argument. This uses the Sprite.image attribute for the source surface, and Sprite.rect. [...]
The rect attribute is also used for collision detection functions like pygame.sprite.groupcollide. You don't need the .x and .y attributes at all. If you use the .rect attribute (.rect.x and .rect.y) insterad, collision detection works immediately.
If you want to store sprite positions with floating point accuracy, you have to store the location of the object in separate attributes. Synchronize the pygame.Rect object when the position of the object is changed in the update method:
def update(self):
self.x = # change
self.y = # change
self.rect.topleft = round(self.x), round(self.y)
Related
I'm trying to make a typical platformer using pygame but I'm not exactly sure how I should go about doing a death sequence. I want it so after time the player dies every sprite's position is reset to its original position if they moved at all.
I tried experimenting with the pygame.sprite.Group().copy() but I don't know how to use it or if it even applies to my situation.
I would store a copy of the original position in the sprite, and then as #furas suggests re-position and re-stat the sprite via a reset() function.
For example:
class ResettableSprite( pygame.sprite.Sprite ):
MAX_HEALTH = 100
def __init__( self, image, x, y ):
pygame.sprite.Sprite.__init__( self )
self.image = image
self.rect = self.image.get_rect()
self.start_x = x
self.start_y = y
self.reset()
def reset( self ):
self.rect.x = self.start_x
self.rect.y = self.start_y
self.health = ResettableSprite.MAX_HEALTH
# ... etc.
The collision works when I use:
pygame.sprite.collide_rect
but it doesnt work when I use:
pygame.sprite.collide_mask
Here are my 2 classes:
class Ball():
def __init__(self):
self.rect = balltexture.get_rect()
self.rect.x=225
self.rect.y=400
self.radius = 100
self.velx=0
self.vely=0
self.action=False
self.switchx=0
self.switchy=0
pygame.sprite.Sprite.__init__(self)
self.mask = pygame.mask.from_surface(balltexture)
def draw(self):
win.blit(balltexture, self.rect)
class rock():
def __init__(self, y):
self.rect = rocktexture.get_rect()
self.rect.x = screensize.current_w
self.rect.y = y
self.width = 120
pygame.sprite.Sprite.__init__(self)
self.mask = pygame.mask.from_surface(rocktexture)
def draw(self, win):
win.blit(rocktexture, self.rect)
and here is the collision check
for i in rocks:
if pygame.sprite.collide_rect(i, Player):
print(1)
if pygame.sprite.collide_mask(i, Player):
print(2)
So when they collide it prints 1 all the time.
BTW I'm moving rect coordinates (e.g. Player.rect.x) to move the Player/rocks and I also tried the mask overlap method but it didn't work either
offset = (Player.rect.x-i.rect.x, Player.rect.y-i.rect.y)
if i.mask.overlap(Player.mask, offset):
print(2)
Not quite sure what's going on, but here's some things to try:
You can set the mask to all 1s with self.mask.fill(). At this point, the mask collision should be identical to the rect-based collision.
You can call print(self.mask.count()), which will show the number of set pixels. Make sure it's not 0 or something.
You can call i.mask.overlap(Player.mask (0, 0)), and confirm that those overlap when they have the same upper-left corner.
This is either going to be an issue with the mask creation, or the offsets are wrong in some way; this should narrow down the issue.
One of my images had no transparency, that was the problem
I have an image in pygame with multiple instances of the image called in a for loop. is there a way I could move each instance of the image independently without moving the others with the code as is? or will I have to load separate instances of the image individually?
def pawn(self):
y_pos = 100
self.image = pygame.transform.scale(pygame.image.load('pawn.png'), (100,100))
for x_pos in range(0,8,1):
pieceNum = x_pos
screen.blit(self.image, (x_pos*100, y_pos))
I recommend to use pygame.sprite.Sprite and pygame.sprite.Group:
Create a class derived from pygame.sprite.Sprite:
class MySprite(pygame.sprite.Sprite):
def __init__(self, image, pos_x, pos_y):
super().__init__()
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = (pos_x, pos_y)
Load the image
image = pygame.transform.scale(pygame.image.load('pawn.png'), (100,100))
Create a list of sprites
imageList = [MySprite(image, x_pos*100, 100) for x_pos in range(0,8,1)]
and create a sprite group:
group = pygame.sprite.Group(imageList)
The sprites of a group can be drawn by .draw (screen is the surface created by pygame.display.set_mode()):
group.draw(screen)
The position of the sprite can be changed by changing the position of the .rect property (see pygame.Rect).
e.g.
imageList[0].rect = imageList[0].rect.move(move_x, move_y)
Of course, the movement can be done in a method of class MySprite:
e.g.
class MySprite(pygame.sprite.Sprite):
# [...]
def move(self, move_x, move_y):
self.rect = self.rect.move(move_x, move_y)
imageList[1].move(0, 100)
I have an image in pygame with multiple instances of the image called in a for loop. is there a way I could move each instance of the image independently without moving the others with the code as is? or will I have to load separate instances of the image individually?
def pawn(self):
y_pos = 100
self.image = pygame.transform.scale(pygame.image.load('pawn.png'), (100,100))
for x_pos in range(0,8,1):
pieceNum = x_pos
screen.blit(self.image, (x_pos*100, y_pos))
I recommend to use pygame.sprite.Sprite and pygame.sprite.Group:
Create a class derived from pygame.sprite.Sprite:
class MySprite(pygame.sprite.Sprite):
def __init__(self, image, pos_x, pos_y):
super().__init__()
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = (pos_x, pos_y)
Load the image
image = pygame.transform.scale(pygame.image.load('pawn.png'), (100,100))
Create a list of sprites
imageList = [MySprite(image, x_pos*100, 100) for x_pos in range(0,8,1)]
and create a sprite group:
group = pygame.sprite.Group(imageList)
The sprites of a group can be drawn by .draw (screen is the surface created by pygame.display.set_mode()):
group.draw(screen)
The position of the sprite can be changed by changing the position of the .rect property (see pygame.Rect).
e.g.
imageList[0].rect = imageList[0].rect.move(move_x, move_y)
Of course, the movement can be done in a method of class MySprite:
e.g.
class MySprite(pygame.sprite.Sprite):
# [...]
def move(self, move_x, move_y):
self.rect = self.rect.move(move_x, move_y)
imageList[1].move(0, 100)
Specifically, I would like each sprite in sg_fireball to have 'bounces', without giving 'bounces' to every sprite from Spell(). Is there a clean way to do this without making 'bounces' an argument of Spell(), or looping through sg_fireball?
The relevant code snippets:
self.sg_fireball = pygame.sprite.Group()
self.sg_fireball.speed = 6.0
self.sg_fireball.image = pygame.image.load("fireball.png")
self.sg_fireball.bounces = 1
if event.type == pygame.MOUSEBUTTONDOWN:
self.character.cast(self.sg_fireball)
def cast(self, sg):
sg.add(Spell(self.rect.center, sg.speed, self.dir, sg.image))
class Spell(pygame.sprite.Sprite):
def __init__(self,pos, speed, direction, img, bounces):
pygame.sprite.Sprite.__init__(self)
self.bounces = bounces
self.image = img
self.rect = pygame.Rect(pos, (8,8))
self.posx = self.rect.x
self.posy = self.rect.y
self.speed = speed
self.dir = direction
self.velx = self.speed*math.cos(self.dir)
self.vely = self.speed*math.sin(self.dir)
If I understood correctly, you wish some of the sprites to have a certain attribute, while others won't. This is a perfect example of polimorphism and inheritance.
This is one of the options that you can do:
You subclass a normal spell as a bouncy spell. You can then have another update function where you will take care of bouncing. You can normally add a BouncySpell in the same sprite group as the NormalSpell.