I am trying to make a spaceship game, however, when I added the part which allows you to fire bullets, the game crashed immediately crashed when I run the game.
Here is my current code (Some parts omitted or replaced by --snip--)
class Spaceship(pygame.sprite.Sprite):
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x, self.y = x, y
self.image = pygame.image.load("C:/eqodqfe/spaceship.png")
self.image = pygame.transform.scale(self.image, (175, 175))
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
self.rect.center = (self.x, self.y)
class Bullet(pygame.sprite.Sprite):
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x, self.y = x, y
self.image = pygame.image.load("C:/eqodqfe/bullet.png")
self.image = pygame.transform.scale(self.image, (100, 100))
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
self.rect.center = (self.x, self.y)
def fire(self):
while self.y <= 490:
self.y -= 5
spaceship = Spaceship(screen, 400, 400)
bullet = Bullet(screen, 0, 0)
running = True
while running:
--snip--
key = pygame.key.get_pressed()
if key[pygame.K_a]:
spaceship.x -= 0.5
elif key[pygame.K_d]:
spaceship.x += 0.5
--snip--
spaceship.update()
bullet.update()
screen.fill((255, 255, 255))
screen.blit(spaceship.image, spaceship.rect)
screen.blit(bullet.image, bullet.rect)
bullet.x = spaceship.x
bullet.y = spaceship.y-20
if pygame.mouse.get_pressed():
bullet.fire()
pygame.display.update()
Does anybody know what went wrong?
What do you expect by
while self.y <= 490:
self.y -= 5
This is an infinite loop.
To make a bullet you have to create an instance of Bullet when mouse button is pressed. However you have to use the MOSUEBUTTONDOWN event. pygame.mouse.get_pressed() returns a list of Boolean values that represent the state (True or False) of all mouse buttons. The state of a button is True as long as a button is held down. The MOUSEBUTTONDOWN event occurs once when you click the mouse button and the MOUSEBUTTONUP event occurs once when the mouse button is released.
Move an kill the bullet in update. update is continuously invoked in the application loop:
class Bullet(pygame.sprite.Sprite):
# [...]
def update(self):
self.y -= 5
self.rect.center = (self.x, self.y)
if self.y < 0:
self.kill()
Create a Bullet when the MOSUEBUTTONDOWN occurs:
bullets = pygame.sprite.Group()
running = True
while running:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == MOSUEBUTTONDOWN
bullet = Bullet(screen, spaceship.x, spaceship.y-20)
bullets.add(bullet)
bullets.update()
# [...]
bullets.draw()
pygame.display.update()
See also:
How can i shoot a bullet with space bar?
How do I stop more than 1 bullet firing at once?
Pygame: problems with shooting in Space Invaders
Related
Heres my code
import pygame, os
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
win = pygame.display
d = win.set_mode((1200, 600))
class player:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
self.speed = 2
def draw(self):
pygame.draw.rect(d, (0, 0, 0), (self.x, self.y, self.width, self.height))
def move_left(self):
self.x -= self.speed
def move_right(self):
self.x += self.speed
class bullet:
def __init__(self):
self.radius = 10
self.speed = 20
def shoot(self):
x = p.x
y = p.y
self.shooting = True
while self.shooting:
d.fill((98, 98, 98))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
y -= self.speed
pygame.draw.circle(d, (255, 0, 0), (x, y), self.radius)
pygame.time.Clock().tick(100)
win.update()
if y <= 0:
self.shooting = False
b = bullet()
p = player(600, 500, 50, 30)
while True:
d.fill((98, 98, 98))
p.draw()
for event in pygame.event.get():
pass
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
b.shoot()
if event.key == pygame.K_LEFT:
p.move_left()
if event.key == pygame.K_RIGHT:
p.move_right()
win.update()
This is what i could come up with after a few Trial and errors but it is really uneffective. Firstly the player disappers when i press space bar. I guess this is obvious as i have a different loops for shooting and player but i dont know how to get around it and implement both shooting and player in the same loop.
The second probllem i am having is breaking the while self.shooting: loop. I tried breaking it when y reaches a certain point by doing this
if y <= 0:
self.shooting = False
but this dosent break. Instead, it restarts the loop all over again.
Another weird problem i am having is that everytime i move mouse(slightly fast) or press a bunch of buttons at once, it breaks the while self.shooting loop.
The general approach to firing bullets is to store the positions of the bullets in a list (bullet_list). When a bullet is fired, add the bullet's starting position ([start_x, start_y]) to the list. The starting position is the position of the object (player or enemy) that fires the bullet. Use a for-loop to iterate through all the bullets in the list. Move position of each individual bullet in the loop. Remove a bullet from the list that leaves the screen (bullet_list.remove(bullet_pos)). For this reason, a copy of the list (bullet_list[:]) must be run through (see How to remove items from a list while iterating?). Use another for-loop to blit the remaining bullets on the screen:
bullet_list = []
while run == True:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullet_list.append([start_x, start_y])
for bullet_pos in bullet_list[:]:
bullet_pos[0] += move_bullet_x
bullet_pos[1] += move_bullet_y
if not screen.get_rect().colliderect(bullet_image.get_rect(center = bullet_pos))
bullet_list.remove(bullet_pos)
# [...]
for bullet_pos in bullet_list[:]
screen.blit(bullet_image, bullet_image.get_rect(center = bullet_pos))
# [...]
See also Shoot bullet.
Please note, that class names should normally use the CapWords convention.
(See Style Guide for Python Code - Class names)
That means it has to be Player and Bullet rather than player and bullet
You have an application loop, so use it. All the objects are continuously updated and drawn in the main application loop, in each frame.
The class Bullet do not need any loop. The constructor has to have parameters for the position (x, y). Further it needs on method which changes the position and one which draws the bullet:
class Bullet:
def __init__(self, x, y):
self.radius = 10
self.speed = 10
self.x = x
self.y = y
def update(self):
self.y -= self.speed#
def draw(self):
pygame.draw.circle(d, (255, 0, 0), (self.x, self.y), self.radius)
Use a list of bullets. Create a new bullet when space is pressed. Move the bullets (update) in every frame an remove a bullet if it is out of the window. Draw the remaining bullets in every frame:
bullets = []
# [...]
while run:
for event in pygame.event.get():
# [...]
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullets.append(Bullet(p.x+p.width//2, p.y))
# [...]
for b in bullets:
b.update()
if b.y < 0:
bullets.remove(b)
# [...]
for b in bullets:
b.draw()
Furthermore use pygame.key.get_pressed() use to get the state of the keys in every frame and to update the position of the player:
while run:
# [...]
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
p.move_left()
if keys[pygame.K_RIGHT]:
p.move_right()
Complete example:
import pygame, os
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
win = pygame.display
d = win.set_mode((1200, 600))
clock = pygame.time.Clock()
class Player:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
self.speed = 2
def draw(self):
pygame.draw.rect(d, (0, 0, 0), (self.x, self.y, self.width, self.height))
def move_left(self):
self.x -= self.speed
def move_right(self):
self.x += self.speed
class Bullet:
def __init__(self, x, y):
self.radius = 10
self.speed = 10
self.x = x
self.y = y
def update(self):
self.y -= self.speed#
def draw(self):
pygame.draw.circle(d, (255, 0, 0), (self.x, self.y), self.radius)
bullets = []
p = Player(600, 500, 50, 30)
run = True
while run:
clock.tick(100)
# handel events
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullets.append(Bullet(p.x+p.width//2, p.y))
# update objects
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
p.move_left()
if keys[pygame.K_RIGHT]:
p.move_right()
for b in bullets:
b.update()
if b.y < 0:
bullets.remove(b)
# clear display
d.fill((98, 98, 98))
# draw scene
for b in bullets:
b.draw()
p.draw()
# update display
win.update()
Heres my code
import pygame, os
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
win = pygame.display
d = win.set_mode((1200, 600))
class player:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
self.speed = 2
def draw(self):
pygame.draw.rect(d, (0, 0, 0), (self.x, self.y, self.width, self.height))
def move_left(self):
self.x -= self.speed
def move_right(self):
self.x += self.speed
class bullet:
def __init__(self):
self.radius = 10
self.speed = 20
def shoot(self):
x = p.x
y = p.y
self.shooting = True
while self.shooting:
d.fill((98, 98, 98))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
y -= self.speed
pygame.draw.circle(d, (255, 0, 0), (x, y), self.radius)
pygame.time.Clock().tick(100)
win.update()
if y <= 0:
self.shooting = False
b = bullet()
p = player(600, 500, 50, 30)
while True:
d.fill((98, 98, 98))
p.draw()
for event in pygame.event.get():
pass
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
b.shoot()
if event.key == pygame.K_LEFT:
p.move_left()
if event.key == pygame.K_RIGHT:
p.move_right()
win.update()
This is what i could come up with after a few Trial and errors but it is really uneffective. Firstly the player disappers when i press space bar. I guess this is obvious as i have a different loops for shooting and player but i dont know how to get around it and implement both shooting and player in the same loop.
The second probllem i am having is breaking the while self.shooting: loop. I tried breaking it when y reaches a certain point by doing this
if y <= 0:
self.shooting = False
but this dosent break. Instead, it restarts the loop all over again.
Another weird problem i am having is that everytime i move mouse(slightly fast) or press a bunch of buttons at once, it breaks the while self.shooting loop.
The general approach to firing bullets is to store the positions of the bullets in a list (bullet_list). When a bullet is fired, add the bullet's starting position ([start_x, start_y]) to the list. The starting position is the position of the object (player or enemy) that fires the bullet. Use a for-loop to iterate through all the bullets in the list. Move position of each individual bullet in the loop. Remove a bullet from the list that leaves the screen (bullet_list.remove(bullet_pos)). For this reason, a copy of the list (bullet_list[:]) must be run through (see How to remove items from a list while iterating?). Use another for-loop to blit the remaining bullets on the screen:
bullet_list = []
while run == True:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullet_list.append([start_x, start_y])
for bullet_pos in bullet_list[:]:
bullet_pos[0] += move_bullet_x
bullet_pos[1] += move_bullet_y
if not screen.get_rect().colliderect(bullet_image.get_rect(center = bullet_pos))
bullet_list.remove(bullet_pos)
# [...]
for bullet_pos in bullet_list[:]
screen.blit(bullet_image, bullet_image.get_rect(center = bullet_pos))
# [...]
See also Shoot bullet.
Please note, that class names should normally use the CapWords convention.
(See Style Guide for Python Code - Class names)
That means it has to be Player and Bullet rather than player and bullet
You have an application loop, so use it. All the objects are continuously updated and drawn in the main application loop, in each frame.
The class Bullet do not need any loop. The constructor has to have parameters for the position (x, y). Further it needs on method which changes the position and one which draws the bullet:
class Bullet:
def __init__(self, x, y):
self.radius = 10
self.speed = 10
self.x = x
self.y = y
def update(self):
self.y -= self.speed#
def draw(self):
pygame.draw.circle(d, (255, 0, 0), (self.x, self.y), self.radius)
Use a list of bullets. Create a new bullet when space is pressed. Move the bullets (update) in every frame an remove a bullet if it is out of the window. Draw the remaining bullets in every frame:
bullets = []
# [...]
while run:
for event in pygame.event.get():
# [...]
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullets.append(Bullet(p.x+p.width//2, p.y))
# [...]
for b in bullets:
b.update()
if b.y < 0:
bullets.remove(b)
# [...]
for b in bullets:
b.draw()
Furthermore use pygame.key.get_pressed() use to get the state of the keys in every frame and to update the position of the player:
while run:
# [...]
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
p.move_left()
if keys[pygame.K_RIGHT]:
p.move_right()
Complete example:
import pygame, os
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
win = pygame.display
d = win.set_mode((1200, 600))
clock = pygame.time.Clock()
class Player:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
self.speed = 2
def draw(self):
pygame.draw.rect(d, (0, 0, 0), (self.x, self.y, self.width, self.height))
def move_left(self):
self.x -= self.speed
def move_right(self):
self.x += self.speed
class Bullet:
def __init__(self, x, y):
self.radius = 10
self.speed = 10
self.x = x
self.y = y
def update(self):
self.y -= self.speed#
def draw(self):
pygame.draw.circle(d, (255, 0, 0), (self.x, self.y), self.radius)
bullets = []
p = Player(600, 500, 50, 30)
run = True
while run:
clock.tick(100)
# handel events
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullets.append(Bullet(p.x+p.width//2, p.y))
# update objects
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
p.move_left()
if keys[pygame.K_RIGHT]:
p.move_right()
for b in bullets:
b.update()
if b.y < 0:
bullets.remove(b)
# clear display
d.fill((98, 98, 98))
# draw scene
for b in bullets:
b.draw()
p.draw()
# update display
win.update()
I simply just want to delete the zombie sprite from my sprite group when the bullet collides with the zombie to create the sense the zombie is dying.
I already tried the Zombie(1,1).kill() but it did not work unfortunatley.
here is my code if it will be helpful...
import pygame
from sys import exit
from random import randint
import math
from pygame.math import Vector2
from pygame.constants import K_LSHIFT, K_SPACE, MOUSEBUTTONDOWN, MOUSEBUTTONUP, K_e
pygame.init()
import time
pygame.mouse.set_visible(True)
class Player(pygame.sprite.Sprite):
def __init__(self, x , y):
super().__init__()
self.x = x
self.y = y
self.image = pygame.image.load('graphics/Robot 1/robot1_gun.png').convert_alpha()
self.rect = self.image.get_rect()
self.orig_image = pygame.image.load('graphics/Robot 1/robot1_gun.png').convert_alpha()
self.rotate_vel = 1
self.cross_image = pygame.image.load('graphics/crosshair049.png')
def draw(self, surface):
""" Draw on surface """
# blit yourself at your current position
surface.blit(self.image, self.rect)
dir_vec = pygame.math.Vector2()
dir_vec.from_polar((180, -self.rotate_vel))
cross_pos = dir_vec + self.rect.center
cross_x, cross_y = round(cross_pos.x), round(cross_pos.y)
surface.blit(self.cross_image, self.cross_image.get_rect(center = (cross_x, cross_y)))
def movement(self):
key = pygame.key.get_pressed()
self.rect = self.image.get_rect(center = (self.x, self.y))
dist = 3 # distance moved in 1 frame, try changing it to 5
if key[pygame.K_DOWN] or key[pygame.K_s]: # down key
self.y += dist # move down
elif key[pygame.K_UP] or key[pygame.K_w]: # up key
self.y -= dist # move up
if key[pygame.K_RIGHT] or key[pygame.K_d]: # right key
self.x += dist # move right
elif key[pygame.K_LEFT] or key[pygame.K_a]: # left key
self.x -= dist # move left
def rotate(self, surface):
keys = pygame.key.get_pressed()
if keys[K_LSHIFT]:
self.rotate_vel += 5
self.image = pygame.transform.rotate(self.orig_image, self.rotate_vel)
self.rect = self.image.get_rect(center=self.rect.center)
surface.blit(self.image, self.rect)
if keys[K_SPACE]:
self.rotate_vel += -5
self.image = pygame.transform.rotate(self.orig_image, self.rotate_vel)
self.rect = self.image.get_rect(center=self.rect.center)
surface.blit(self.image, self.rect)
def update(self):
self.movement()
self.draw(screen)
self.rotate(screen)
class Bullet(pygame.sprite.Sprite):
def __init__(self, pos, angle):
super().__init__()
self.image = pygame.image.load('graphics/weapons/bullets/default_bullet.png')
self.image = pygame.transform.rotate(self.image, angle)
self.rect = self.image.get_rect(center = pos)
self.speed = 25
self.pos = pos
self.dir_vec = pygame.math.Vector2()
self.dir_vec.from_polar((self.speed, -angle))
def update(self, screen):
self.pos += self.dir_vec
self.rect.center = round(self.pos.x), round(self.pos.y)
class Zombie(pygame.sprite.Sprite):
def __init__(self, x , y):
super().__init__()
self.x = x
self.y = y
self.image = pygame.image.load('graphics/zombie/zoimbie1_hold.png').convert_alpha()
self.rect = self.image.get_rect(center = (x,y))
self.orig_image = pygame.image.load('graphics/Robot 1/robot1_gun.png').convert_alpha()
def draw(self, surface):
surface.blit(self.image, self.rect)
def update(self):
self.draw(screen)
#screen
clock = pygame.time.Clock()
FPS = 60
screen = pygame.display.set_mode((1200, 600))
#player
player_sprite = Player(600, 300)
player = pygame.sprite.GroupSingle()
player.add(player_sprite)
#bullet
bullet_group = pygame.sprite.Group()
#Zombie
zombie_sprite = Zombie(600, 300)
zombie = pygame.sprite.Group()
zombie.add(zombie_sprite)
#keys
keys = pygame.key.get_pressed()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
pos = player_sprite.rect.center
new_bullet = Bullet(pos, player_sprite.rotate_vel)
bullet_group.add(new_bullet)
if pygame.sprite.spritecollide(zombie_sprite,bullet_group , True):
Zombie(1,1).kill()
bullet_group.update(screen)
#screen
screen.fill('grey')
#player sprite funtions
player.update()
#buller group draw
bullet_group.draw(screen)
bullet_group.update(screen)
#Zombie update
zombie.update()
clock.tick(FPS)
pygame.display.update()
Try zombie_sprite.kill() instead of Zombie(1,1).kill()
You are instantiating a new object instead of destroying the original one.
I am making a game for my school project where you just go up and down shooting at enemies. I have made the enemy however he is not appearing on the screen. There are no errors and everything else works perfectly fine, the enemy is also meant to go up and down the y-axis, so only up and down. Here is my code:
import pygame, sys
# classes
# Player class
class Player(pygame.sprite.Sprite):
def __init__(self, image):
super().__init__()
self.image = image
self.rect = self.image.get_rect(center = (screen_width//2, screen_height//2))
def update(self):
self.rect.center = pygame.mouse.get_pos()
def create_bullet(self):
return Bullet(*pygame.mouse.get_pos())
# bullet class
class Bullet (pygame.sprite.Sprite):
def __init__(self,pos_x,pos_y):
super().__init__()
self.image = pygame.Surface((50,10))
self.image.fill((255,255,0))
self.rect = self.image.get_rect(center = (pos_x,pos_y))
def update(self):
self.rect.x += 5
# enemy class
class enemy(pygame.sprite.Sprite):
walkup = pygame.image.load("enemy.png")
walkdown = pygame.image.load("enemy.png")
def __init__(self, x, y, width, height, end):
self.x = x
self.y = y
self.width = width
self.height = height
self.end = end
self.path = [self.y, self.end]
self.walkcount = 0
self.vel = 3
def draw(self,win):
self.move()
if self.walkcount + 1 <= 33:
self.walkcount = 0
if self.vel > 0:
win.blit(self.walkup[self.walkcount //3], (self.x, self.y))
self.walkcount += 1
else:
win.blit(self.walkdown [self.walkcount //3], (self.x, self.y) )
self.walkcount += 1
def move(self):
if self.vel >0:
if self.x + self.vel < self.path[1]:
self.x += self.vel
else:
self.vel - self.vel * -1
self.walkcount - 0
else:
if self.y - self.vel > self.path[0]:
self.x += self.vel
else:
self.vel = self.vel * -1
self.walkcount = 0
# making the enemy appear
def redrawGameWindow():
thief.draw(screen)
thief = enemy(100, 410, 64, 64, 450)
# general setup
pygame.init()
clock = pygame.time.Clock()
# game screen
screen_width = 1920
screen_height = 1080
screen = pygame .display.set_mode((screen_width, screen_height))
background = pygame.image.load("BackGround.png")
# player
player_image = pygame.image.load("Charachter2.png")
player = Player(player_image)
player_group = pygame.sprite.Group()
player_group.add(player)
# Bullet
bullet_group = pygame.sprite.Group()
# caption
pygame.display.set_caption("Wild-West Shooter")
# makes game quit
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame:quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
bullet_group.add(player.create_bullet())
player_group.update()
bullet_group.update()
screen.blit(background,(0,0))
player_group.draw(screen)
bullet_group.draw(screen)
pygame.display.flip()
clock.tick(120)
You can draw the thief in the while loop by adding thief.draw(screen) like so:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame:quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
bullet_group.add(player.create_bullet())
player_group.update()
bullet_group.update()
screen.blit(background,(0,0))
player_group.draw(screen)
bullet_group.draw(screen)
thief.draw(screen)
pygame.display.flip()
clock.tick(120)
Then try removing the array-notation in your Enemy class draw method. The code tries to use array-notation when calling blit with: win.blit(self.walkup[self.walkcount //3], (self.x, self.y)) so use something like this instead:
def draw(self,win):
self.move()
if self.walkcount + 1 <= 33:
self.walkcount = 0
if self.vel > 0:
win.blit(self.walkup, (self.x, self.y))
self.walkcount += 1
else:
win.blit(self.walkdown, (self.x, self.y) )
self.walkcount += 1
The redrawGameWindow function isn't called.
The while loop qhould look like that:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame:quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
bullet_group.add(player.create_bullet())
player_group.update()
bullet_group.update()
# draw:
screen.blit(background,(0,0)) # background
player_group.draw(screen) # player
bullet_group.draw(screen) # bullets
redrawGameWindow() # enemy
pygame.display.flip()
clock.tick(120)
(Off-topic) You should also rename the redrawGameWindow function (it doesn't really redraw the window, does it?), or even delete it (a function containing only one line of code should be removed and its content is called instead)
Also, if you want the redrawGameWindow to actually redraw the window, you can put all of the code which draws onto the screen in this function like that:
..........
def redrawGameWindow(): # to call this function will redraw...
player_group.update()
bullet_group.update()
screen.blit(background,(0,0)) # the background,
player_group.draw(screen) # the player,
bullet_group.draw(screen) # the bullets
thief.draw(screen) # and finally the enemy
pygame.display.flip()
thief = enemy(100, 410, 64, 64, 450)
# general setup
pygame.init()
clock = pygame.time.Clock()
..........
# caption
pygame.display.set_caption("Wild-West Shooter")
# makes game quit
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame:quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
bullet_group.add(player.create_bullet())
redrawGameWindow() # redraw all
clock.tick(120)
I'm quite new to pygame and came across a bug that i just can't fix on my own. I'm trying to program a Flappy Bird game. The Problem is that the collision detection works, but it also messes with my sprites. If i manage to get past the first obstacle while playing, then the gap resets itself randomly. But the gap should always be the same, just on another position. If i remove the collision detection, it works perfectly fine. Any ideas?
import pygame
import random
randomy = random.randint(-150, 150)
class Bird(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((25,25))
self.image.fill((255,255,255))
self.rect = self.image.get_rect()
self.rect.center = (100, 200)
self.velocity = 0.05
self.acceleration =0.4
def update(self):
self.rect.y += self.velocity
self.velocity += self.acceleration
if self.rect.bottom > 590:
self.velocity = 0
self.acceleration = 0
class Pipe1(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((85, 500))
self.image.fill((255, 255, 255))
self.rect = self.image.get_rect()
self.rect.center = (500, randomy)
self.randomyupdate = random.randint(-150, 150)
def update(self):
self.rect.x -= 2
if self.rect.x < -90:
self.randomyupdate = random.randint(-150, 150)
self.rect.center = (450, self.randomyupdate)
class Pipe2(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((85, 500))
self.image.fill((255, 255, 255))
self.rect = self.image.get_rect()
self.rect.center = (500, (randomy +640))
def update(self):
self.rect.x -= 2
self.randomyupdate = Pipe1.randomyupdate
if self.rect.x < -90:
self.rect.center = (450, (self.randomyupdate + 640))
pygame.init()
pygame.mouse.set_visible(1)
pygame.key.set_repeat(1, 30)
pygame.display.set_caption('Crappy Bird')
clock = pygame.time.Clock()
Bird_sprite = pygame.sprite.Group()
Pipe_sprite = pygame.sprite.Group()
Bird = Bird()
Pipe1 = Pipe1()
Pipe2 = Pipe2 ()
Bird_sprite.add(Bird)
Pipe_sprite.add(Pipe1)
Pipe_sprite.add(Pipe2)
def main():
running = True
while running:
clock.tick(60)
screen = pygame.display.set_mode((400,600))
screen.fill((0,0,0))
Bird_sprite.update()
Pipe_sprite.update()
Bird_sprite.draw(screen)
Pipe_sprite.draw(screen)
The line im talking about:
collide = pygame.sprite.spritecollideany(Bird, Pipe_sprite)
if collide:
running = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.event.post(pygame.event.Event(pygame.QUIT))
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
Bird.rect.y -= 85
Bird.velocity = 0.05
Bird.acceleration = 0.4
Bird.rect.y += Bird.velocity
Bird.velocity += Bird.acceleration
pygame.display.flip()
if __name__ == '__main__':
main()
This has nothing to do with the collision detection, it has to do with the order of the sprites in the sprite group which can vary because sprite groups use dictionaries internally which are unordered (in Python versions < 3.6). So if the Pipe1 sprite comes first in the group, the game will work correctly, but if the Pipe2 sprite comes first, then its update method is also called first and the previous randomyupdate of Pipe1 is used to set the new centery coordinate of the sprite.
To fix this you could either turn the sprite group into an ordered group, e.g.
Pipe_sprite = pygame.sprite.OrderedUpdates()
or update the rect of Pipe2 each frame,
def update(self):
self.rect.x -= 2
self.rect.centery = Pipe1.randomyupdate + 640
if self.rect.x < -90:
self.rect.center = (450, Pipe1.randomyupdate + 640)
Also, remove the global randomy variable and always use the randomyupdate attribute of Pipe1.