I am trying to use pygame.KEYDOWN so that when I press the key represented by pygame.K_q the block that the player has collided with can be dragged (pick up) to where the player moves as it is done using pygame.MOUSEBUTTONDOWN in this tutorial.
But pressing the q key to drag the block does not work...
This need of mine arose when trying to implement this functionality in another larger code. So I decided to get this other tutorial2 to set up my MWE. I looked to see if anyone had already asked about it but I just found relative but not exact questions that used a very different or very large code structure and I still haven't figured out why I can't get my MWE code to work. This should be a simple question and someone may have already posted this question, so if so let me know where there is a question already posted that clarifies my question.
My MWE:
import pygame
import random
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
class Block(pygame.sprite.Sprite):
"""
This class represents the block to be picked up.
It derives from the "Sprite" class in Pygame.
"""
def __init__(self, color, width, height):
""" Constructor. Pass in the color of the block,
and its x and y position. """
# Call the parent class (Sprite) constructor
super().__init__()
# Create an image of the block, and fill it with a color.
# This could also be an image loaded from the disk.
self.image = pygame.Surface([width, height])
self.image.fill(color)
# Fetch the rectangle object that has the dimensions of the image
# image.
# Update the position of this object by setting the values
# of rect.x and rect.y
self.rect = self.image.get_rect()
class Player(pygame.sprite.Sprite):
""" The class is the player-controlled sprite. """
carry_block_list = []
def __init__(self, x, y):
"""Constructor function"""
# Call the parent's constructor
super().__init__()
# Set height, width
self.image = pygame.Surface([15, 15])
self.image.fill(RED)
# Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def update(self):
diff_x = self.rect.x - 4
diff_y = self.rect.y - 4
# Loop through each block that we are carrying and adjust
# it by the amount we moved.
for block in self.carry_block_list:
block.rect.x -= diff_x
block.rect.y -= diff_y
print("something")
# Call this function so the Pygame library can initialize itself
pygame.init()
# Create an 800x600 sized screen
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode([screen_width, screen_height])
# Set the title of the window
pygame.display.set_caption('Move Sprite With Keyboard')
# Create the player paddle object
player = Player(50, 50)
all_sprites_list = pygame.sprite.Group()
all_sprites_list.add(player)
block_list = pygame.sprite.Group()
for i in range(50):
# This represents a block
block = Block(BLACK, 20, 15)
# Set a random location for the block
block.rect.x = random.randrange(screen_width)
block.rect.y = random.randrange(screen_height)
# Add the block to the list of objects
block_list.add(block)
all_sprites_list.add(block)
clock = pygame.time.Clock()
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
print("pick up")
# When the mouse button is pressed, see if we are in contact with
# other sprites:
blocks_hit_list = pygame.sprite.spritecollide(player, block_list, False)
# Set the list of blocks we are in contact with as the list of
# blocks being carried.
player.carry_block_list = blocks_hit_list
if event.key == pygame.K_l:
print("let go")
# When we let up on the mouse, set the list of blocks we are
# carrying as empty.
player.carry_block_list = []
if event.key == pygame.K_LEFT:
player.rect.x -= player.rect.width
elif event.key == pygame.K_RIGHT:
player.rect.x += player.rect.width
elif event.key == pygame.K_UP:
player.rect.y -= player.rect.height
elif event.key == pygame.K_DOWN:
player.rect.y += player.rect.height
# -- Draw everything
# Clear screen
screen.fill(WHITE)
# Draw sprites
all_sprites_list.draw(screen)
# Flip screen
pygame.display.flip()
# Pause
clock.tick(40)
pygame.quit()
You move blocks in Player.update() but you never execute it
First I tried to execute this function in every loop but finally I changed this function to run only when players is moved
def update(self, diff_x, diff_y):
# Loop through each block that we are carrying and adjust
# it by the amount we moved.
for block in self.carry_block_list:
block.rect.x += diff_x
block.rect.y += diff_y
and
if event.key == pygame.K_LEFT:
player.rect.x -= player.rect.width
player.update(-player.rect.width, 0)
elif event.key == pygame.K_RIGHT:
player.rect.x += player.rect.width
player.update(player.rect.width, 0)
elif event.key == pygame.K_UP:
player.rect.y -= player.rect.height
player.update(0, -player.rect.height)
elif event.key == pygame.K_DOWN:
player.rect.y += player.rect.height
player.update(0, player.rect.height)
Full code:
import pygame
import random
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
class Block(pygame.sprite.Sprite):
"""
This class represents the block to be picked up.
It derives from the "Sprite" class in Pygame.
"""
def __init__(self, color, width, height):
""" Constructor. Pass in the color of the block,
and its x and y position. """
# Call the parent class (Sprite) constructor
super().__init__()
# Create an image of the block, and fill it with a color.
# This could also be an image loaded from the disk.
self.image = pygame.Surface([width, height])
self.image.fill(color)
# Fetch the rectangle object that has the dimensions of the image
# image.
# Update the position of this object by setting the values
# of rect.x and rect.y
self.rect = self.image.get_rect()
class Player(pygame.sprite.Sprite):
""" The class is the player-controlled sprite. """
carry_block_list = []
def __init__(self, x, y):
"""Constructor function"""
# Call the parent's constructor
super().__init__()
# Set height, width
self.image = pygame.Surface([15, 15])
self.image.fill(RED)
# Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def update(self, diff_x, diff_y):
# Loop through each block that we are carrying and adjust
# it by the amount we moved.
for block in self.carry_block_list:
block.rect.x += diff_x
block.rect.y += diff_y
# Call this function so the Pygame library can initialize itself
pygame.init()
# Create an 800x600 sized screen
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode([screen_width, screen_height])
# Set the title of the window
pygame.display.set_caption('Move Sprite With Keyboard')
# Create the player paddle object
player = Player(50, 50)
all_sprites_list = pygame.sprite.Group()
all_sprites_list.add(player)
block_list = pygame.sprite.Group()
for i in range(50):
# This represents a block
block = Block(BLACK, 20, 15)
# Set a random location for the block
block.rect.x = random.randrange(screen_width)
block.rect.y = random.randrange(screen_height)
# Add the block to the list of objects
block_list.add(block)
all_sprites_list.add(block)
clock = pygame.time.Clock()
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
# When the mouse button is pressed, see if we are in contact with
# other sprites:
blocks_hit_list = pygame.sprite.spritecollide(player, block_list, False)
# Set the list of blocks we are in contact with as the list of
# blocks being carried.
player.carry_block_list = blocks_hit_list
if event.key == pygame.K_l:
# When we let up on the mouse, set the list of blocks we are
# carrying as empty.
player.carry_block_list = []
if event.key == pygame.K_LEFT:
player.rect.x -= player.rect.width
player.update(-player.rect.width, 0)
elif event.key == pygame.K_RIGHT:
player.rect.x += player.rect.width
player.update(player.rect.width, 0)
elif event.key == pygame.K_UP:
player.rect.y -= player.rect.height
player.update(0, -player.rect.height)
elif event.key == pygame.K_DOWN:
player.rect.y += player.rect.height
player.update(0, player.rect.height)
# -- Draw everything
# Clear screen
screen.fill(WHITE)
# Draw sprites
all_sprites_list.draw(screen)
# Flip screen
pygame.display.flip()
# Pause
clock.tick(40)
pygame.quit()
I am currently working on a square collecting game in which the User/Player will be a blue square that can only collect squares that are smaller than the player's square. These are represented by color, if the square is smaller than the player than it is green, if it is bigger it will be red.
I have run into a problem where I can not figure out how to INCREASE THE SIZE of the Player when the player collides with the green squares. I have tried a number of different things that don't seem to work and would appreciate some different options to try and accomplish this task.
Here is the code that I am currently using:
import pygame
import random
# Define some colors
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
class Block(pygame.sprite.Sprite):
"""
This class represents the ball.
It derives from the "Sprite" class in Pygame.
"""
def __init__(self, color, width, height):
""" Constructor. Pass in the color of the block,
and its x and y position. """
# Call the parent class (Sprite) constructor
super().__init__()
# Create an image of the block, and fill it with a color.
# This could also be an image loaded from the disk.
self.image = pygame.Surface([width, height])
self.image.fill(color)
# Fetch the rectangle object that has the dimensions of the image
# image.
# Update the position of this object by setting the values
# of rect.x and rect.y
self.rect = self.image.get_rect()
class Player(pygame.sprite.Sprite):
""" The class is the player-controlled sprite. """
# -- Methods
def __init__(self, x, y):
"""Constructor function"""
# Call the parent's constructor
super().__init__()
# Set height, width
self.image = pygame.Surface([15, 15])
self.image.fill(BLUE)
# Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
# -- Attributes
# Set speed vector
self.change_x = 0
self.change_y = 0
def changespeed(self, x, y):
""" Change the speed of the player"""
self.change_x += x
self.change_y += y
def update(self):
""" Find a new position for the player"""
# move left or right
self.rect.x += self.change_x
if self.rect.x <= 0:
self.rect.x = 1
wall_sound.play()
if self.rect.x >= 680:
self.rect.x = 679
wall_sound.play()
# move up or down
self.rect.y += self.change_y
if self.rect.y <= 0:
self.rect.y = 1
wall_sound.play()
if self.rect.y >= 385:
self.rect.y = 384
wall_sound.play()
# Initialize Pygame
pygame.init()
# Set the height and width of the screen
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode([screen_width, screen_height])
# This is a list of 'sprites.' Each block in the program is
# added to this list. The list is managed by a class called 'Group.'
block_list = pygame.sprite.Group()
# This is a list of every sprite.
# All blocks and the player block as well.
all_sprites_list = pygame.sprite.Group()
# Create a BLUE player block
p_width = 30
p_height = 25
p_size = (p_width, p_height)
player = Player(p_width, p_height)
all_sprites_list.add(player)
# BLOCK LIST
for i in range(50):
width = random.randrange(20, 50)
height = width - 5
b_size = (width, height)
if b_size <= p_size:
color = GREEN
if b_size > p_size:
color = RED
# This represents a block
block = Block(color, width, height)
# Set a random location for the block
block.rect.x = random.randrange(screen_width - width)
block.rect.y = random.randrange(screen_height - height)
# Add the block to the list of objects
block_list.add(block)
all_sprites_list.add(block)
# Loop until the user clicks the close button.
done = False
# Pygame sound effects
collect_good = pygame.mixer.Sound("SnowWalk.ogg")
collect_bad = pygame.mixer.Sound("icebreaks.ogg")
wall_sound = pygame.mixer.Sound("evillaugh.ogg")
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
font = pygame.font.Font(None, 25)
score = 0
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Set the speed based on the key pressed
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.changespeed(-3, 0)
elif event.key == pygame.K_RIGHT:
player.changespeed(3, 0)
elif event.key == pygame.K_UP:
player.changespeed(0, -3)
elif event.key == pygame.K_DOWN:
player.changespeed(0, 3)
# Reset speed when key goes up
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
player.changespeed(3, 0)
elif event.key == pygame.K_RIGHT:
player.changespeed(-3, 0)
eli
# See if the player block has collided with anything.
#good
blocks_hit_list = pygame.sprite.spritecollide(player, block_list, True)
# Check the list of collisions.
#good
for block in blocks_hit_list:
score += 1
collect_good.play()
if score <= -1:
done = True
# Draw all the spites
all_sprites_list.draw(screen)
text = font.render("Score: " + str(score), True, BLACK)
screen.blit(text, [20, 300])
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit to 60 frames per second
clock.tick(60)
pygame.quit()f event.key == pygame.K_UP:
player.changespeed(0, 3)
elif event.key == pygame.K_DOWN:
player.changespeed(0, -3)
# This calls update on all the sprites
all_sprites_list.update()
# Clear the screen
screen.fill(WHITE)
# See if the player block has collided with anything.
#good
blocks_hit_list = pygame.sprite.spritecollide(player, block_list, True)
# Check the list of collisions.
#good
for block in blocks_hit_list:
score += 1
collect_good.play()
if score <= -1:
done = True
# Draw all the spites
all_sprites_list.draw(screen)
text = font.render("Score: " + str(score), True, BLACK)
screen.blit(text, [20, 300])
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit to 60 frames per second
clock.tick(60)
pygame.quit()
Make the player constructor take the width and height as well as the x and y. In the collision check, increase these and create a new image for the player with the new increased x and y values. Don't forget to reset the player rect at the same time as well.
Also, you are passing the width and height in and using them as x and y not width and height.
I am creating a piece of code that involves a maze and doors that open when a button is hit. The problem is that when I put in the doors the player collision detects the doors but no longer detects the maze. I have put all my code down on here in hopes that if people can see any other problems they can help Thanks :)
import pygame, sys, random, time
#Setting up the colours
black = (0,0,0)
white = (255,255,255)
blue = (0,0,255)
green = (0,255,0)
red = (255,0,0)
orange = (248,100,1)
slateG = (112,128,144)
pink = (255,20,147)
dTurq = (0,206,209)
honeyD = (240,255,240)
maroon = (128,0,0)
goldenrod = (218,165,32)
sGreen = (0,255,127)
Fus = (255,0,255)
class player(pygame.sprite.Sprite):
change_x=0
change_y=0
#initialise the class
def __init__(self,x,y):
pygame.sprite.Sprite.__init__(self)
#the player will be 13x13
self.image = pygame.Surface([13,13])
self.image.fill(maroon)
self.rect = self.image.get_rect()
self.rect.top = y
self.rect.left = x
def changespeed(self,x,y): #this function changes the speed of the player
self.change_x+=x
self.change_y+=y
def updateWall(self,walls): #This is a collision detection function
old_x=self.rect.left
new_x = old_x+self.change_x
self.rect.left = new_x
collide = pygame.sprite.spritecollide(self,walls,False)
if collide:
self.rect.left = old_x
old_y = self.rect.top
new_y = old_y+self.change_y
self.rect.top = new_y
collide = pygame.sprite.spritecollide(self,walls,False)
if collide:
self.rect.top = old_y
def updateBarrier1(self,barriers):
gx=self.rect.left
nx = gx+self.change_x
self.rect.left = nx
collide = pygame.sprite.spritecollide(self,barriers,False)
if collide:
self.rect.left = gx
gy = self.rect.top
ny = gy+self.change_y
self.rect.top = ny
collide = pygame.sprite.spritecollide(self,barriers,False)
if collide:
self.rect.top = gy
class Wall(pygame.sprite.Sprite):
def __init__(self,x,y,width,height,color): #this sets up for the x/y coordinates, the width and the height and the colour of the wall
pygame.sprite.Sprite.__init__(self)
#how big will the wall be
self.image = pygame.Surface([width,height])
#what colour will the wall be
self.image.fill(color)
#initialising the x/y coordinates
self.rect = self.image.get_rect()
self.rect.top = y
self.rect.left = x
class Bar(pygame.sprite.Sprite):
def __init__(self,x,y,width,height,color):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width,height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.top = y
self.rect.left = x
class Button(pygame.sprite.Sprite):
def __init__(self,x,y,width,height,color):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width,height]) #the buttons dimensions
self.image.fill(color) #colour of the button
#x/y coordinates
self.rect = self.image.get_rect()
self.rect.top=y
self.rect.left=x
#Setting up the walls for the maze
def maze1():
wall_list = pygame.sprite.RenderPlain()
#Walls of maze
walls = [[0,0,20,390,goldenrod],[0,410,20,390,goldenrod],[0,0,1000,20,goldenrod],
[980,0,20,800,goldenrod],[0,780,980,20,goldenrod],[20,40,940,10,dTurq],
[950,40,10,350,dTurq],[950,410,10,350,dTurq],[40,750,370,10,dTurq],
[430,570,10,210,dTurq],[430,570,240,10,dTurq],[660,570,10,210,dTurq],
[690,550,10,210,dTurq],[690,750,260,10,dTurq],[400,550,10,210,dTurq],
[40,410,10,350,dTurq],[20,100,30,10,dTurq], [40,100,10,290,dTurq],
[50,410,460,10,green],[50,380,350,10,green],[430,380,100,10,green],
[530,380,10,100,green],[500,410,10,100,green],[500,510,60,10,green],
[550,510,10,30,green],[580,510,10,30,green],[400,540,160,10,dTurq],
[580,540,120,10,dTurq],[530,480,150,10,green],[680,430,10,60,green],
[580,510,130,10,green],[710,460,10,60,green],[680,430,250,10,green],
[710,460,190,10,green],[920,430,10,300,green],[890,460,10,230,green],
[820,720,100,10,green],[820,690,80,10,green],[810,690,10,40,green],
[550,410,400,10,sGreen],[550,210,10,200,sGreen],[250,210,300,10,sGreen],
[250,210,10,120,sGreen],[250,330,180,10,sGreen],[430,330,10,50,sGreen],
[580,380,370,10,sGreen],[580,180,10,200,sGreen],[220,180,360,10,sGreen],
[220,180,10,180,sGreen],[220,360,180,10,sGreen],[390,360,10,20,sGreen]]
for item in walls:
wall = Wall(item[0],item[1],item[2],item[3],item[4])
wall_list.add(wall)
return wall_list
def BarrierWhite():
barriers = pygame.sprite.RenderPlain()
barrier = [[500,540,10,40,white],[400,180,10,40,white]]
for item in barrier:
barrie = Bar(item[0],item[1],item[2],item[3],item[4])
barriers.add(barrie)
return barriers
def BarrierPink():
wall_list = pygame.sprite.RenderPlain()
walls = [[550,520,40,10,pink],[20,600,30,10,pink],[300,380,10,40,pink]]
for item in walls:
wall = Wall(item[0],item[1],item[2],item[3],item[4])
wall_list.add(wall)
return wall_list
def Buttons(): #Creating buttons
buttons = pygame.sprite.RenderPlain()
button1 = [[965,765,10,10,white]]
for item in button1:
button = Button(item[0],item[1],item[2],item[3],item[4])
buttons.add(button)
return buttons
def pinkButton():
pinkB = pygame.sprite.RenderPlain()
b = [[835,705,10,10,pink]]
for item in b:
button = Button(item[0],item[1],item[2],item[3],item[4])
pinkB.add(button)
return pinkB
#main line
pygame.init()
window = pygame.display.set_mode([1000,800])
background = pygame.Surface(window.get_size())
background = background.convert()
background.fill(black)
player = player(25,25)
movingsprt = pygame.sprite.RenderPlain()
movingsprt.add(player)
clock = pygame.time.Clock()
wall_list = maze1()
barriers = BarrierWhite()
buttons = Buttons()
B1 = pinkButton()
done = False
while done == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
#This makes the player move
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.changespeed(-6,0)
if event.key == pygame.K_RIGHT:
player.changespeed(6,0)
if event.key == pygame.K_UP:
player.changespeed(0,-6)
if event.key == pygame.K_DOWN:
player.changespeed(0,6)
#This stops the player from moving
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
player.changespeed(6,0)
if event.key == pygame.K_RIGHT:
player.changespeed(-6,0)
if event.key == pygame.K_UP:
player.changespeed(0,6)
if event.key == pygame.K_DOWN:
player.changespeed(0,-6)
player.updateWall(wall_list)
player.updateBarrier1(barriers)
window.fill(black)
movingsprt.draw(window)
barriers.draw(window)
wall_list.draw(window)
buttons.draw(window)
B1.draw(window)
pygame.display.flip()
clock.tick(40)
pygame.quit()
Also if someone could tell me how to put a countdown timer that will disable the player and display a game over sign that would be awesome too :D
Adding doors (or barriers as you call them in your code) makes the wall collision ineffective because of a design problem in your game.
Here's what you do in your main loop:
player.updateWall(wall_list)
player.updateBarrier1(barriers)
You first check with collisions for a wall, then check again for collisions with barriers.
The problem is that you incorporated the part that makes your character move in those updateWall and updateBarrier1 methods.
So for instance, if your character collides with a wall, but not with a door, the character will still move because the updateBarrier1 method will not detect a collision with a barrier (door) and will make him move.
Instead, you should do some methods called collides_wall and collides_barrier that return True or False and then replace the two previously quoted lines by:
if not player.collides_wall(wall_list) and not player.collides_barriers(barriers):
player.move()
With move a method that would move your player as you currently do in the updateSomething methods.
Note: Someone pointed out in the comments that you also don't need two separate methods for walls and barriers collision, because ultimately they are both sprites and you do exactly the same operations on both of them. So just add a collides method.