I am making a game and when the sprite "Bomb" goes under 450 on the y axis, it is supposed to be deleted.
import pygame
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREY = (129, 129, 129)
class SpriteSheet(object):
def __init__(self, file_name):
self.sprite_sheet = pygame.image.load(file_name).convert()
def get_image(self, x, y, width, height, colour):
image = pygame.Surface([width, height]).convert()
image.set_colorkey(colour)
image.blit(self.sprite_sheet, (0, 0), (x, y, width, height))
return image
class Bomb(pygame.sprite.Sprite):
change_x =0
change_y = 0
def __init__(self):
super().__init__()
sprite_sheet = SpriteSheet("Untitled.png")
self.image = sprite_sheet.get_image(2, 2, 48, 48, WHITE)
self.rect = self.image.get_rect()
def move(self, y):
self.rect.y += y
if self.rect.y > 450:
self.kill()
print (self.rect.y)
pygame.init()
screen_width = 1000
screen_height = 500
screen = pygame.display.set_mode([screen_width, screen_height])
pygame.display.set_caption("Game")
clock = pygame.time.Clock()
done = False
bomb = Bomb()
all_sprites = pygame.sprite.Group()
all_sprites.add(bomb)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(WHITE)
all_sprites.draw(screen)
bomb.move(2)
clock.tick(60)
pygame.display.flip()
pygame.quit()
(Note that I'm printing the y axis of the bomb to check if it's deleted or not.) I tried to use the "kill()" method, but what happens is, it does disappear, but the y axis continues to print, which I think is not supposed to happen.
I tried to use the kill() method in another piece of code. It gets deleted and the y axis stops printing. The only difference between the 2 codes is that in this one, I am blitting an image onto the screen to use as a sprite, and in that one, I am using "Rect" to make the sprite.
Can someone please guide me on what else can I do to permanently remove the sprite from the memory, or is it okay if the values of the y axis are printing all the time (the sprite should be deleted)?
The kill() method does not magically remove objects from memory. All it does is removing a Sprite from all it's Groups.
So in your code, the bomb is no longer displayed because it's no longer in all_sprites after kill() is called. But you still have a reference to the object (bomb), and you still call the move function every frame on the object. So the print statement is executed.
What do you expect to happen to your bomb variable after you called kill()?
Usually, you should implement all sprite logic in a method called update, and not call this method directly, but via the group, too, like the draw method.
Something like this:
class Bomb(pygame.sprite.Sprite):
change_x =0
change_y = 0
def __init__(self):
super().__init__()
sprite_sheet = SpriteSheet("Untitled.png")
self.image = sprite_sheet.get_image(2, 2, 48, 48, WHITE)
self.rect = self.image.get_rect()
self.speed = 2
def update(self, y):
self.rect.y += self.speed
if self.rect.y > 450:
self.kill()
print (self.rect.y)
...
all_sprites = pygame.sprite.Group()
all_sprites.add(Bomb())
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(WHITE)
all_sprites.draw(screen)
all_sprites.update()
clock.tick(60)
pygame.display.flip()
pygame.quit()
This way, after kill() is called, no references to the object remain (and the python runtime will remove it from memory eventually).
Related
I am following a video about pygame and I saw this code
crosshair = pygame.sprite.Group()
Could someone explain me this?
Read the documentation of pygame.sprite.Group.
pygame.sprite.Group.update() and pygame.sprite.Group.draw() are methods which are provided by pygame.sprite.Group.
The former delegates the to the update method of the contained pygame.sprite.Sprites - you have to implement the method.
pygame.sprite.Group.update()
Calls the update() method on all Sprites in the Group.
The later uses the image and rect attributes of the contained pygame.sprite.Sprites to draw the objects - you have to ensure that the pygame.sprite.Sprites have the required attributes
pygame.sprite.Group.draw()
Draws the contained Sprites to the Surface argument. This uses the Sprite.image attribute for the source surface, and Sprite.rect for the position.
The Sprites in the Groups can be removed and thus destroyed by calling pygame.sprite.Sprite.kill. When the object is no longer referenced, it is destroyed:
The Sprite is removed from all the Groups that contain it. This won't change anything about the state of the Sprite. It is possible to continue to use the Sprite after this method has been called, including adding it to Groups.
See also Sprite Groups
Minimal example:
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self, center_pos):
super().__init__()
self.image = pygame.Surface((40, 40))
self.image.fill((255, 255, 0))
self.rect = self.image.get_rect(center = center_pos)
class Bullet(pygame.sprite.Sprite):
def __init__(self, center_pos):
super().__init__()
self.image = pygame.Surface((20, 10))
self.image.fill((0, 255, 255))
self.rect = self.image.get_rect(center = center_pos)
def update(self):
self.rect.x += 10
if self.rect.right > 300:
self.kill()
pygame.init()
window = pygame.display.set_mode((400, 300))
clock = pygame.time.Clock()
player = Player((25, window.get_height() // 2))
all_sprites = pygame.sprite.Group(player)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
all_sprites.add(Bullet(player.rect.center))
all_sprites.update()
print(len(all_sprites))
window.fill(0)
pygame.draw.rect(window, (255, 0, 0), (300, 0, 10, window.get_height()))
all_sprites.draw(window)
pygame.display.flip()
pygame.quit()
exit()
I am learning pygame by making a simple game.
Here is the code:
Main script:
import pygame
from gracz2 import SpriteGenerator
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
GREEN = ( 0, 255, 0)
RED = ( 255, 0, 0)
BLUE = ( 0, 0, 255)
pygame.init()
pygame.display.set_caption("Super Gra!")
screen_height = 720
screen_width = 1280
screen = pygame.display.set_mode((screen_width, screen_height))
all_sprites_list = pygame.sprite.Group()
playerSprite = SpriteGenerator(1,150,150)
playerSprite.rect.x = (screen_width/2 - 75)
playerSprite.rect.y = 550
all_sprites_list.add(playerSprite)
clock = pygame.time.Clock()
mainloop = True
playtime = 0
while mainloop:
for event in pygame.event.get():
# User presses QUIT-button.
if event.type == pygame.QUIT:
mainloop = False
elif event.type == pygame.KEYDOWN:
# User presses ESCAPE-Key
if event.key == pygame.K_ESCAPE:
mainloop = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
playerSprite.moveLeft(8)
if keys[pygame.K_RIGHT]:
playerSprite.moveRight(8)
milliseconds = clock.tick(60)
playtime += milliseconds / 1000.0
all_sprites_list.update()
pygame.display.set_caption("Czas gry: " + str(round(playtime,1)) + " sekund")
# Refresh the screen
screen.fill(BLACK)
all_sprites_list.draw(screen)
screen.blit(playerSprite.image, (playerSprite.rect.x,playerSprite.rect.y))
pygame.display.flip()
print(all_sprites_list.sprites())
print(all_sprites_list)
print(playerSprite.rect.x)
print(playerSprite.rect.y)
pygame.quit()
and another file called "gracz2.py":
import pygame
WHITE = (255, 255, 255)
BLACK = ( 0, 0, 0)
class SpriteGenerator(pygame.sprite.Sprite):
#This class represents a player. It derives from the "Sprite" class in Pygame.
def __init__(self, type, width, height):
# Call the parent class (Sprite) constructor
super().__init__()
# Pass in the color of the player, and its x and y position, width and height.
# Set the background color and set it to be transparent
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
if type == 1:
self.image = pygame.image.load("sprite-ufo.gif").convert_alpha()
elif type == 2:
self.image = pygame.image.load("sprite-bomb.jpg").convert_alpha()
# Fetch the rectangle object that has the dimensions of the image.
self.rect = self.image.get_rect()
def moveRight(self, pixels):
self.rect.x += pixels
def moveLeft(self, pixels):
self.rect.x -= pixels
It could be done in one file but the code is more readable for me this way.
around line 50 i call all_sprites_list.draw(screen)
which to my understanding should blit all the sprites contained in all_sprites_list but it does nothing.
I have to use screen.blit(playerSprite.image, (playerSprite.rect.x,playerSprite.rect.y))
to manually blit the sprite.
As i am going to add generate lots of sprites later i can't blit them all manually.
Why doesn't all_sprites_list.draw(screen) work as intended?
It's probably a stupid mistake in the code but I am trying to find for over an hour now and I am unable to locate it.
Turns out that when i restart my PC the draw() function works fine.
I don't know what caused it first, sorry for asking without following the first rule of IT troubleshooting first (restart and try again)
PS: thank you furas for your answers
I am making a flappy bird clone game, using pygame. I want to draw pillars by using Sprite.draw. I made a Pillar class and initialized it with two rectangles p_upper and p_lower on the left side of the screen, coming towards the right side with the help of the update function of the sprite. But the screen is only showing the p_lower pillar. Can anyone help?
class Pillar(pygame.sprite.Sprite):
# the "h" parameter is height of upper pillar upto gap
# "w" is the width of the pillar
# pillar is coming from left to right
def __init__(self, w, h, gap):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((w, h))
self.image.fill(green)
self.p_upper = self.rect = self.image.get_rect()
self.p_upper.topleft = (-w, 0)
self.image = pygame.Surface((w, HEIGHT - (h + gap)))
self.image.fill(green)
self.p_lower = self.rect = self.image.get_rect()
self.p_lower.topleft = (-w, h + gap)
def update(self):
self.p_upper.x += 1
self.p_lower.x += 1
Because of the following two lines:
self.p_upper = self.rect = self.image.get_rect()
and...
self.p_lower = self.rect = self.image.get_rect()
These are both grabbing the same reference to the self.rect. The first line runs and assigns the rect reference to p_upper. Then the same reference is assigned to p_lower. Because it's the same reference, when you update the location of the lower rectangle, you're actually updating both.
Using a sprite that consists of two rects and images isn't a good solution for this problem. I suggest to create two separate sprites with their own image and rect. To create two sprite instances at the same time and to add them to a sprite group, you can write a short function as you can see in this example:
import pygame as pg
from pygame.math import Vector2
green = pg.Color('green')
HEIGHT = 480
class Pillar(pg.sprite.Sprite):
def __init__(self, x, y, w, h):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((w, h))
self.image.fill(green)
self.rect = self.image.get_rect(topleft=(x, y))
def update(self):
self.rect.x += 1
def create_pillars(w, h, gap, sprite_group):
sprite_group.add(Pillar(0, 0, w, h-gap))
sprite_group.add(Pillar(0, HEIGHT-(h+gap), w, h+gap))
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
create_pillars(50, 170, 0, all_sprites)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_a:
create_pillars(50, 170, 15, all_sprites)
elif event.key == pg.K_s:
create_pillars(50, 170, 30, all_sprites)
elif event.key == pg.K_d:
create_pillars(50, 100, -60, all_sprites)
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
I'm working on my school project for which im designing a 2D game.
I have 3 images, one is the player and the other 2 are instances (coffee and computer). What i want to do is, when the player image collides with one of the 2 instances i want the program to print something.
I'm unsure if image collision is possible. But i know rect collision is possible. However, after several failed attempts, i can't manage to make my images rects. Somebody please help me. Here is my source code:
import pygame
import os
black=(0,0,0)
white=(255,255,255)
blue=(0,0,255)
class Player(object):
def __init__(self):
self.image = pygame.image.load("player1.png")
self.image2 = pygame.transform.flip(self.image, True, False)
self.coffee=pygame.image.load("coffee.png")
self.computer=pygame.image.load("computer.png")
self.flipped = False
self.x = 0
self.y = 0
def handle_keys(self):
""" Movement keys """
key = pygame.key.get_pressed()
dist = 5
if key[pygame.K_DOWN]:
self.y += dist
elif key[pygame.K_UP]:
self.y -= dist
if key[pygame.K_RIGHT]:
self.x += dist
self.flipped = False
elif key[pygame.K_LEFT]:
self.x -= dist
self.flipped = True
def draw(self, surface):
if self.flipped:
image = self.image2
else:
im = self.image
for x in range(0, 810, 10):
pygame.draw.rect(screen, black, [x, 0, 10, 10])
pygame.draw.rect(screen, black, [x, 610, 10, 10])
for x in range(0, 610, 10):
pygame.draw.rect(screen, black, [0, x, 10, 10])
pygame.draw.rect(screen, black, [810, x, 10, 10])
surface.blit(self.coffee, (725,500))
surface.blit(self.computer,(15,500))
surface.blit(im, (self.x, self.y))
pygame.init()
screen = pygame.display.set_mode((800, 600))#creates the screen
player = Player()
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit() # quit the screen
running = False
player.handle_keys() # movement keys
screen.fill((255,255,255)) # fill the screen with white
player.draw(screen) # draw the player to the screen
pygame.display.update() # update the screen
clock.tick(60) # Limits Frames Per Second to 60 or less
Use pygame.Rect() to keep image size and position.
Image (or rather pygame.Surface()) has function get_rect() which returns pygame.Rect() with image size (and position).
self.rect = self.image.get_rect()
Now you can set start position ie. (0, 0)
self.rect.x = 0
self.rect.y = 0
# or
self.rect.topleft = (0, 0)
# or
self.rect = self.image.get_rect(x=0, y=0)
(Rect use left top corner as (x,y)).
Use it to change position
self.rect.x += dist
and to draw image
surface.blit(self.image, self.rect)
and then you can test collision
if self.rect.colliderect(self.rect_coffe):
BTW: and now class Player looks almost like pygame.sprite.Sprite :)
For a while now I've been trying to teach myself how to use sprites in pygame and right now I am stuck on collision detection.
The specific place I am having trouble with in my code is the commented section labeled "error here" and that's the code that keeps giving me the "TypeError: Argument must be rect style object" error and the goal of that specific code is to detect collision.
The goal of this code is to print a message in the shell whenever the player block enters the non-player block and as I said earlier I have been having trouble getting that to happen.
from pygame.locals import *
import pygame
pygame.init()
SIZE = WIDTH, HEIGHT = 500, 700
screen = pygame.display.set_mode(SIZE)
plr_g = pygame.sprite.Group()
h_box = pygame.sprite.Group()
BLUE = (0, 206, 209)
GREEN = (0, 255, 0)
class Player(pygame.sprite.Sprite):
def __init__(self, width, height):
pygame.sprite.Sprite.__init__(self, plr_g)
self.image = pygame.Surface([width, height])
self.image.fill(BLUE)
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
#self.rect = (400, 200)
def x_pos(self):
self.rect.x
def y_pos(self):
self.rect.y
def move_l(self, pixels):
self.rect.x -= pixels
def move_r(self, pixels):
self.rect.x += pixels
def move_u(self, pixels):
self.rect.y -= pixels
def move_d(self, pixels):
self.rect.y += pixels
class Hitbox(pygame.sprite.Sprite):
def __init__(self, bx, by):
pygame.sprite.Sprite.__init__(self, h_box)
self.image = pygame.Surface([100, 100])
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect = (bx, by)
hitbox = Hitbox(300, 300)
hitbox = Hitbox(100, 500)
player = Player(50, 50)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_LEFT:
player.move_l(10)
if event.key==pygame.K_RIGHT:
player.move_r(10)
if event.key==pygame.K_UP:
player.move_u(10)
if event.key==pygame.K_DOWN:
player.move_d(10)
#error here
if plr_g.colliderect(h_box):
print("collide")
#----------------
plr_g.update()
h_box.update()
screen.fill((50, 50, 50))
h_box.draw(screen)
plr_g.draw(screen)
pygame.display.flip()
h_box is a sprite group, not a sprite and definitely not a rect. The collide_rect function of sprites must be called on individual sprites. A possible solution is something like the following, iterating over all the sprites in h_box:
if any([plr_g.colliderect(sp) for sp in h_box]):
print("collide")
block_hit_list = plr_g.colliderect(h_box)
for block in block_hit_list:
print ("collide")
I basically loop through the list to see if there are any collisions between the sprites in plr_g and the sprites in hitbox.