How to draw shapes using the grouping thing - python

I just learned how to group stuff in pygame but when I try to use it to draw a sprite it says: " ExternalError: TypeError: Cannot read property 'left' of undefined on line 32"
Here is my code:
import pygame
from pygame import *
screen = pygame.display.set_mode((600, 600))
blocks = pygame.sprite.Group()
class Block():
def __init__(self, x, y, w, h, typ):
super().__init__()
self.x = x
self.y = y
self.w = w
self.h = h
self.typ = typ
def draw(self):
pygame.draw.rect(screen, (0, 0, 0), (self.x, self.y, self.w, self.h))
class Player():
def __init__(self, x, y):
super().__init__()
self.x = x
self.y = y
self.accX = 0
self.accY = 0
def draw(self):
pygame.draw.rect(screen, (0, 0, 0), (self.x, self.y, 20, 20))
player = Player(0, 0)
while True:
Blocks = Block(0, 0, 100, 50, 1)
blocks.add(Blocks)
blocks.draw(screen)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
pygame.display.update()

pygame.sprite.Group.draw() and pygame.sprite.Group.update() 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. See 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. See pygame.sprite.Group.draw():
Draws the contained Sprites to the Surface argument. This uses the Sprite.image attribute for the source surface, and Sprite.rect. [...]
This means Block and Player must to be derived from pygame.sprite.Sprite and has to have the attributes rect and image. Add the Block and Player instances to a Group and us the darw() method to draw them.
Complete example:
import pygame
from pygame import *
screen = pygame.display.set_mode((600, 600))
class Block(pygame.sprite.Sprite):
def __init__(self, x, y, w, h, typ):
super().__init__()
self.image = pygame.Surface((w, h))
self.image.fill((127, 127, 127))
self.rect = self.image.get_rect(topleft = (x, y))
class Player(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((20, 20))
self.image.fill((255, 255, 0))
self.rect = self.image.get_rect(topleft = (x, y))
blocks = pygame.sprite.Group()
objects = pygame.sprite.Group()
block = Block(0, 0, 100, 50, 1)
blocks.add(block)
player = Player(0, 0)
objects.add(player)
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.fill((0, 0, 0))
blocks.draw(screen)
objects.draw(screen)
pygame.display.update()
pygame.quit()

Related

how to create my games spikes like pipes in flappy bird [duplicate]

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()

How do you program collision in classes?

I've been having a hard time getting collision to work. I'm new and don't know the in and outs with coding so please can you explain your reasoning.
Here's a simple of some code:
def __init__(self, x, y, width, height, sprite):
self.x = player_x
self.y = player_y
self.width = player.width
self.height = player.height
self.col = pygame.sprite.collide_rect(self, sprite)
pygame.sprite.Sprite.__init__(self)
def velocity_player(self):
player_vertical_velocity = 0
player_horizontal_velocity = 0
player_y = player_y + player_vertical_velocity
player_x = player_x + player_horizontal_velocity
player_fr = 5
def render(self):
player_surface = pygame.image.load("Bunny.png")
player_surface.set_colorkey(TRANSPARENT_GREEN)
player_left = pygame.image.load("Bunny_left.png")
player_left.set_colorkey(TRANSPARENT_GREEN)
player_left = pygame.image.load("Bunny_left.png")
player_left.set_colorkey(TRANSPARENT_GREEN)
player_fall = pygame.image.load("Bunny_Fall.png")
player_fall.set_colorkey(TRANSPARENT_GREEN)
player_jump = pygame.image.load("Bunny_r_Jump.png")
screen.blit(player_surface, [player_x, player_y])
First, start by learning the concept of Classes (see also Python - Classes and OOP Basics). The collision must be detected in every frame. Usually you don't want to do it in the constrictor of a class. See How do I detect collision in pygame?.
When using pygame.sprite.Sprites and pygame.sprite.Groups, collision is detected by functions like pygame.sprite.spritecollide() and pygame.sprite.groupcollide(). However, collision detection is usually not performed in the Sprite class. This is done in the outer code that manages the Sprites and the interaction between the Sprites.
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
class RectSprite(pygame.sprite.Sprite):
def __init__(self, color, x, y, w, h):
super().__init__()
self.image = pygame.Surface((w, h))
self.image.fill(color)
self.rect = pygame.Rect(x, y, w, h)
player = RectSprite((255, 0, 0), 0, 0, 50, 50)
obstacle1 = RectSprite((128, 128, 128), 50, 150, 50, 50)
obstacle2 = RectSprite((128, 128, 128), 150, 50, 50, 50)
all_group = pygame.sprite.Group([obstacle1, obstacle2, player])
obstacle_group = pygame.sprite.Group([obstacle1, obstacle2])
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
player.rect.center = pygame.mouse.get_pos()
collide = pygame.sprite.spritecollide(player, obstacle_group, False)
window.fill(0)
all_group.draw(window)
for s in collide:
pygame.draw.rect(window, (255, 255, 255), s.rect, 5, 1)
pygame.display.flip()
pygame.quit()

Image not showing up on pygame screen

I am making my own game with pygame for the first time, and I'm using sprites. I'm trying to blit an image onto the screen but it doesn't work. All it shows is a blank white screen.
Here is the code:
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
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):
image = pygame.Surface([width, height]).convert()
image.blit(self.sprite_sheet, (0, 0), (x, y, width, height))
image.set_colorkey(BLACK)
return image
class Bomb(pygame.sprite.Sprite):
change_x =0
change_y = 0
def __init__(self):
image = pygame.Surface([256, 256]).convert()
sprite_sheet = SpriteSheet("Bomb_anim0001.png")
image = sprite_sheet.get_image(0, 0, 256, 256)
pygame.init()
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode([screen_width, screen_height])
pygame.display.set_caption("Game")
clock = pygame.time.Clock()
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(WHITE)
bomb = Bomb()
clock.tick(60)
pygame.display.flip()
pygame.quit()
This is the image:
Click on this link.
Any help will be highly appreciated. Thanks!
You must blit the image on the screen Surface.
But there is more to be done. Add a rect attribute to the Bomb class:
class Bomb(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
sprite_sheet = SpriteSheet("Bomb_anim0001.png")
self.image = sprite_sheet.get_image(0, 0, 256, 256)
self.rect = self.image.get_rect(topleft = (x, y)
self.change_x = 0
self.change_y = 0
Create a pygame.sprite.Group and add the bomb to the Group. draw all the sprites in the Group on the screen:_
bomb = Bomb(100, 100)
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)
pygame.display.flip()
clock.tick(60)
pygame.quit()
pygame.sprite.Group.draw() and pygame.sprite.Group.update() are methods which are provided by pygame.sprite.Group.
The former delegates the to the update mehtod of the contained pygame.sprite.Sprites - you have to implement the method. See pygame.sprite.Group.update():
Calls the update() method on all Sprites in the Group [...]
The 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. See pygame.sprite.Group.draw():
Draws the contained Sprites to the Surface argument. This uses the Sprite.image attribute for the source surface, and Sprite.rect. [...]

How do you move a sprite in pygame?

I know that this question has already been answered in other forms, but I can't seem to make it work for my game. I'm trying to make the sprite move and keep him in the place he moved to. This is part of my code:
class player(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
def draw(self, x, y):
player = pygame.image.load("stand_down.png").convert_alpha()
player = pygame.transform.scale(player, (100, 200))
screen.blit(player, (x, y))
pygame.display.update()
for event in pygame.event.get():
key = pygame.key.get_pressed()
if event.type == KEYDOWN :
if key [K_LEFT] :
player = pygame.image.load("standleft.png").convert_alpha()
player = pygame.transform.scale(player, (100, 200))
x -= 50
screen.blit(player, (x, y))
pygame.display.update()
pygame.sprite.Sprite.update(player)
if key [K_RIGHT]:
player = pygame.image.load("standright.png").convert_alpha()
player = pygame.transform.scale(player, (100, 200))
x += 50
screen.blit(player, (x, y))
pygame.display.update()
pygame.sprite.Sprite.update(player)
def update(self):
pygame.sprite.Sprite.update(player)
And then this is part of my main function:
player.draw(screen, x, y)
player.update(screen)
I've tried everything I can think of, but the player just keeps flashing at a different point, then returning to the previous position. If you could help, it would be much appreciated!
I can't test it but I would do this
# --- classes ---
class Player(pygame.sprite.Sprite): # UpperCaseName for class
def __init__(self, x, y):
super().__init__()
temp_image = pygame.image.load("stand_down.png").convert_alpha()
self.image_down = pygame.transform.scale(temp_image, (100, 200))
temp_image = pygame.image.load("standleft.png").convert_alpha()
self.image_left = pygame.transform.scale(temp_image, (100, 200))
temp_image = pygame.image.load("standright.png").convert_alpha()
self.image_right = pygame.transform.scale(temp_image, (100, 200))
self.image = self.image_down
# keep position and size in pygame.Rect()
# to use it in collision checking
self.rect = self.image.get_rect(x=x, y=y)
def draw(self, screen):
screen.blit(self.image, self.rect)
def handle_event(self):#, event)
self.image = self.image_down
key = pygame.key.get_pressed()
if key[K_LEFT]:
self.rect.x -= 50
self.image = self.image_left
if key[K_RIGHT]:
self.rect.x += 50
self.image = self.image_right
def update(self, enemy):
# check collision with eneny
if self.rect.colliderect(enemy.rect):
print("I'm killed by enemy")
# --- main ---
player = Player(400, 400)
enemy = Enemy(100, 100)
while True:
# - events -
for event in pygame.event.get():
#player.handle_event(event)
player.handle_event()
# - updates -
enemy.update()
player.update(enemy)
# - draws -
screen.fill( (0,0,0) )
player.draw(screen)
enemy.draw(screen)
pygame.display.update()

How to display font that is related to a rectangle position using classes?

I am struggling to rewrite a piece of -working code- I wrote in a more object oriented way and don't know how to attach Fonts (showing the position) to the 3 moving rects. In other words when I move those rects I want the number with their position to be attached to each of them.
Any suggestions to improve this code are also highly recommended.
import sys
import pygame as pg
WIDTH = 800
HEIGHT = 600
WHITE = (255, 255, 255)
SCREEN = pg.display.set_mode((WIDTH, HEIGHT))
clock = pg.time.Clock()
pg.font.init()
class Rectangle(pg.sprite.Sprite):
def __init__(self, color, width, height, x , y):
super().__init__()
self.width = width
self.height = height
self.color = color
self.image = pg.Surface((width, 3*140 + height),pg.SRCALPHA)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
for z in range(0, 3 * 140, 140):
pg.draw.rect(self.image, color, (0, z, self.width, self.height))
def draw(self, screen):
screen.blit(self.image, self.rect)
return int(self.rect.y)
def update(self):
self.rect.y += event.rel[1]
def rect_y(self):
return self.rect.y
class Font(Rectangle):
def __init__(self, color, width, height, x , y):
super().__init__( color, width, height, x , y)
self.font_a = pg.font.Font(None, 20)
self.text = self.font_a.render(str(abs(int(object1.rect_y()))), True, (0,0,0))
self.text_rect = self.text.get_rect()
self.text_rect.y = Rectangle.rect_y(self)
object1 = Rectangle((50,100,100), 30, 100, 110 , 120)
selected1 = False
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
if object1.rect.collidepoint(event.pos):
selected1 = True
elif event.type == pg.MOUSEBUTTONUP:
selected1 = False
elif event.type == pg.MOUSEMOTION:
if selected1:
object1.update()
SCREEN.fill(WHITE)
font1 = Font((50,100,100), 30, 100, 110 , 120)
SCREEN.blit(font1.text, font1.rect)
object1.draw(SCREEN)
pg.display.update()
clock.tick(60)
if __name__ == '__main__':
pg.init()
pg.quit()
sys.exit()
I'd add the Font objects (I renamed the class as Label) as attributes to the Rectangle instances. If you need multiple labels you can put them into a list. I gave the Rectangle a handle_event method to which I pass the MOUSEMOTION events to update its self.rect, then the rectangle passes the event to its labels, so that they can update their positions.
To store the sprite instances, you can use a pygame.sprite.Group (all_sprites in the example) which allows you to update all contained sprites by calling all_sprites.update() and all_sprites.draw(SCREEN). all_sprites.update() is actually useless in this case, because the sprites don't have update methods (I just wanted to show you how it should be called in the main loop).
import pygame as pg
pg.init()
WHITE = (255, 255, 255)
SCREEN = pg.display.set_mode((800, 600))
FONT_A = pg.font.Font(None, 20)
clock = pg.time.Clock()
class Rectangle(pg.sprite.Sprite):
def __init__(self, color, width, height, x, y, all_sprites):
super().__init__()
self.color = color
self.image = pg.Surface((width, 3*140 + height), pg.SRCALPHA)
self.rect = self.image.get_rect(topleft=(x, y))
# If you need multiple labels, you can put them into a list.
# You could also create and add them in the for loop below.
self.labels = [Label(self.rect.right+3, self.rect.top, (0,0,0)),
Label(self.rect.right+3, self.rect.top+140, (0,0,0))]
all_sprites.add(self.labels)
for z in range(0, 3*140, 140):
pg.draw.rect(self.image, color, (0, z, width, height))
def handle_event(self, event):
self.rect.y += event.rel[1]
# Pass the event to the labels, so that they can handle it themselves.
for label in self.labels:
label.handle_event(event)
class Label(pg.sprite.Sprite):
def __init__(self, x, y, color):
super().__init__()
self.color = color
self.image = FONT_A.render(str(abs(y)), True, self.color)
self.rect = self.image.get_rect(topleft=(x, y))
def handle_event(self, event):
self.rect.y += event.rel[1]
self.image = FONT_A.render(str(abs(self.rect.y)), True, self.color)
def main():
# Create a sprite group which will contain all sprite instances.
# To update and draw the sprites you can just call its .update and .draw
# methods.
all_sprites = pg.sprite.Group()
# Pass the group to the Rectangle, because we need to add the labels.
object1 = Rectangle((50,100,100), 30, 100, 110, 120, all_sprites)
all_sprites.add(object1)
selected1 = False
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
if object1.rect.collidepoint(event.pos):
selected1 = True
elif event.type == pg.MOUSEBUTTONUP:
selected1 = False
elif event.type == pg.MOUSEMOTION:
if selected1:
# Pass the event to the object's handle_event method,
# where it uses it to update its rect and labels.
object1.handle_event(event)
all_sprites.update()
SCREEN.fill(WHITE)
all_sprites.draw(SCREEN)
pg.display.update()
clock.tick(60)
if __name__ == '__main__':
main()
pg.quit()
Addendum: If all labels should show the same value (the top position of the rects in this case), you can pass this value to the label instances and add it as an attribute. Then update it in the handle_event method as well and use it to render the font surface.
class Rectangle(pg.sprite.Sprite):
def __init__(self, color, width, height, x, y, all_sprites):
super().__init__()
self.color = color
self.image = pg.Surface((width, 3*140 + height), pg.SRCALPHA)
self.rect = self.image.get_rect(topleft=(x, y))
# Pass another argument (the `value`) to the labels.
self.labels = [Label(self.rect.right+3, self.rect.top, self.rect.top, (0,0,0)),
Label(self.rect.right+3, self.rect.top+140, self.rect.top, (0,0,0))]
all_sprites.add(self.labels)
for z in range(0, 3*140, 140):
pg.draw.rect(self.image, color, (0, z, width, height))
def handle_event(self, event):
self.rect.y += event.rel[1]
for label in self.labels:
label.handle_event(event)
class Label(pg.sprite.Sprite):
def __init__(self, x, y, value, color):
super().__init__()
self.value = value
self.color = color
self.image = FONT_A.render(str(abs(self.value)), True, self.color)
self.rect = self.image.get_rect(topleft=(x, y))
def handle_event(self, event):
# Update the self.value as well and use it to render the new font surface.
self.value += event.rel[1]
self.rect.y += event.rel[1]
self.image = FONT_A.render(str(abs(self.value)), True, self.color)

Categories