Pygame: Accessing colliding sprite's properties using spritecollide() - python
I am building a top down shooter in the style of Raiden 2. I need to know how to get access to the enemy object when I detect collision using 'spritecollide'.
The reason I need the enemy object is so that I can lower their energy level for every bullet that hits them.
This is the dictionary that I have to work with:
enemyCollisions = pygame.sprite.groupcollide(shipBulletGroup, enemyGroup, True, False)
Here's my Bullet class:
class Bullet(Entity):
def __init__(self, ship, angle):
Entity.__init__(self)
self.speed = 8
self.level = 0
self.image = pygame.Surface((BULLET_DIMENSIONS, BULLET_DIMENSIONS)).convert()
self.rect = self.image.get_rect()
self.rect.x = ship.rect.centerx
self.rect.y = ship.rect.top
self.angle = angle
def update(self, enemyGroup):
if self.rect.bottom < 0:
self.kill()
else:
self.rect.y -= self.speed
Here's my unfinished Enemy class:
class Enemy_1(Entity):
def __init__(self):
Entity.__init__(self)
self.speed = (1, 1)
self.energy = 10
self.begin = False
self.run = False
self.image = pygame.Surface((WIN_W/5, WIN_H/15)).convert()
self.image.fill((70, 70, 70))
self.rect = self.image.get_rect()
self.rect.centerx = WIN_W/1.333
self.rect.y = -WIN_H/15
def shoot(self):
bullet_1 = Bullet(self, 270)
bullet_2 = Bullet(self, 225)
bullet_3 = Bullet(self, 315)
def update(self, enemyGroup, timer):
if timer == 300:
self.begin = True
elif timer == 900:
self.run = True
elif timer > 900 and self.rect.x > WIN_W:
self.remove(enemyGroup)
if self.run:
self.rect.x += self.speed[0]
self.rect.y += self.speed[1]
elif self.begin:
self.rect.y += self.speed[1]
if self.rect.y > WIN_H/4:
self.rect.y = WIN_H/4
if self.energy == 0:
self.kill()
Here's the complete program:
import sys, pygame, os, random, math
from ast import literal_eval
# Force static position of screen
os.environ['SDL_VIDEO_CENTERED'] = '1'
# Constants
LEFT = 'left'
RIGHT = 'right'
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
PILL_WIDTH = 5
PILL_HEIGHT = 20
WIN_W = 500
WIN_H = 800
SHIP_WIDTH = WIN_W/15
SHIP_HEIGHT = WIN_H/15
BULLET_DIMENSIONS = 5
TIMER = 0
class Entity(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
class Text(Entity):
def __init__(self, text, size, color, position, font=None):
Entity.__init__(self)
self.color = color
self.font = pygame.font.Font(font, size)
self.text = text
self.set(text, position)
def set(self, text, position):
self.image = self.font.render(str(text), 1, self.color)
self.rect = self.image.get_rect()
self.rect.move_ip(position[0]-self.rect.width/2, (position[1]-self.rect.height)/2)
class Ship(Entity):
def __init__(self, container):
Entity.__init__(self)
self.speed = 5
self.score = 0
self.image = pygame.Surface((SHIP_WIDTH, SHIP_HEIGHT)).convert()
self.rect = self.image.get_rect()
self.rect.centerx = container.centerx
self.rect.y = container.centery
def update(self, bulletGroup):
key = pygame.key.get_pressed()
if key[pygame.K_w]:
self.rect.centery -= self.speed
if key[pygame.K_s]:
self.rect.centery += self.speed
if key[pygame.K_d]:
self.rect.centerx += self.speed
if key[pygame.K_a]:
self.rect.centerx -= self.speed
if key[pygame.K_SPACE]:
if TIMER % 7 == 0:
bullet = Bullet(self, 90, 'friend')
bulletGroup.add(bullet)
# Ship Movement Boundaries
if self.rect.y < WIN_H/25:
self.rect.y = WIN_H/25
if self.rect.y > WIN_H - SHIP_HEIGHT:
self.rect.y = WIN_H - SHIP_HEIGHT
if self.rect.x < 0:
self.rect.x = 0
if self.rect.x > WIN_W - SHIP_WIDTH:
self.rect.x = WIN_W - SHIP_WIDTH
class Bullet(Entity):
def __init__(self, ship, angle, type):
Entity.__init__(self)
self.speed = 8
self.level = 0
self.image = pygame.Surface((BULLET_DIMENSIONS, BULLET_DIMENSIONS)).convert()
self.rect = self.image.get_rect()
self.dx = math.cos(math.radians(angle)) * self.speed
self.dy = math.sin(math.radians(angle)) * self.speed
self.setXY(ship, type)
def setXY(self, ship, type):
self.rect.x = ship.rect.centerx
if type == 'friend':
self.rect.y = ship.rect.top
elif type == 'enemy':
self.rect.y = ship.rect.bottom
def update(self):
if self.rect.bottom < 0:
self.kill()
else:
self.rect.x -= self.dx
self.rect.y -= self.dy
if type == 'friend':
self.rect.y = -self.rect.y
self.rect.x = -self.rect.x
class Powerup(Entity):
def __init__(self, xden, pillCount):
Entity.__init__(self)
self.speed = 3
self.density = xden
self.image = pygame.Surface((PILL_WIDTH, PILL_HEIGHT)).convert()
self.rect = self.image.get_rect()
self.rect = self.rect.move(100,100)
def restart(self):
pass
def update(self):
if self.rect.y > WIN_H:
del self
else:
self.rect = self.rect.move((0, self.speed))
class Enemy(Entity):
def __init__(self):
Entity.__init__(self)
self.begin = False
self.run = False
self.color = (153, 0, 76)
class Planes(Enemy):
def __init__(self, speed, energy, size, location):
Enemy.__init__(self)
self.speed = speed
self.energy = energy
self.image = pygame.Surface(size).convert()
self.image.fill(self.color)
self.rect = self.image.get_rect()
self.rect.centerx = location[0]
self.rect.y = location[1]
def shoot(self, enemyBulletGroup):
bullet_1 = Bullet(self, 240, 'enemy')
bullet_2 = Bullet(self, 270, 'enemy')
bullet_3 = Bullet(self, 300, 'enemy')
enemyBulletGroup.add(bullet_1, bullet_2, bullet_3)
def update(self, enemyGroup, enemyBulletGroup):
if TIMER == 300:
self.begin = True
elif TIMER == 900:
self.run = True
elif self.rect.x > WIN_W or self.energy == 0:
self.kill()
if self.run:
self.rect.x += self.speed[0]
self.rect.y += self.speed[1]
if TIMER % 100 == 0:
self.shoot(enemyBulletGroup)
elif self.begin:
self.rect.y += self.speed[1]
if self.rect.y > WIN_H/4:
self.rect.y = WIN_H/4
if TIMER % 100 == 0:
self.shoot(enemyBulletGroup)
def main():
# Initialize Everything
pygame.init()
global TIMER
fps = 60
lLeft = lRight = lUp = lDown = shoot = False
clock = pygame.time.Clock()
play = True
pygame.display.set_caption('Pong')
screen = pygame.display.set_mode((WIN_W, WIN_H), pygame.SRCALPHA)
# Create Game Objects
ship = Ship(pygame.rect.Rect(0, 0, WIN_W, WIN_H))
score1 = Text("Score: " + str(ship.score), 40, BLACK, (WIN_W/2, (WIN_H/25)))
enemy_1 = Planes((1, 1), 10, (WIN_W/5, WIN_H/15), (WIN_W/1.4, -WIN_H/15))
# Create Groups
powerupGroup = pygame.sprite.Group()
shipGroup = pygame.sprite.Group()
shipGroup.add(ship)
textGroup = pygame.sprite.Group()
textGroup.add(score1)
shipBulletGroup = pygame.sprite.Group()
enemyBulletGroup = pygame.sprite.Group()
enemyGroup = pygame.sprite.Group()
enemyGroup.add(enemy_1)
# Gameplay
while play:
# Checks if window exit button pressed
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
# Update
powerupGroup.update()
ship.update(shipBulletGroup)
shipBulletGroup.update()
textGroup.update()
enemyGroup.update(enemyGroup, enemyBulletGroup)
enemyBulletGroup.update()
enemyCollisions = pygame.sprite.groupcollide(shipBulletGroup, enemyGroup, True, False)
for key in enemyCollisions:
print enemyCollisions[key].energy
# Print Background/Sprites
screen.fill(WHITE)
powerupGroup.draw(screen)
shipGroup.draw(screen)
shipBulletGroup.draw(screen)
textGroup.draw(screen)
enemyGroup.draw(screen)
enemyBulletGroup.draw(screen)
# Print Score Bar
hori_partition = pygame.Surface((WIN_W, 1))
screen.blit(hori_partition, (0, WIN_H/25))
TIMER += 1
# Limits frames per iteration of while loop
clock.tick(fps)
# Writes to main surface
pygame.display.flip()
if __name__ == "__main__":
main()
The pygame.sprite.groupcollide() function returns a dictionary which values are list objects:
Every Sprite inside group1 is added to the return dictionary. The value for each item is the list of Sprites in group2 that intersect.
Thats the reason why calling print enemyCollisions[key].energy when iterating over the return dict fails, since a list object -- the value of enemyCollisions[key] -- does not have an energy() method.
To access all values of a dictionary in Python 2.X you could use the .itervalues() method of a dict instance. To get each colliding sprite, iterate again over these values:
enemyCollisions = pygame.sprite.groupcollide(shipBulletGroup, enemyGroup, True, False)
#iterate over all values of the enemyCollisions dict
for enemyCollisions in enemyCollisions.itervalues():
#access all enemies which collided with an shipBullet
for enemy in enemyCollisions:
print enemy.energy
The syntax for accessing the collided sprite's property is as follows:
collisions = pygame.sprite.spritecollide(self, pillGroup, True)
for key in collisions:
self.density += key.density
Related
Im stuck on doing an explosion animation for my cement block sprites [duplicate]
This question already exists: I want to do an explosion animation. I have 4 png files number 0 - 3. In my debris class I loaded them into an image list using a for loop [duplicate] Closed 1 year ago. I want to do an explosion animation. I have 4 png files number 0 - 3. In my debris class I loaded them into an image list using a for loop. Then I made an explosion function but when I load my pygame windows and start shooting my cement block sprites, my cement block sprites disappear which is fine but they don't do the exploding animation I want. How can I fix this problem. Would appreciate the help. My code: import random import pygame import pygame.freetype pygame.init() #screen settings WIDTH = 1000 HEIGHT = 400 screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("AutoPilot") screen.fill((255, 255, 255)) #fps FPS = 120 clock = pygame.time.Clock() #load images bg = pygame.image.load('background/street.png').convert_alpha() # background bullets = pygame.image.load('car/bullet.png').convert_alpha() debris_img = pygame.image.load('debris/cement.png') #define game variables shoot = False #player class class Player(pygame.sprite.Sprite): def __init__(self, scale, speed): pygame.sprite.Sprite.__init__(self) self.bullet = pygame.image.load('car/bullet.png').convert_alpha() self.bullet_list = [] self.speed = speed #self.x = x #self.y = y self.moving = True self.frame = 0 self.flip = False self.direction = 0 self.score = 0 #load car self.images = [] img = pygame.image.load('car/car.png').convert_alpha() img = pygame.transform.scale(img, (int(img.get_width()) * scale, (int(img.get_height()) * scale))) self.images.append(img) self.image = self.images[0] self.rect = self.image.get_rect() self.update_time = pygame.time.get_ticks() self.movingLeft = False self.movingRight = False self.rect.x = 465 self.rect.y = 325 #draw car to screen def draw(self): screen.blit(self.image, (self.rect.centerx, self.rect.centery)) #move car def move(self): #reset the movement variables dx = 0 dy = 0 #moving variables if self.movingLeft and self.rect.x > 33: dx -= self.speed self.flip = True self.direction = -1 if self.movingRight and self.rect.x < 900: dx += self.speed self.flip = False self.direction = 1 #update rectangle position self.rect.x += dx self.rect.y += dy #shoot def shoot(self): bullet = Bullet(self.rect.centerx + 18, self.rect.y + 30, self.direction) bullet_group.add(bullet) #check collision def collision(self, debris_group): for debris in debris_group: if pygame.sprite.spritecollide(debris, bullet_group, True): debris.health -= 1 if debris.health <= 0: self.score += 1 debris.kill() #player stats def stats(self): myfont = pygame.font.SysFont('comicsans', 30) scoretext = myfont.render("Score: " + str(self.score), 1, (0,0,0)) screen.blit(scoretext, (100,10)) #bullet class class Bullet(pygame.sprite.Sprite): def __init__(self, x, y, direction): pygame.sprite.Sprite.__init__(self) self.speed = 5 self.image = bullets self.rect = self.image.get_rect() self.rect.center = (x,y) self.direction = direction def update(self): self.rect.centery -= self.speed #check if bullet has gone off screen if self.rect.centery < 1: self.kill() #debris class class Debris(pygame.sprite.Sprite): def __init__(self,scale,speed): pygame.sprite.Sprite.__init__(self) self.scale = scale self.x = random.randrange(100,800) self.speed_y = 10 self.y = 15 self.speed = speed self.vy = 0 self.on_ground = True self.move = True self.health = 4 self.max_health = self.health self.alive = True self.velocity = random.randrange(1,2) self.speed_x = random.randrange(-3,3) self.moving_down = True #load debris self.image = debris_img self.rect = self.image.get_rect() self.rect.x = random.randrange(100, 800) self.rect.y = random.randrange(-150, -100) self.rect.center = (self.x,self.y) #load explosion self.images = [] for i in range(4): self.images.append(pygame.image.load(f'explosion/{i}.png')) self.index = 0 self.img = self.images[self.index] #spawn new debris def spawn_new_debris(self): self.rect.x = random.randrange(100, 800) self.rect.y = random.randrange(-150, -100) self.velocity = random.randrange(1, 2) self.speed_x = random.randrange(-3, 3) #respawn debris when they go of the screen def boundaries(self): if self.rect.left > WIDTH + 10 or self.rect.right < -10 or self.rect.top > HEIGHT + 10: self.spawn_new_debris() #update image def update(self): self.rect.y += self.velocity self.rect.x += self.speed_x self.boundaries() #make debris fall down def falldown(self): self.rect.centery += self.velocity if self.moving_down and self.rect.y > 350: self.kill() #explosion def explode(self): if self.max_health <= 0: self.index += 1 if self.index >= len(self.images): self.index = 0 self.img = self.images[self.index] ######################CAR/DEBRIS########################## player = Player(1,5) ########################################################## #groups bullet_group = pygame.sprite.Group() debris_group = pygame.sprite.Group() all_sprites = pygame.sprite.Group() for i in range(50): d = Debris(1,5) debris_group.add(d) all_sprites.add(d) #game runs here run = True while run: #draw street screen.blit(bg, [0, 0]) #update groups bullet_group.update() bullet_group.draw(screen) debris_group.update() debris_group.draw(screen) #draw car player.draw() player.move() player.collision(debris_group) player.stats() #update all sprites all_sprites.update() all_sprites.draw(screen) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False #check if key is down if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: run = False if event.key == pygame.K_a: player.movingLeft = True if event.key == pygame.K_d: player.movingRight = True if event.key == pygame.K_SPACE: player.shoot() shoot = True #check if key is up if event.type == pygame.KEYUP: if event.key == pygame.K_a: player.movingLeft = False if event.key == pygame.K_d: player.movingRight = False #update the display pygame.display.update() pygame.display.flip() clock.tick(FPS) pygame.quit()
How do I rotate the player movement? [duplicate]
This question already has answers here: Pygame doesn't let me use float for rect.move, but I need it (2 answers) How to draw a moving circle in Pygame with a small angle at a low speed and blinking? (1 answer) Closed 2 years ago. I am making an Astroid clone where you only move with the keyboard. The player is able to rotate and needs to move forward to were it is facing. For example: When I rotate the player by 30 degrees and push W to go forward I want the player to go forward in the direction the player is facing. I can't just use self.rect.x += self.vel I also tried to use this function: def calculate_new_xy(speed, degrees): add_x = (speed*(math.sin((degrees)*math.pi/180))) add_y = (speed*(math.cos((degrees)*math.pi/180))) return add_x, add_y In theory, it should work because I am taking the sin and cos of this Triangle. But when I added that to my code the movement of the player wasn't as smooth as I hoped it to be. And sometimes the player moved indirectly forward. I also noticed that the player moves faster when facing to the top left than to the bottom right. Due to that the player doesn't move in a circle when turning always right. Instead the player moves elliptical to the top left. Here is the code: import pygame import time import random import math from os import path #Colors: WHITE = (255, 255, 255) BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) YELLOW = (255, 255, 0) #Settings: pygame.init() pygame.mixer.init() #for sound WIDTH = 700 HEIGHT = 500 screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Astroids") clock = pygame.time.Clock() FPS = 60 #Images/Sounds: game_folder = path.dirname(__file__) img_folder = path.join(game_folder, "img") player = pygame.image.load(path.join(img_folder, 'Player.png')) stone = pygame.image.load(path.join(img_folder, 'Stein.png')) #Game Classes class Player(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.original_image = player self.image = self.original_image.copy() self.rect = self.image.get_rect() self.rect.center = (WIDTH/2, HEIGHT/2) self.speed_x = 0 self.speed_y = 0 self.vel = 3 self.degrees = 0 def boundary(self): if self.rect.left > WIDTH: self.rect.right = 0 if self.rect.bottom < 0: self.rect.top = HEIGHT if self.rect.right < 0: self.rect.left = WIDTH if self.rect.top > HEIGHT: self.rect.bottom = 0 def movement(self): '''Player Movement''' keystate = pygame.key.get_pressed() if keystate[pygame.K_q]: self.degrees += 3 if keystate[pygame.K_e]: self.degrees -= 3 if keystate[pygame.K_w]: self.rect.centerx += (self.vel*(math.sin((self.degrees+180)*math.pi/180))) self.rect.centery += (self.vel*(math.cos((self.degrees+180)*math.pi/180))) if keystate[pygame.K_s]: self.rect.centery += -self.vel * math.sin(math.radians(self.degrees - 90)) self.rect.centerx += self.vel * math.cos(math.radians(self.degrees - 90)) def rotate(self): old_center = self.rect.center self.image = pygame.transform.rotate(self.original_image, self.degrees) self.rect = self.image.get_rect() self.rect.center = old_center def update(self): '''Picture is "printed"''' self.movement() self.boundary() self.rotate() #self.rect.y += self.speed_y class Astroid(pygame.sprite.Sprite): def __init__(self, life): pygame.sprite.Sprite.__init__(self) self.original_image = stone self.image = self.original_image.copy() self.rect = self.image.get_rect() self.rect.center = (WIDTH/2, HEIGHT/2) self.speed = 2 self.life = life self.speed_x = random.randrange(-3, 3) self.speed_y = random.randrange(-3, 3) self.rect.x = random.randrange(0, WIDTH - self.rect.width) self.rect.y = random.randrange(-3, 3) self.last_rotation = pygame.time.get_ticks() # keeps track of time in milliseconds self.rotation_deegre = random.randint(0, 360) self.rotation_speed = 5 def rotate(self): current_time = pygame.time.get_ticks() if current_time - self.last_rotation > 50: self.last_rotation = current_time self.rotation_deegre += self.rotation_speed old_center = self.rect.center self.image = pygame.transform.rotate(self.original_image, self.rotation_deegre) self.rect = self.image.get_rect() self.rect.center = old_center '''def new_astroids(self): for i in range(2): m = Astroid() all_sprites.add(m) all_astroids.add(m)''' #to do def boundary(self): if self.rect.left > WIDTH: self.rect.right = 0 if self.rect.bottom < 0: self.rect.top = HEIGHT if self.rect.right < 0: self.rect.left = WIDTH if self.rect.top > HEIGHT: self.rect.bottom = 0 def movement(self): self.rect.x += self.speed_x self.rect.y += self.speed_y def update(self): '''Picture is "printed"''' self.movement() self.boundary() self.rotate() #Game Funktions def calculate_new_xy(speed, degrees): add_x = (speed*(math.sin((degrees)*math.pi/180))) add_y = (speed*(math.cos((degrees)*math.pi/180))) return add_x, add_y #Game Sprites all_astroids = pygame.sprite.Group() all_sprites = pygame.sprite.Group() player = Player() all_sprites.add(player) for i in range (1): m = Astroid(3) all_astroids.add(m) all_sprites.add(m) #Main Game Loop running = True while running: #Keep the game runnung at 60 FPS clock.tick(FPS) #Check for events: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False #Update for Sprites all_sprites.update() #Check to see if any Astroids hit the player astroid_colision = pygame.sprite.spritecollide(player, all_astroids, False) if astroid_colision: running = False #Draw/Render screen.fill(BLACK) all_sprites.draw(screen) #Update the display pygame.display.update() pygame.quit() Any help is welcomed.
But when I added that to my code the movement of the player wasn't as smove as i hoped The issue is caused by the fact, that the attributes of a pygame.Rect are integral values. Every time the position of the rectangle is changed, the fraction of the floating point motion vector is lost. You have to compute the position with floating point accuracy and to synchronize the integral rectangle location by rounding the floating point position (round). Add the attributes self.x and self.y: class Player(pygame.sprite.Sprite): def __init__(self): # [...] self.x, self.y = self.rect.center Update self.x and self.y when the rectangle goes out of bounds: class Player(pygame.sprite.Sprite): # [...] def boundary(self): if self.rect.left > WIDTH: self.rect.right = 0 self.x = self.rect.centerx if self.rect.bottom < 0: self.rect.top = HEIGHT self.y = self.rect.centery if self.rect.right < 0: self.rect.left = WIDTH self.x = self.rect.centerx if self.rect.top > HEIGHT: self.rect.bottom = 0 self.y = self.rect.centery Change the the attributes self.x and self.y when the player moves and update self.rect.center by self.x and self.y: class Player(pygame.sprite.Sprite): # [...] def movement(self): # [...] if keystate[pygame.K_w]: self.x += (self.vel*(math.sin((self.degrees+180)*math.pi/180))) self.y += (self.vel*(math.cos((self.degrees+180)*math.pi/180))) if keystate[pygame.K_s]: self.y += -self.vel * math.sin(math.radians(self.degrees - 90)) self.x += self.vel * math.cos(math.radians(self.degrees - 90)) self.rect.center = round(self.x), round(self.y) Class Player: class Player(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.original_image = player self.image = self.original_image.copy() self.rect = self.image.get_rect() self.rect.center = (WIDTH/2, HEIGHT/2) self.speed_x = 0 self.speed_y = 0 self.vel = 3 self.degrees = 0 self.x, self.y = self.rect.center def boundary(self): if self.rect.left > WIDTH: self.rect.right = 0 self.x = self.rect.centerx if self.rect.bottom < 0: self.rect.top = HEIGHT self.y = self.rect.centery if self.rect.right < 0: self.rect.left = WIDTH self.x = self.rect.centerx if self.rect.top > HEIGHT: self.rect.bottom = 0 self.y = self.rect.centery def movement(self): '''Player Movement''' keystate = pygame.key.get_pressed() if keystate[pygame.K_q]: self.degrees += 3 if keystate[pygame.K_e]: self.degrees -= 3 if keystate[pygame.K_w]: self.x += (self.vel*(math.sin((self.degrees+180)*math.pi/180))) self.y += (self.vel*(math.cos((self.degrees+180)*math.pi/180))) if keystate[pygame.K_s]: self.y += -self.vel * math.sin(math.radians(self.degrees - 90)) self.x += self.vel * math.cos(math.radians(self.degrees - 90)) self.rect.center = round(self.x), round(self.y) def rotate(self): old_center = self.rect.center self.image = pygame.transform.rotate(self.original_image, self.degrees) self.rect = self.image.get_rect() self.rect.center = old_center def update(self): '''Picture is "printed"''' self.movement() self.boundary() self.rotate() #self.rect.y += self.speed_y
Make bullets fire off in the direction the player is facing
I was just getting some help to figure out how to get my player fire bullets when I realized that they only go (kinda expected this but however as only had y value for movement). I don't know how I'll make the bullets fire off in the direction the player is facing. I have some idea of what to but I just don't know how to do it... I thought I could somehow use the cursor and player tracking that's in this game for the visuals but I don't know how to make that a one-time thing instead of a constant. For diagonal movement of the bullet, I have no clue. Code below (split into two parts/file Main.py and PlayerSprite.py): Main: py.init() py.mixer.init() screen = py.display.set_mode((WIDTH, HEIGHT)) py.display.set_caption("Dimensional Drifter") clock = py.time.Clock() all_sprites = py.sprite.Group() NPCs = py.sprite.Group() bullets = py.sprite.Group() player = Player() all_sprites.add(player) for i in range(14): n = NPC(player) all_sprites.add(n) NPCs.add(n) # Game loop running = True while running: # keep loop running at the right speed clock.tick(FPS) for event in py.event.get(): # check for closing window if event.type == py.QUIT: running = False elif event.type == py.KEYDOWN: if event.key == py.K_SPACE: New_bullet = player.Shoot() all_sprites.add(New_bullet) bullets.add(New_bullet) # Update all_sprites.update() # # check if there a collision between the bullet and NPC hits = py.sprite.groupcollide(NPCs, bullets, True, True) # check if there a collision between the player and NPC hits = py.sprite.spritecollide(player, NPCs, True) if hits: running = False # updates the position of of mouse and rotates it towards the mouse position mouse_x, mouse_y = py.mouse.get_pos() player.rotate(mouse_x, mouse_y) # render screen.fill(BLACK) all_sprites.draw(screen) # flip the display py.display.flip() py.quit() PlayerSprite import pygame as py import math import random WIDTH = 800 HEIGHT = 600 FPS = 60 # define colors WHITE = (255, 255, 255) BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) YELLOW = (255, 255, 0) class Player(py.sprite.Sprite): def __init__(self): py.sprite.Sprite.__init__(self) self.image = py.Surface((40, 40), py.SRCALPHA) self.image.fill(GREEN) self.rect = self.image.get_rect() self.rect.centerx = WIDTH / 2 self.rect.bottom = HEIGHT / 2 self.Yspeed = 0 self.rotatableimage = self.image def update(self): self.Xspeed = 0 self.Yspeed = 0 # line below allow for key press to equate to move of sprite keypreesed = py.key.get_pressed() if keypreesed[py.K_a]: self.Xspeed = - 11 if keypreesed[py.K_d]: self.Xspeed = 11 if keypreesed[py.K_w]: self.Yspeed = - 11 if keypreesed[py.K_s]: self.Yspeed = 11 self.rect.x += self.Xspeed self.rect.y += self.Yspeed # line below allow the sprite to wrap around the screen if self.rect.left > WIDTH: self.rect.right = 0 if self.rect.right < 0: self.rect.left = WIDTH if self.rect.top > HEIGHT: self.rect.top = 0 if self.rect.bottom < 0: self.rect.bottom = HEIGHT def rotate(self, mouse_x, mouse_y): rel_x = mouse_x - self.rect.x rel_y = mouse_y - self.rect.y angle = (180 / math.pi) * -math.atan2(rel_y, rel_x) self.image = py.transform.rotate(self.rotatableimage, int(angle)) self.rect = self.image.get_rect(center=(self.rect.centerx, self.rect.centery)) return def Shoot(self): return Bullet(self.rect.centerx, self.rect.top) class NPC(py.sprite.Sprite): def __init__(self, player): py.sprite.Sprite.__init__(self) self.player = player self.image = py.Surface((30, 30)).convert_alpha() self.image.fill(RED) self.originalimage = self.image self.rect = self.image.get_rect() self.spawn() # allows of spawning from all four side of the screen and set the x, y speed and spawn position def spawn(self): self.direction = random.randrange(4) if self.direction == 0: self.rect.x = random.randrange(WIDTH - self.rect.width) self.rect.y = random.randrange(-100, -40) self.Xspeed = random.randrange(-2, 2) self.Yspeed = random.randrange(4, 8) elif self.direction == 1: self.rect.x = random.randrange(WIDTH - self.rect.width) self.rect.y = random.randrange(HEIGHT, HEIGHT + 60) self.Xspeed = random.randrange(-2, 2) self.Yspeed = -random.randrange(4, 8) elif self.direction == 2: self.rect.x = random.randrange(-100, -40) self.rect.y = random.randrange(HEIGHT - self.rect.height) self.Xspeed = random.randrange(4, 8) self.Yspeed = random.randrange(-2, 2) elif self.direction == 3: self.rect.x = random.randrange(WIDTH, WIDTH + 60) self.rect.y = random.randrange(HEIGHT - self.rect.height) self.Xspeed = -random.randrange(4, 8) self.Yspeed = random.randrange(-2, 2) def update(self): self.rect.x += self.Xspeed self.rect.y += self.Yspeed # makes it so that NPC point to wards the player as it passes from side to side dir_x, dir_y = self.player.rect.x - self.rect.x, self.player.rect.y - self.rect.y self.rot = (180 / math.pi) * math.atan2(-dir_x, -dir_y) self.image = py.transform.rotate(self.originalimage, self.rot) # Respawns the NPC when they hit an side if self.direction == 0: if self.rect.top > HEIGHT + 10: self.spawn() elif self.direction == 1: if self.rect.bottom < -10: self.spawn() elif self.direction == 2: if self.rect.left > WIDTH + 10: self.spawn() elif self.direction == 3: if self.rect.right < -10: self.spawn() class Bullet(py.sprite.Sprite): def __init__(self, x, y): py.sprite.Sprite.__init__(self) self.image = py.Surface((5, 5)) self.image.fill(YELLOW) self.rect = self.image.get_rect() self.rect.bottom = y self.rect.centerx = x self.Yspeed = -10 def update(self): self.rect.y += self.Yspeed # kill if moved of screen if self.rect.bottom > HEIGHT or self.rect.top < 0: self.kill() if self.rect.right > WIDTH or self.rect.left < 0: self.kill()
Add 2 attributes self.lastX and self.lastY to the class Player and change the attributes when the player changes the direction: class Player(py.sprite.Sprite): def __init__(self): # [...] self.lastX = 0 self.lastY = -10 def update(self): # [...] self.rect.x += self.Xspeed self.rect.y += self.Yspeed if self.Xspeed != 0 or self.Yspeed != 0: self.lastX = self.Xspeed self.lastY = self.Yspeed Add an argument Xspeed ans Yspeed to the class Bullet class Bullet(py.sprite.Sprite): def __init__(self, x, y, Xspeed, Yspeed): py.sprite.Sprite.__init__(self) self.image = py.Surface((5, 5)) self.image.fill(YELLOW) self.rect = self.image.get_rect() self.rect.bottom = y self.rect.centerx = x self.Xspeed = Xspeed self.Yspeed = Yspeed def update(self): self.rect.x += self.Xspeed self.rect.y += self.Yspeed # [...] Set the attributes when the bullet spawns class Player(py.sprite.Sprite): # [...] def Shoot(self): return Bullet(self.rect.centerx, self.rect.centery, self.lastX, self.lastY) Alternatively it is also possible to set the speed dependent on the direction to the mouse cursor. Get the position of the player and the mouse cursor and compute the x and y distance (Vector ): pos = self.rect.center mpos = py.mouse.get_pos() vx = mpos[0] - pos[0] vy = mpos[1] - pos[1] If the mouse position and the bullet position are equal, that does not make any sense, thus the bullet is skipped if vx == 0 and vy == 0: return None Of course this vector is far to long, if you would use it for the direction (Xspeed, Yspeed) directly, then the bullet would step to the mouse cursor in one turn. In the following I use pygame.math.Vector2, because it provides the handy method scale_to_length, that scales a vector to a specified Euclidean length: direction = py.math.Vector2(vx, vy) direction.scale_to_length(10) Now the x and y component of the vector contain the x and y component of the speed. Since the components are floating point values, they are round to integral values: return Bullet(pos[0], pos[1], round(direction.x), round(direction.y)) Method Shoot: class Player(py.sprite.Sprite): # [...] def Shoot(self): pos = self.rect.center mpos = py.mouse.get_pos() vx, vy = mpos[0] - pos[0], mpos[1] - pos[1] if vx == 0 and vy == 0: return None direction = py.math.Vector2(vx, vy) direction.scale_to_length(10) return Bullet(pos[0], pos[1], round(direction.x), round(direction.y)) Note, if you set the bullet dependent on the direction to the mouse cursor, then it may be useful to spawn the bullet by a mouse click: while running: # [...] for event in py.event.get(): if event.type == py.QUIT: # [...] elif event.type == py.MOUSEBUTTONDOWN: if event.button == 1: New_bullet = player.Shoot() if New_bullet: all_sprites.add(New_bullet) bullets.add(New_bullet)
You can use pygames Vector2 to move in any direction. You calculate the angle of the player you can use that. class Player(py.sprite.Sprite): def __init__(self): ... self.angle = 0 def rotate(self, mouse_x, mouse_y): ... self.angle = -angle #make negative otherwise it will be going away from mouse def Shoot(self): return Bullet(self.rect.centerx, self.rect.top, py.math.Vector2(1,0).rotate(self.angle)) then in your bullet class, get the direction and add to its position class Bullet(py.sprite.Sprite): def __init__(self, x, y, Dir): ... self.Dir = Dir def update(self): self.rect.y += self.Dir[1] * self.speed self.rect.x += self.Dir[0] * self.speed ...
Can I get the rect.x from one class to use in another class?
So basically I want to my Road class to always know where my Car (Player1) class is so road knows whether to slow down as Car has left road (or be able to speed back up when Car gets back within road area. As Car only moves left / right and it is the road that speeds up coming down screen, I figured a way to mimic the car using a "self.where_player" at same start position and then add / subtract x position and follow it with key's pressed (a, d), but after awhile it can lose Car placement as Car can either have x values added or subtracted while slipping left or right on ice or have it keys (a, d) temporarily disabled if spinning on oil, while the road does not know these things... If I could figure out how the Road class could "always" know where the Car class rect.x was, well that would be fantastic! import pygame W = 1000 H = 800 pygame.init() pygame.display.set_mode((W, H)) class Road1(pygame.sprite.Sprite): def __init__(self, top, player): pygame.sprite.Sprite.__init__(self) bkgrnd = pygame.image.load("Images/Road.png").convert_alpha() self.image = pygame.transform.scale(bkgrnd, (300, 100)) self.rect = self.image.get_rect(topleft = (120, top)) self.top = top self.speedy = 0 self.player = player self.out_of_bounds = False def update(self): self.top += self.speedy if self.top >= 800: self.top = self.top - 900 self.rect.top = self.top keys = pygame.key.get_pressed() if keys[pygame.K_w]: self.speedy = min(20, self.speedy + 0.25) if keys[pygame.K_s]: self.speedy = max(0, self.speedy - 0.25) if keys[pygame.K_a]: self.player -= 5 if self.player <= 120: self.player = 120 self.out_of_bounds = True if self.out_of_bounds and self.speedy > 5: self.speedy -= 0.35 else: self.out_of_bounds = False if keys[pygame.K_d]: self.player += 5 if self.player >= 420: self.player = 420 self.out_of_bounds = True if self.out_of_bounds and self.speedy > 5: self.speedy -= 0.35 else: self.out_of_bounds = False The following is a portion from the Main page enough to make Road work: import pygame import random import sys # from Ice_Patch import Ice1, Ice2 # from Oil_Slick import Oil1, Oil2 from Player1 import Player1, car1_image # from Player2 import Player2, car2_image from Roads import Road1, # Road2 W, H = 1000, 800 HW, HH = W / 2, H / 2 AREA = W * H FPS = 30 GREEN = (0, 200, 0) pygame.init() CLOCK = pygame.time.Clock() DS = pygame.display.set_mode((W, H)) pygame.display.set_caption("Racing Fury") def events(): for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() player = Player1() all_sprites = pygame.sprite.Group() road = pygame.sprite.Group() for i in range(9): rd = Road1((i - 1) * 100, player) all_sprites.add(rd) road.add(rd) while True: CLOCK.tick(FPS) events() all_sprites.update() DS.fill(GREEN) all_sprites.draw(DS) pygame.display.flip() Player1 Class: import pygame W = 1000 H = 800 pygame.init() pygame.display.set_mode((W, H)) car1_image = pygame.image.load("Images/GalardB.png").convert_alpha() car1_image = pygame.transform.scale(car1_image, (40, 70)) GREY = (211, 211, 211) ANIM_DELAY = 50 POWERUP_TIME = 5000 SPIN_TIME = 1950 class Player1(pygame.sprite.Sprite): def __init__(self, car1_image, x, y): pygame.sprite.Sprite.__init__(self) self.image = car1_image self.image.set_colorkey(GREY) self.rect = self.image.get_rect() self.rect.center = (x, y) self.radius = 25 self.speedx = 0 self.rotations = [self.image] self.spinning_since = 0 self.rotate() self.damage = 100 self.shoot_delay = 250 self.last_shot = pygame.time.get_ticks() self.lives = 3 self.hidden = False self.hide_timer = pygame.time.get_ticks() self.score = 0 self.crashx = 0 self.crashy = 0 self.power = 0 self.power_time = pygame.time.get_ticks() self.not_spinning = True def update(self): if self.power >= 1 and pygame.time.get_ticks() - self.power_time > POWERUP_TIME: self.power -= 1 self.power_time = pygame.time.get_ticks() if self.hidden and pygame.time.get_ticks() - self.hide_timer > 1500: self.hidden = False self.rect.center = (300, 700) self.speedx = 0 keystate1 = pygame.key.get_pressed() if keystate1[pygame.K_SPACE]: self.shoot() if self.not_spinning: if keystate1[pygame.K_a]: self.speedx = -5 if keystate1[pygame.K_d]: self.speedx = 5 self.rect.x += self.speedx if self.rect.right > 420: self.rect.right = 420 if self.rect.left < 120: self.rect.left = 120 if self.spinning_since > 0: time_spinning = pygame.time.get_ticks() - self.spinning_since if time_spinning >= SPIN_TIME: self.spinning_since = 0 index = 0 self.not_spinning = True else: index = int(time_spinning / ANIM_DELAY) index %= len(self.rotations) self.not_spinning = False cx, cy = self.rect.center self.image = self.rotations[index] self.rect = self.image.get_rect() self.rect.centerx = cx self.rect.centery = cy def powerup(self): self.power += 1 self.power_time = pygame.time.get_ticks() def shoot(self): now = pygame.time.get_ticks() if now - self.last_shot > self.shoot_delay: self.last_shot = now if self.power == 0: pass if self.power >= 1: bullet1 = Bullet(self.rect.centerx, self.rect.top) all_sprites.add(bullet1) bullets1.add(bullet1) def rotate(self): ROT_FRAMES = 36 for i in range(ROT_FRAMES): angle = i * (720 / ROT_FRAMES) rotated_image = pygame.transform.rotozoom(self.image, angle, 1) self.rotations.append(rotated_image) def spin(self): if self.spinning_since == 0: self.spinning_since = pygame.time.get_ticks() else: pass def slide(self): keystate1 = pygame.key.get_pressed() if keystate1[pygame.K_a]: self.rect.x -= 2 self.rect.x -= 1.5 self.rect.x -= 1 self.rect.x -= 0.5 self.rect.x -= 0.25 if keystate1[pygame.K_d]: self.rect.x += 2 self.rect.x += 1.5 self.rect.x += 1 self.rect.x += 0.5 self.rect.x += 0.25 def hide(self): self.crashx = self.rect.x self.crashy = self.rect.y self.hidden = True self.hide_timer = pygame.time.get_ticks() self.rect.center = (W * 2, H * 2)
The easiest solution is to make the "road" knowing the player. Add an attribute player the class Road1: class Road1(pygame.sprite.Sprite): def __init__(self, top, player): pygame.sprite.Sprite.__init__(self) self.player = player # [...] Instead of self.where_player you can directly ask self.player for its position. Pass the player to the road objects when they are constructed. e.g.: player = Player1(?????) all_sprites = pygame.sprite.Group() road = pygame.sprite.Group() for i in range(9): rd = Road1((i - 1) * 100, player) all_sprites.add(rd) road.add(rd) An other option would be to add an argument player to the method update: class Road1(pygame.sprite.Sprite): # [...] def update(self, player): # [...] Of course you have to separate the update of road and player. e.g.: player.update() road.update(player)
Bullet not colliding with enemy Pygame (Python 3)
I want to make a mega man similar game where you jump around and shooting stuff. But I've noticed there's something wrong with the collision, I have a video below: https://youtu.be/p2VCtbBkefo I'm planning to make this project open source, so anybody can customize it. Please don't steal this code, but you may use chunks of it to help you with something. because I haven't put it on GitHub publicly yet. main.py: import pygame as pg from player import * from settings import * from levels import * from block import * from enemy import * class Game: def __init__(self): pg.init() pg.mixer.init() self.screen = pg.display.set_mode((width, height)) pg.display.set_caption("wait until realesed.") self.clock = pg.time.Clock() self.enemiesList = [] self.running = True self.shootRight = True def loadLevel(self, level, enemies, group, group2, group3): for y in range(0, len(level)): for x in range(0, len(level[y])): if (level[y][x] == 1): blockList.append(Block(x*32, y*32)) group.add(Block(x*32, y*32)) group2.add(Block(x*32, y*32)) for amount in range(0, enemies): group2.add(FlyingEnemy(self)) group3.add(FlyingEnemy(self)) self.enemies.add(FlyingEnemy(self)) self.enemiesList.append(FlyingEnemy(self)) def new(self): self.platforms = pg.sprite.Group() self.all_sprites = pg.sprite.Group() self.enemies = pg.sprite.Group() self.bullets = pg.sprite.Group() self.player = Player() self.loadLevel(level1["platform"], level1["enemies"], self.platforms, self.all_sprites, self.enemies) self.all_sprites.add(self.player) self.run() def shoot(self): if self.shootRight: self.bullet = Bullet(self.player.rect.centerx, self.player.rect.centery) self.bullet.speed = 10 self.all_sprites.add(self.bullet) self.bullets.add(self.bullet) print(self.bullet) elif self.shootRight == False: self.bullet = Bullet(self.player.rect.centerx, self.player.rect.centery) self.bullet.speed = -10 self.all_sprites.add(self.bullet) self.bullets.add(self.bullet) print(self.bullet) def run(self): self.playing = True while self.playing: self.clock.tick(FPS) self.events() self.update() self.draw() def update(self): self.all_sprites.update() self.enemy_hits = pg.sprite.spritecollide(self.player, self.enemies, False) #print(enemy_hits) if self.enemy_hits: pass #print("hit") self.bullet_hits = pg.sprite.groupcollide(self.enemies, self.bullets, True, True) if self.bullet_hits: print(self.bullet_hits) pygame.quit() hits = pg.sprite.spritecollide(self.player, self.platforms, False) if hits: self.player.pos.y = hits[0].rect.top + 1 self.player.vel.y = 0 def events(self): for event in pg.event.get(): if event.type == pg.QUIT: if self.playing: self.playing = False self.running = false if event.type == pg.KEYDOWN: if event.key == pg.K_UP: self.player.jump() if event.key == pg.K_SPACE: self.shoot() if event.key == pg.K_RIGHT: self.shootRight = True if event.key == pg.K_LEFT: self.shootRight = False def draw(self): self.screen.fill((255, 255, 255)) self.all_sprites.draw(self.screen) pg.display.flip() def show_start_screen(self): pass def show_go_screen(self): pass g = Game() g.show_start_screen() while g.running: g.new() g.show_go_screen() pg.quit() """width = 800 height = 600 FPS = 60 pg.init() pg.mixer.init() screen = pg.display.set_mode((width, height)) pg.display.set_caption("doom room") clock = pg.time.Clock() running = True while running: for event in pg.event.get(): clock.tick(FPS) if event.type == pg.QUIT: running = false screen.fill((255, 255, 255)) pg.display.flip() pg.quit()""" import pygame class Bullet(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) self.image = pygame.Surface((20, 10)) self.image.fill((240, 43, 12)) self.rect = self.image.get_rect() self.rect.bottom = y self.rect.centerx = x self.speed = -10 def update(self): self.rect.x += self.speed if self.rect.bottom < 0: self.kill() player.py import pygame as pg from settings import * from laser import * vec = pg.math.Vector2 class Player(pg.sprite.Sprite): def __init__(self): pg.sprite.Sprite.__init__(self) self.image = pg.Surface((40, 40)) self.image.fill((80, 123, 255)) 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.vx = 0 #self.vy = 0 def jump(self): self.vel.y = -15 def update(self): self.acc = vec(0, player_gravity) keys = pg.key.get_pressed() if keys[pg.K_LEFT]: self.acc.x = -player_acc if keys[pg.K_RIGHT]: self.acc.x = player_acc self.acc.x += self.vel.x * player_friction self.vel += self.acc self.pos += self.vel + 0.5 * self.acc if self.pos.x > width: self.pos.x = 0 if self.pos.x < 0: self.pos.x = width if self.pos.y <= 0: self.pos.y += 15 self.rect.midbottom = self.pos enemy.py import pygame as pg from random import * from settings import * class FlyingEnemy(pg.sprite.Sprite): def __init__(self, game): pg.sprite.Sprite.__init__(self) self.game = game self.image = pg.Surface((45, 45)) self.image.fill((20, 203, 50)) self.rect = self.image.get_rect() self.rect.centerx = choice([-100, width + 100]) self.vx = randrange(4, 7) if self.rect.centerx > width: self.vx *= -1 self.rect.y = height / 4 self.rect.x = 0 self.vy = 0 self.dy = 0.5 def update(self): if self.rect.x > width: self.rect.x = 0 if self.rect.x < 0: self.rect.x = width self.rect.x += self.vx self.vy += self.dy if self.vy > 3 or self.vy < -3: self.dy *= -1 center = self.rect.center if self.dy < 0: pass #print("bobbed up") else: pass #print("bobbed down") settings.py import pygame blockList = [] player_acc = 1.0 player_friction = -0.12 player_gravity = 0.5 bullets = pygame.sprite.Group() true = True false = False width = 800 height = 600 FPS = 60 levels.py level1 = { "platform": [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1], [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0], [0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0], [0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0], [0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0], [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], ], "enemies": 5 } block.py import pygame as pg class Block(pg.sprite.Sprite): def __init__(self, x, y): pg.sprite.Sprite.__init__(self) self.image = pg.Surface((32, 32)) self.image.fill((0, 0, 0)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y Thanks all help is appreciated.
This part of the loadLevel method causes the problem: for amount in range(0, enemies): group2.add(FlyingEnemy(self)) group3.add(FlyingEnemy(self)) self.enemies.add(FlyingEnemy(self)) self.enemiesList.append(FlyingEnemy(self)) You're adding 4 different FlyingEnemy objects to these groups and the list (btw, the list is useless), so the sprites in the self.all_sprites group and in the self.enemies group are not the same. Since you're only updating the all_sprites and not the enemies, the sprites in the enemies group, which are used for the collision detection, stay at the left screen edge all the time and are also invisible, because you don't draw this group. To solve the problem, create one instance and add this instance to the two groups: for amount in range(0, enemies): enemy = FlyingEnemy(self) self.enemies.add(enemy) self.all_sprites.add(enemy) I found the bug by printing the rect of one enemy sprite in the self.enemies group. Then I checked the update method of this sprite, but it looked correct, so I went to the instantiation part in loadLevel and noticed the mistake.