how does collide.rect work in pygame - python

I'm having some trouble understanding how colliderect works with sprites. I have a good idea about it, but whenever I try to implement it into my game, I just get the error message "attributeError:'pygame.surface' object has no attribute 'rect'"
ufo_lvl_1 = pygame.image.load("ufo1.png")
bullet = pygame.image.load("bullet.png")
class Bullet(pygame.sprite.Sprite):
def __init__(self):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
self.image = bullet
self.damage = 5
self.rect = self.image.get_rect()
def update(self):
if 1 == 1:
self.rect.x += 15
if self.rect.x >1360:
self.kill()
class ufo1(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = ufo_lvl_1
self.health = 10
self.rect = self.image.get_rect()
def update(self):
if 1==1:
self.rect.x -= 10
if self.rect.colliderect(bullet.rect):
self.health -= bullet.damage
if self.health >= 0:
self.kill()
bullet.kill()
Basically all my sprites work (excluding ufo1), but the moment I create a ufo1 sprite it crashes and I don't know how to fix it.
Thanks in advance.

you called your class Bullet and your surface for that class bullet. When you are calling colliderect you are having it check bullet instead of Bullet or whatever you named the object of class Bullet

Related

Play animation upon collision with enemy

so I have loaded the images into pygame:
Explosion = [pygame.image.load('Explosion1.png'), pygame.image.load('Explosion2.png'), pygame.image.load('Explosion3.png'), pygame.image.load('Explosion4.png'), pygame.image.load('Explosion5.png'), pygame.image.load('Explosion6.png'), pygame.image.load('Explosion7.png'), pygame.image.load('Explosion8.png'), pygame.image.load('Explosion9.png'), pygame.image.load('Explosion10.png')]
and I would like, when the bullet, which is a separate class, makes a collision with the missile, it plays this animation at the position where both the bullet and the enemy collide, I'm not sure how I go around doing this?
Collision Script (In the main loop):
hits = pygame.sprite.groupcollide(enemies, bullets, True, True)
Bullet Class:
class Bullet (pygame.sprite.Sprite):
def __init__ (self, x, y):
super (Bullet, self).__init__()
self.surf = pygame.image.load("Bullet.png").convert()
self.surf.set_colorkey((255,255,255), RLEACCEL)
self.rect = self.surf.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedx = bullet_speed
def update(self):
self.rect.x += self.speedx
if self.rect.left > SCREEN_WIDTH:
self.kill()
Enemy Class:
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super(Enemy, self).__init__()
self.surf = pygame.image.load("Missiles.png").convert()
self.surf.set_colorkey((255,255,255), RLEACCEL)
self.rect = self.surf.get_rect(
center=(
random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
random.randint(0, SCREEN_HEIGHT),
)
)
self.speed = random.randint(Enemy_SPEED_Min, Enemy_SPEED_Max)
def update(self):
self.rect.move_ip(-self.speed, 0)
if self.rect.right < 0:
self.kill()
all of the code is here https://pastebin.com/CG2C6Bkc if you need it!
thank you in advance!
Do not destroy the enemies, when it collides with an bullet. Iterate through the enemies and which are returned in hits and start the explosion animation instead of killing the enemies:
hits = pygame.sprite.groupcollide(enemies, bullets, True, True)
hits = pygame.sprite.groupcollide(enemies, bullets, False, True)
for enemy in hits:
enemy.start_animation()
Add the attributes animation_count, animation_frames and animation to th class Enemy. When the explosion animation is started then animation is set True. animation_frames controls the speed of the animation. If the animation is started, then the enemy stops to move and the Explosion image is shown instead of the enemy. After the last image of the animation is shown, the enemy is killed:
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super(Enemy, self).__init__()
self.surf = pygame.image.load("Missiles.png").convert()
self.surf.set_colorkey((255,255,255), RLEACCEL)
self.rect = self.surf.get_rect(
center=(
random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
random.randint(0, SCREEN_HEIGHT),
)
)
self.speed = random.randint(Enemy_SPEED_Min, Enemy_SPEED_Max)
self.animation_count = 0
self.animation_frames = 10
self.animation = False
def start_animation(self):
self.animation = True
def update(self):
if self.animation:
image_index = self.animation_count // self.animation_frames
self.animation_count += 1
if image_index < len(Explosion):
self.surf = Explosion[image_index]
self.rect = self.surf.get_rect(center = self.rect.center)
else:
self.kill()
self.rect.move_ip(-self.speed, 0)
if self.rect.right < 0:
self.kill()
Well i dont want to do it for you, But i will say one way you could do it, then you can go and try to do it, and if you run into a problem/error, you can update the question and we can help you.
I would create another class called Explosion and and give it the images, when when you draw it, draw the first image, then change to the second, then draw that one, then change...
Then after it finishes, destroy the object.
So, create the class, create a spritegroup, when the bullet collides, create a new instance of the class, giving it the x and y co ordinates of the collision, update it every frame, and it will draw all the images then destroy itself
also, an easier way to get all of your images instead of typing them all out is
Explosion_imgs = [pygame.image.load("explosion" + str(x) + ".png") for x in range(1,11,1)]

Pygame: How to call defined variable that is inside a class in my main game loop?

So, i'm making some stupid platform game, and i'm currently working with a projectile. Right now everything works fine, but I don't know how to make the projectile move left, I can only move it right. I have a variable called 'Bullet' that is inside a class, but I don't know how to call it in the main loop. When ever I try to call it, its not defined. Heres my code for the bullet:
def shoot(self):
if self.ammo > 0:
Bullet = bullet(self.Game,self.rect.x,self.rect.top)
self.Game.all_sprites.add(Bullet)
self.Game.bullets.add(Bullet)
self.ammo -= 1
Heres the bullet class
class bullet(pg.sprite.Sprite):
def __init__(self, Game,x,y):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load('magicBullet.png')
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.Game = Game
self.Player = Player
self.speed = 10
def update(self):
self.rect.x += self.speed
Can anyone help?
Why start the variable name with a capital letter and the class name is lowercase? It should be the other way around. See Style Guide for Python Code - Class Names
Anyway, you have to define the direction of movement when the bullet spawns.
Add an additional argument for the speed to the constructor of the class bullet:
class bullet(pg.sprite.Sprite):
def __init__(self, Game, x, y, speed):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load('magicBullet.png')
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.Game = Game
self.Player = Player
self.speed = speed
def update(self):
self.rect.x += self.speed
Add an argument which defines the direction to the method shoot. The accepted values for the argument direction are either -1 for the movement to the left or 1 for the movement to the right:
def shoot(self, direction):
if self.ammo > 0:
speed = 10 * direction
Bullet = bullet(self.Game, self.rect.x, self.rect.top, speed)
self.Game.all_sprites.add(Bullet)
self.Game.bullets.add(Bullet)
self.ammo -= 1

Collision detection in Pygame between Player and sprite group mudballGroup

I am following along with a tutorial on how to detect collisions between the class Player and a sprite group MudballGroup. When setting up the collision in the statement pg.sprite.spritecollide(Player, mudballGroup, False) I get the error type object 'Player' has no attribute 'rect'. I have code from my Player sprite here that shows rect to be defined in the following statement: self.rect = self.image.get_rect(). I don't know why I am getting this error. Please if someone could help.
class Player(pg.sprite.Sprite):
def __init__(self, game):
pg.sprite.Sprite.__init__(self)
self.game = game
self.walking = False
self.jumping = False
self.current_frame = 0
self.last_update = 0
self.load_images()
self.image = self.girl_standing[0]
#Isn't this my rect attribute for Player?
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.vel = vec(0, 0)
self.acc = vec(0, 0)
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
self.clock = pg.time.Clock()
def shoot(self):
mudball = MUDBALL(self.rect.centerx, self.rect.centery)
print("SHOOT function")
self.game.all_sprites.add(mudball)
mudballGroup = pg.sprite.Group()
mudballGroup.add(mudball)
# Me attempting collision
hits = pg.sprite.spritecollide(self.player, mudballGroup, False)
if hits:
print("HITS!!!!!!!!", hits)
def hailing(self):
jets = JETS()
print("FLYOVER")
self.game.all_sprites.add(jets)
jetsGroup = pg.sprite.Group()
jetsGroup.add(jets)
class MUDBALL(pg.sprite.Sprite):
def __init__(self, x, y):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load("SLIMEballGREEN.png")
# self.mudballGroup = pg.sprite.Group()
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedx = -30
def update(self):
self.rect.x += self.speedx
#kill if moves off screen
if self.rect.centerx < 0:
self.kill()
class JETS(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load("JETSscaled.png")
#self.jetsGroup = pg.sprite.Group()
self.rect = self.image.get_rect()
self.rect.x = 1366
self.rect.y = 0
self.speedx = -70
def update(self):
self.rect.x += self.speedx
#kill if moves off screen
if self.rect.x + 848 < 0:
self.kill()
You're using the Player class for the collision detection, but you have to use an instance of this class instead.
# Player is the class, but spritecollide needs an instance.
hits = pg.sprite.spritecollide(Player, mudballGroup, False)
To create an instance of the Player class, just write:
# Don't forget to pass the game instance to the `Player`s __init__ method.
player = Player(game)
It also looks odd to me that you define the mudballGroup inside of the shoot method. That means the group will only contain one mudball, but then you could also just check if the rects of the player and mudball collide: player.rect.colliderect(mudball.rect) instead of spritecollide. However, if you want multiple mudballs you need to store the mudballGroup as an attribute of the other class, in the __init__ method write:
self.mudballGroup = pg.sprite.Group()
Edit: Okay, you already have a self.player instance in your game instance. I recommend to define the mudballGroup in the Game class as well and then just pass it and the all_sprites group to the player's shoot method to add a mudball. The collision detection can be done inside the game's update method.
class Game:
def new(self):
# Other code omitted ...
self.mudballGroup = pg.sprite.Group()
def update(self):
# Check if the player is shooting.
if self.player.shooting: # You have to add a `shooting` attribute to player.
# `shoot` just adds a mudball to these groups.
self.player.shoot(self.all_sprites, self.mudballGroup)
# Then detect collisions.
hits = pg.sprite.spritecollide(self.player, self.mudballGroup, False)
if hits:
print("HITS!!!!!!!!", hits)
class Player(pg.sprite.Sprite):
# Pass the sprite groups of the game to the shoot method.
def shoot(self, all_sprites, mudballGroup):
mudball = MUDBALL(self.player.centerx, self.player.centery)
# Add sprite to the passed groups.
all_sprites.add(mudball)
mudballGroup.add(mudball)
Edit 2: Here's the other variant. Pass the two needed sprite groups to the player when you create the instance (you don't have to pass the complete game instance), then set them as attributes of the player.
class Game:
def new(self):
# Other code omitted ...
self.all_sprites = pg.sprite.Group()
self.mudballGroup = pg.sprite.Group()
self.player = Player(self.all_sprites, self.mudballGroup)
def update(self):
# Check if the player is shooting.
if self.player.shooting:
# `shoot` adds a mudball to self.all_sprites & self.mudballGroup.
self.player.shoot()
# Then detect collisions.
hits = pg.sprite.spritecollide(self.player, self.mudballGroup, False)
if hits:
print("HITS!!!!!!!!", hits)
class Player(pg.sprite.Sprite):
def __init__(self, all_sprites, mudballGroup):
# Other code omitted ...
# These two attributes are references to the groups
# that were defined in the Game class.
self.all_sprites = all_sprites
self.mudballGroup = mudballGroup
def shoot(self):
mudball = MUDBALL(self.player.centerx, self.player.centery)
# Add sprite to the passed groups.
self.all_sprites.add(mudball)
self.mudballGroup.add(mudball)
Edit 3: Okay, forget the shooting attribute (it would only be needed for the player instance to check if the user is shooting). You also don't need to call trump's shoot method in the Game class, since you already call it in his update method. So here's the updated code:
class Game(pg.sprite.Sprite):
def new(self):
# Other code omitted ...
self.all_sprites = pg.sprite.Group()
self.mudballGroup = pg.sprite.Group()
# Pass the groups to trump during the instantiation.
# You don't have to pass the complete game instance (self).
self.trump = TRUMP(self.all_sprites, self.mudballGroup)
self.all_sprites.add(self.trump)
def update(self):
hits = pg.sprite.spritecollide(self.player, self.mudballGroup, False)
for collided_sprite in hits:
print("HIT", collided_sprite)
class TRUMP(pg.sprite.Sprite):
def __init__(self, all_sprites, mudballGroup):
# Now set the passed groups as attributes of the trump instance.
# These attributes are references to the same sprite groups as
# in the Game class.
self.all_sprites = all_sprites
self.mudballGroup = mudballGroup
def shoot(self):
print("SHOOT function")
mudball = MUDBALL(self.rect.centerx, self.rect.centery)
# Add the mudball to the groups.
self.all_sprites.add(mudball)
self.mudballGroup.add(mudball)

Random direction bullets?

I've been working on a bullet-hell game, but when I try to make the bullets spawn in random directions and travel in a straight line, I can't seem to be able to do it. I can see that making the bullet travel +/- certain pixels in x and y axis only causes them to travel in a random pattern that seems unnatural.
My code:
class Bullet(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Bullet.png")
self.rect = self.image.get_rect()
def update(self) :
self.rect.x += random.randrange(-10,10)
self.rect.y += random.randrange(-10,10)
In the __init__ method, you can create self.dx and self.dy variables that will be randomly chosen once upon instantiation and then stay constant every update:
class Bullet(pygame.sprite.Sprite):
def init(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Bullet.png")
self.rect = self.image.get_rect()
self.dx = random.randrange(-10,10)
self.dy = random.randrange(-10,10)
def update(self):
self.rect.x += self.dx
self.rect.y += self.dy

SpriteGroup - adding muliple objects

I am trying to create a sprite class where the user can define and add any number of sprites on to the screen from a random location using this tutorial. However, when I try to run my current program it puts the error
AttributeError: type object 'Sprite' has no attribute 'sprite'
But I dont understand why, all the logic seems correct.
Any suggestions?
Heres my code:
import pygame, sys, random
pygame.init()
black = (0, 0, 0)
image = pygame.image.load("resources/images/img.png")
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 400
sprite_width = 5
sprite_height = 5
sprite_count = 5
# no changes here when using sprite groups
class Sprite(pygame.sprite.Sprite):
def __init__(self, Image, pos):
pygame.sprite.Sprite.__init__(self)
self.image =pygame.image.load("resources/images/img.png")
self.rect = self.image.get_rect()
self.rect.topleft = pos
self.pygame.display.set_caption('Sprite Group Example')
self.clock = pygame.time.Clock()
# this is a how sprite group is created
self.sprite = pygame.sprite.Group()
def update(self):
self.rect.x += 1
self.rect.y += 2
if self.rect.y > SCREEN_HEIGHT:
self.rect.y = -1 * sprtie_height
if self.rect.x > SCREEN_WIDTH:
self.rect.x = -1 * sprite_width
#classmethod
def sprites(self):
for i in range(actor_count):
tmp_x = random.randrange(0, SCREEN_WIDTH)
tmp_y = random.randrange(0, SCREEN_HEIGHT)
# all you have to do is add new sprites to the sprite group
self.sprite.add(Sprite(image, [tmp_x, tmp_y]))
#classmethod
def game_loop(self):
screen = pygame.display.set_mode([640, 400])
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(black)
# to update or blitting just call the groups update or draw
# notice there is no for loop
# this will automatically call the individual sprites update method
self.sprite.update()
self.sprite.draw(screen)
self.pygame.display.update()
self.clock.tick(20)
Sprite.sprites()
Sprite.game_loop()
You define class method (#classmethod) but self.sprite exists only in object/instance not in class - it is created in __init__ when you crate new object/instance.
Remove #classmethod to have object/instance method and have no problem with self.sprite.
Or create sprite outside __init__ to get class attribute
class Sprite(pygame.sprite.Sprite):
sprite = pygame.sprite.Group()
def __init__(self, Image, pos)
# instance variable - value copied from class variable
print self.sprite
# class variable
print self.__class__.sprite
#classmethod
def sprites(self):
# instance variable not exists
# class variable
print self.sprite

Categories

Resources