I'm trying to make a game where the player can move the main sprite (gran) left right in 3 columns avoiding falling thunderclouds from the sky. Both components work but won't at the same time, so I can either move the player or the thundercloud falls from the top. Please can someone help so both of these events can occur at the same time
Heres my code...
import pygame, sys
import random
import time
from pygame.locals import *
#sets colours for transparency
BLACK = ( 0, 0, 0)
#Sets gran sprite
class Sprite_maker(pygame.sprite.Sprite): # This class represents gran it derives from the "Sprite" class in Pygame
def __init__(self, filename):
super().__init__() # Call the parent class (Sprite) constructor
self.image = pygame.image.load(filename).convert()# Create an image loaded from the disk
self.image.set_colorkey(BLACK)#sets transparrency
self.rect = self.image.get_rect()# Fetchs the object that has the dimensions of the image
#Initilizes pygame game
pygame.init()
pygame.display.set_caption("2nd try")
#sets background
swidth = 360
sheight = 640
sky = pygame.image.load("sky.png")
screen = pygame.display.set_mode([swidth,sheight])
#This is a list of every sprite
all_sprites_list = pygame.sprite.Group()
#Sets thunder list and creates a new thunder cloud and postions it
tcloud_speed = 10
tc_repeat = 0
thunder_list = [0, 120, 240]
for i in range(1):
tx = random.choice(thunder_list)
tcloud = Sprite_maker("thundercloud.png")
tcloud.rect.x = tx
tcloud.rect.y = 0
all_sprites_list.add(tcloud)
#Creates the gran sprite
gran = Sprite_maker("gran.png")
all_sprites_list.add(gran)
gran.rect.x = 120
gran.rect.y = 430
#Movement of gran sprite when left/right pressed
def gran_movement(movex):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT and gran.rect.x == 0:
gran.rect.x = 0
elif event.key == pygame.K_RIGHT and gran.rect.x == 240:
gran.rect.x = 240
elif event.key == pygame.K_LEFT:#left key pressed player moves left 1
gran.rect.x -= 120
elif event.key == pygame.K_RIGHT:#right key pressed player moves left 1
gran.rect.x += 120
return movex
#Main program loop
game_start = True
while game_start == True:
for event in pygame.event.get():
tcloud.rect.y += tcloud_speed
if event.type == pygame.QUIT: #If user clicked close
game_start = False #Exits loop
#Clear the screen and sets background
screen.blit(sky, [0, 0])
#Displays all the sprites
all_sprites_list.draw(screen)
pygame.display.flip()
#Moves gran by accessing gran
gran_movement(gran.rect.x)
#Moves cloud down screen
if tc_repeat == 0:
tcloud.rect.y = 0
time.sleep(0.25)
tc_repeat = 1
else:
tcloud.rect.y += tcloud_speed
pygame.quit()
I'd actually create some pygame.sprite.Sprite subclasses and sprite groups, but since you're not familiar with them, I'll just use pygame.Rects and pygame.Surfaces.
So, create a rect for the player and a list of rects for the clouds. The rects are used as the blit positions (images/surfaces get blitted at the rect.topleft coordinates) and also for the collision detection (colliderect).
To move the clouds, you have to iterate over the cloud_list with a for loop and increment the y coordinate of each rect. That will happen once per frame (iteration of the while loop) and the game will run with 30 frames per second (because of clock.tick(30)).
The player movement (in the event loop) will seemingly take place at the same time as the cloud movement.
import random
import pygame
pygame.init()
# Some replacement images/surfaces.
PLAYER_IMG = pygame.Surface((38, 68))
PLAYER_IMG.fill(pygame.Color('dodgerblue1'))
CLOUD_IMG = pygame.Surface((38, 38))
CLOUD_IMG.fill(pygame.Color('gray70'))
def main():
screen = pygame.display.set_mode((360, 640))
clock = pygame.time.Clock()
# You can create a rect with the `pygame.Surface.get_rect` method
# and pass the desired coordinates directly as an argument.
player_rect = PLAYER_IMG.get_rect(topleft=(120, 430))
# Alternatively create pygame.Rect instances in this way.
cloud_rect = pygame.Rect(120, 0, 38, 38)
# The clouds are just pygame.Rects in a list.
cloud_list = [cloud_rect]
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_d:
player_rect.x += 120
elif event.key == pygame.K_a:
player_rect.x -= 120
remaining_clouds = []
for cloud_rect in cloud_list:
# Move the cloud downwards.
cloud_rect.y += 5
# Collision detection with the player.
if player_rect.colliderect(cloud_rect):
print('Collision!')
# To remove cloud rects that have left the
# game area, append only the rects above 600 px
# to the remaining_clouds list.
if cloud_rect.top < 600:
remaining_clouds.append(cloud_rect)
else:
# I append a new rect to the list when an old one
# disappears.
new_rect = pygame.Rect(random.choice((0, 120, 240)), 0, 38, 38)
remaining_clouds.append(new_rect)
# Assign the filtered list to the cloud_list variable.
cloud_list = remaining_clouds
screen.fill((30, 30, 30))
# Blit the cloud image at the cloud rects.
for cloud_rect in cloud_list:
screen.blit(CLOUD_IMG, cloud_rect)
screen.blit(PLAYER_IMG, player_rect)
pygame.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pygame.quit()
Related
I am only quite new to Python only been working on the Introduction Netacad course for about 4 weeks now and I am having trouble with an lab exercise given to me by my lecturer.
Using a prebuilt pygame file it asks to add in lines of code to add left and right movement upon left and right keydown evens, up and down, and increase speed and decrease for which ive already completed. I am having trouble with the final task which asks:
Add a feature to make player1 sprite visible/invisible when the space bar is pressed.
So i figure this must just be another keydown event/listener but what exactly is the if statement outcome to make my already drawn rectangle sprite disappear and reappear.
This is my code below and sorry if i havent been clear with this im quite new to this language and only ever done C# intro before.
import pygame # accesses pygame files
import sys # to communicate with windows
# game setup ################ only runs once
pygame.init() # starts the game engine
clock = pygame.time.Clock() # creates clock to limit frames per second
FPS = 60 # sets max speed of main loop
SCREENSIZE = SCREENWIDTH, SCREENHEIGHT = 1000, 800 # sets size of screen/window
screen = pygame.display.set_mode(SCREENSIZE) # creates window and game screen
# set variables for colors RGB (0-255)
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
yellow = (255, 255, 0)
green = (0, 255, 0)
gameState = "running" # controls which state the games is in
player1XPos = 100 #Variable for x axes position of player 1
player1YPos = 100 #Variable for Y axis position
player1Direction = ""
player1Speed = 5
player1Visible
# game loop #################### runs 60 times a second!
# your code starts here ##############################
while gameState != "exit": # game loop - note: everything in the mainloop is indented one tab
for event in pygame.event.get(): # get user interaction events
if event.type == pygame.QUIT: # tests if window's X (close) has been clicked
gameState = "exit" # causes exit of game loop
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
#Decreases playerYPos -5 on the X axis
player1Direction = "left"
elif event.key == pygame.K_RIGHT:
#Inrceases playerYPos +5 on the Y Axis
player1Direction = "right"
elif event.key == pygame.K_UP:
#player1XPos Decreases playerxPos -5 on the X axis
player1Direction = "up"
elif event.key == pygame.K_DOWN:
#player1YPos Inrceases playeryPos +5 on the Y axis
player1Direction = "down"
elif event.key == pygame.K_w:
player1Speed= player1Speed / 2
#Decrease movement speed player1Speed of movement
elif event.key == pygame.K_q:
player1Speed= player1Speed * 2
#Increase movement speed player1Speed
elif event.key == pygame.K_SPACE:
# Player 1 Event handler code now...
if player1Direction =="up":
player1YPos = player1YPos - player1Speed
#Increases the player1 rectangle up on the Y axis
elif player1Direction =="down":
player1YPos = player1YPos + player1Speed
#Decreases the player1 rectangle on Y Axis
if player1Direction =="left":
player1XPos = player1XPos - player1Speed
#Moves the player1Pos to the left by decreasing X Pos by the value of player1Speed
#which is 5
elif player1Direction =="right":
player1XPos = player1XPos + player1Speed
#Moves player1Pos rectangle to the right by increasing player1XPos X axis position
#by the value of player1Speed
screen.fill(black)
player1 = pygame.draw.rect(screen, red, (player1XPos, player1YPos, 80, 80))
# your code ends here ###############################
pygame.display.flip() # transfers build screen to human visable screen
clock.tick(FPS) # limits game to frame per second, FPS value
# out of game loop ###############
print("The game has closed") # notifies user the game has ended
pygame.quit() # stops the game engine
sys.exit() # close operating system window
The solution is to conditionally draw the player 1 sprite only if player 1 is visible. Every time when the space is pressed, the value of player1Visible changes to the opposite value.
import pygame # accesses pygame files
import sys # to communicate with windows
# game setup ################ only runs once
pygame.init() # starts the game engine
clock = pygame.time.Clock() # creates clock to limit frames per second
FPS = 60 # sets max speed of main loop
SCREENSIZE = SCREENWIDTH, SCREENHEIGHT = 1000, 800 # sets size of screen/window
screen = pygame.display.set_mode(SCREENSIZE) # creates window and game screen
# set variables for colors RGB (0-255)
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
yellow = (255, 255, 0)
green = (0, 255, 0)
gameState = "running" # controls which state the games is in
player1XPos = 100 #Variable for x axes position of player 1
player1YPos = 100 #Variable for Y axis position
player1Direction = ""
player1Speed = 5
player1Visible = True
# game loop #################### runs 60 times a second!
# your code starts here ##############################
while gameState != "exit": # game loop - note: everything in the mainloop is indented one tab
for event in pygame.event.get(): # get user interaction events
if event.type == pygame.QUIT: # tests if window's X (close) has been clicked
gameState = "exit" # causes exit of game loop
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
#Decreases playerYPos -5 on the X axis
player1Direction = "left"
elif event.key == pygame.K_RIGHT:
#Inrceases playerYPos +5 on the Y Axis
player1Direction = "right"
elif event.key == pygame.K_UP:
#player1XPos Decreases playerxPos -5 on the X axis
player1Direction = "up"
elif event.key == pygame.K_DOWN:
#player1YPos Inrceases playeryPos +5 on the Y axis
player1Direction = "down"
elif event.key == pygame.K_w:
player1Speed= player1Speed / 2
#Decrease movement speed player1Speed of movement
elif event.key == pygame.K_q:
player1Speed= player1Speed * 2
#Increase movement speed player1Speed
elif event.key == pygame.K_SPACE:
player1Visible = not player1Visible
# Player 1 Event handler code now...
if player1Direction =="up":
player1YPos = player1YPos - player1Speed
#Increases the player1 rectangle up on the Y axis
elif player1Direction =="down":
player1YPos = player1YPos + player1Speed
#Decreases the player1 rectangle on Y Axis
if player1Direction =="left":
player1XPos = player1XPos - player1Speed
#Moves the player1Pos to the left by decreasing X Pos by the value of player1Speed
#which is 5
elif player1Direction =="right":
player1XPos = player1XPos + player1Speed
#Moves player1Pos rectangle to the right by increasing player1XPos X axis position
#by the value of player1Speed
screen.fill(black)
if player1Visible:
player1 = pygame.draw.rect(screen, red, (player1XPos, player1YPos, 80, 80))
# your code ends here ###############################
pygame.display.flip() # transfers build screen to human visable screen
clock.tick(FPS) # limits game to frame per second, FPS value
# out of game loop ###############
print("The game has closed") # notifies user the game has ended
pygame.quit() # stops the game engine
sys.exit() # close operating system window
Outside of my game loop, I have created a function that creates a list of 200 enemies with random coordinates. These enemies are suppose to start at the top of the screen and then drop down at random speeds. Inside the loop, I use a "for" loop to blit the enemies on screen. It works, but all 200 hundred are spawned and fall at the same time, albeit, at different speeds. So I know I need a timer and herein lies the problem; nothing I do works. Ive tried clock.tick(), pygame.delay(), import time and do the time.time() method. Everything either strobes or the system just crashes. What's causing the problem?
[Code]
import pygame
import sys
import random
import time
pygame.init()
#MAIN GAME
game_screen = pygame.display.set_mode((600, 600))
pygame.display.set_caption("Beer Goggles")
bg = pygame.image.load("bg.png")
bg_image = pygame.transform.scale(bg, (600, 600))
class Object:
def __init__(self, image_path, width, height, x, y):
self.image_path = image_path
self.width = width
self.height = height
self.x = x
self.y = y
player = pygame.image.load(image_path)
self.player_main = pygame.transform.scale(player, (width,height))
def draw(self, background):
background.blit(self.player_main, (self.x, self.y))
#enemies
def enemy():
enemy_list = []
for e in range(200):
x_cor = random.randint(25, 361)
e = Object("enemy.png", 70, 70, x_cor, 25)
enemy_list.append(e)
return enemy_list
#Main Objects
player1 = Object("crate.png", 70, 70, 25, 500)
list1 = enemy()
#ladies
fat_lady = Object("fat_lady.png", 300, 300, 360, 180)
# Main Loop
direction = 0
game_on = True
while game_on:
clock = pygame.time.Clock()
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_on = False
pygame.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
direction = 1
elif event.key == pygame.K_LEFT:
direction = -1
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT or event.key == pygame.K_LEFT:
direction = 0
game_screen.fill((0,0,0))
game_screen.blit(bg_image, (0,0))
#title.draw(game_screen)
player1.draw(game_screen)
fat_lady.draw(game_screen)
#move
if direction > 0:
player1.x = player1.x + 10
elif direction < 0:
player1.x = player1.x - 10
#boundaries
if player1.x <= 25:
player1.x = 25
elif player1.x >= 360:
player1.x = 360
#for enemy in list1:
for enemy in list1:
a = random.randint(1, 30)
enemy.draw(game_screen)
enemy.y += a
#collisions
#scoring
pygame.display.update()
quit()
In the code where you create the enemy list you could add a drop start time. Just like you create a random x co-ordinate you could create a time to start dropping it. Then later when you start changing the y position for them (dropping them), you would not start it dropping until after that time had passed.
By the way You have a method enemy() and later you have a loop iterator enemy which will override hide the method by that name. After that point if you tried to call the method enemy() it would fail and you would access the loop iterator instead. It does not affect your code here because you do not try to access the method after creating the loop iterator variable, but it is not a great idea and could cause problems if you later changed the code and did try to access that method. You should be careful about name choices.
My pygame game runs super slowly, about 2 fps. You are supposed to be able to move around with WASD and always be centered in the screen. I know it has something to do with my tilemap and the way I do my math, but I can't pin it down. Also, if there is a better way to keep the player in the center while moving the map behind it, I would love to know how to do that.
import pygame, sys, numpy
#create fps clock
clock = pygame.time.Clock()
#
MAPHEIGHT = 80
MAPWIDTH = 80
TILESIZE = 40
TILESONSCREENW = 13
TILESONSCREENH = 13
#set screen size
SCREENH = TILESONSCREENH*TILESIZE
SCREENW = TILESONSCREENW*TILESIZE
#create character vars
circleRad = 40
circleSpeed = 4
#create circle pos vars
circleX = 250
circleY = 250
#create keyboard button vars
rightP = False
leftP = False
upP = False
downP = False
#
playerOnTileS = pygame.Surface((MAPWIDTH*TILESIZE, MAPHEIGHT*TILESIZE))
#constants for the tilemap
GRASS = pygame.image.load("grass.png")
#tilemap
tilemap = [[GRASS for i in range(MAPHEIGHT)] for j in range(MAPWIDTH)]
#create window
DISPLAYSURF = pygame.display.set_mode((SCREENW, SCREENH))
#set window name
pygame.display.set_caption("Snowball Fight!")
#---------------------------------------------------
class Player:
def __init__(self, playX, playY, size):
self.playerX = playX
self.playerY = playY
self.size = size
self.playerSurface = pygame.Surface((size, size))
pygame.draw.rect(self.playerSurface, (19,135,67), (0,0,size, size))
#------------------------------------------------
def update(self):
playerOnTileS.blit(self.playerSurface, (self.playerX, self.playerY))
DISPLAYSURF.blit(playerOnTileS, (SCREENW/2-self.playerX-self.size ,SCREENH/2-self.playerY-self.size))
#game loop
myPlayer = Player(0,0,circleRad)
while True:
DISPLAYSURF.fill((0,0,0))
for event in pygame.event.get():
#if the user closed the window
if event.type == pygame.QUIT:
#close pygame
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
leftP = True
if event.key == pygame.K_d:
rightP = True
if event.key == pygame.K_w:
upP = True
if event.key == pygame.K_s:
downP = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
leftP = False
if event.key == pygame.K_d:
rightP = False
if event.key == pygame.K_w:
upP = False
if event.key == pygame.K_s:
downP = False
if leftP:
myPlayer.move(-circleSpeed,0,True)
if rightP:
myPlayer.move(circleSpeed,0,True)
if downP:
myPlayer.move(0,circleSpeed,True)
if upP:
myPlayer.move(0,-circleSpeed,True)
for row in range(len(tilemap)):
for column in range(len(tilemap[row])):
playerOnTileS.blit(tilemap[row][column],(column*TILESIZE,row*TILESIZE))
myPlayer.update()
pygame.display.update()
clock.tick(30)
Images/pygame.Surfaces should usually be converted with the pygame.Surface.convert or convert_alpha methods. The performance of unconverted surfaces is abysmal. When I convert the grass image, I get nearly 30 FPS.
The next problem is the nested for loop. Python's function call overhead is rather big, so it would be a good idea to blit all the tiles onto one large background surface before the main loop starts and then blit this background surface onto the DISPLAYSURF once per frame to clear it. With that change I get pygame's apparent maximum FPS, that means clock.get_fps() jumps between 2000 and 1666.666 FPS.
# Ahead of the while loop.
playerOnTileS = pygame.Surface((MAPWIDTH*TILESIZE, MAPHEIGHT*TILESIZE))
for row in range(len(tilemap)):
for column in range(len(tilemap[row])):
playerOnTileS.blit(tilemap[row][column],(column*TILESIZE,row*TILESIZE))
You have to change the update method (by the way, better call it draw) and blit the player surface onto the DISPLAYSURF instead.
I currently have a racecar game in development, using Pygame. I understand that in order to get the sprite to move the way a real car does, trigonometry is required. However, for now, I am simply trying to get the racecar image to rotate as the user holds a button. What is the simplest way to do so?
import pygame
pygame.init()
#DEFING COLOURS
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
BLUE = (0, 0, 255)
GREEN = ( 0, 255, 0)
RED = (255, 0, 0)
size = (800,600)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My First Game")
class Car(pygame.sprite.Sprite):
#BASE CAR CLASS
def __init__(self, filename):
#INITIALISE OBJECT PROPERTIES
super().__init__()
#LOAD IMAGE
self.image = pygame.image.load(filename).convert_alpha()
#SET BACKGROUND COLOUR
#self.image.set_colorkey(WHITE)
#SET RECTANGLE COLLISION BOX
self.rect = self.image.get_rect()
self.angle = 0
self.angle_change = 0
#Create sprites list
all_sprites_list = pygame.sprite.Group()
#Create F1car object
F1car = Car("car.png")
car_rotation = 0.0
surface = pygame.Surface((15, 15))
#Add F1car to sprites list
all_sprites_list.add(F1car)
#LOOP UNTIL USER EXITS THE GAME
carryOn = True
#CLOCK TO CONTROL FRAME RATE
clock = pygame.time.Clock()
##MAIN LOOP##
while carryOn:
#MAIN EVENT LOOP
for event in pygame.event.get(): #USER DID SOMETHING
if event.type == pygame.QUIT: #IF USER CLICKED CLOSE
carryOn = False #END THE LOOP
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
car_rotation += 0.1
pygame.transform.rotate(surface, car_rotation)
#GAME LOGIC
#DRAWING CODE
#CLEARING SCREEN
screen.fill(WHITE)
#DRAWING SHAPES
pygame.draw.rect(screen, RED, [55, 200, 100, 70], 0)
pygame.draw.rect(screen, BLUE, [78, 300, 60, 70], 0)
#LIST OF SPRITES TO COLLIDE WITH EACHOTHER
#blocks_hit_list = pygame.sprite.spritecollide(F1car, )
#DRAW SPRITES FROM all_sprites_list LIST
all_sprites_list.draw(screen)
#UPDATE THE SCREEN
pygame.display.flip()
#SET UPDATE RATE
clock.tick(60)
pygame.quit()
I'd give the Car class an update method and in this method rotate the car if self.angle_change is not 0. That allows you to call all_sprites.update() to call the update methods of all contained sprites.
Set the angle_change in the event loop to start the rotation. In the update method, increase the self.angle by the self.angle_change, then use pygame.transform.rotate or .rotozoom and pass the self.angle. Afterwards you need to get a new rect and pass the center of the old rect as the center argument.
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
WHITE = (255, 255, 255)
CAR_IMAGE = pygame.Surface((45, 90), pygame.SRCALPHA)
CAR_IMAGE.fill((150, 20, 0))
class Car(pygame.sprite.Sprite):
def __init__(self, pos, image):
super().__init__()
self.image = image
# Store a reference to the original to preserve the image quality.
self.orig_image = self.image
self.rect = self.image.get_rect(center=pos)
self.angle = 0
self.angle_change = 0
def update(self):
if self.angle_change != 0:
self.angle += self.angle_change
# I prefer rotozoom because it looks smoother.
self.image = pygame.transform.rotozoom(self.orig_image, self.angle, 1)
self.rect = self.image.get_rect(center=self.rect.center)
all_sprites = pygame.sprite.Group()
f1_car = Car((300, 300), CAR_IMAGE)
all_sprites.add(f1_car)
carryOn = True
while carryOn:
for event in pygame.event.get():
if event.type == pygame.QUIT:
carryOn = False
elif event.type == pygame.KEYDOWN:
# Set the rotation speed of the car sprite.
if event.key == pygame.K_RIGHT:
f1_car.angle_change = -3
elif event.key == pygame.K_LEFT:
f1_car.angle_change = 3
elif event.type == pygame.KEYUP:
# Stop rotating if the player releases the keys.
if event.key == pygame.K_RIGHT and f1_car.angle_change < 0:
f1_car.angle_change = 0
elif event.key == pygame.K_LEFT and f1_car.angle_change > 0:
f1_car.angle_change = 0
all_sprites.update()
screen.fill(WHITE)
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
This can be done rather simply. You need a game loop that checks for inputs. Then you must check that the desired input is present, and increase the rotation of your car each time the input is present.
import pygame
run = True
car_rotation = 0.0
surface = pygame.Surface((100, 60)) # 100 horizontal length. 60 is the vertical length.
while run:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
car_rotation += 0.1
surface = pygame.transform.rotate(surface, car_rotation)
For the case of your code
You have done some wrong checks in the loop. Change your code from pygame.K_RIGHT to pygame.K_r, to use your R key to rotate your sprite. In order to use the mouse, change the pygame.event.type to .MOUSEBUTTONDOWN or .MOUSEBUTTONUP, and keep pygame.K_RIGHT.
Change the if statement, if event.key == pygame.K_r, to
if event.key == pygame.K_r
car_rotation += 1.0
for car in all_sprites_list:
car.image = pygame.transform.rotate(car.image, car_rotation)
and then remove surface = pygame.Surface((15, 15)).
I have written the following code that creates a simple game where when you click an arrow on the keyboard a box moves a unit over in the game.
I am trying to make it so that if i push any of the arrow buttons the box will continue to move in that direction until another arrow is pushed. So if i push the right arrow once instead of scooting +50 pixels it will move continuously across the screen untill a different arrow is clicked and then it will go that way
import pygame #importing the pygame library
# some initializations
pygame.init() # this line initializes pygame
window = pygame.display.set_mode( (800,600) ) # Create a window with width=800 and height=600
pygame.display.set_caption( 'Rectangle move' ) # Change the window's name we create to "Rectangle move"
clock = pygame.time.Clock() # Clocks are used to track and control the frame-rate of a game (how fast and how slow the pace of the game)
# This line creates and initializes a clock.
# color definitions, using RBG color model.
black = (0,0,0)
white = (255,255,255)
# initial center position for the square (bob)
x,y = 0,0
lastKey=0
game_loop=True
while game_loop:
for event in pygame.event.get(): # loop through all events
if event.type == pygame.QUIT:
game_loop = False # change the game_loop boolean to False to quit.
if event.type == pygame.KEYDOWN:
lastKey = event.key
#check last entered key
#lastKey equals "LEFT", "RIGHT", "UP", "DOWN" --> do the required stuff!
#set x coordinate minus 50 if left was pressed
if lastKey == pygame.K_LEFT:
x -= 50
if lastKey == pygame.K_RIGHT:
x += 50
if lastKey == pygame.K_UP:
y += 50
if lastKey == pygame.K_DOWN:
y -= 50
if event.key == pygame.K_LEFT:
x -= 50
if event.key == pygame.K_RIGHT:
x += 50
if event.key == pygame.K_UP:
y += 50
if event.key == pygame.K_DOWN:
y -= 50
# draw and update screen
window.fill( black ) # fill the screen with black overwriting even bob.
pygame.draw.rect( window, white, (x, y, 50, 50) ) # draw bob on the screen with new coordinates after its movement.
# the parameters are as follows: window: is the window object you want to draw on. white: the object color used to fill the rectangle
# (x,y,50,50) x is the x position of the left side of the rectangle. y is the y position of the upper side of the rectangle.
# In other words (x,y) is the coordinate of the top left point of the rectangle.
# 50 is the width, and 50 is the height
pygame.display.update() #updates the screen with the new drawing of the rectangle.
#fps stuff:
clock.tick(10) # this controls the speed of the game. low values makes the game slower, and large values makes the game faster.
pygame.quit()
any help would be much appreciated.
Try to save the entered key into a variable and check it after your Event-Loop.
Like this:
#...
lastKey = None
while game_loop:
for event in pygame.event.get(): # loop through all events
if event.type == pygame.QUIT:
game_loop = False # change the game_loop boolean to False to quit.
if event.type == pygame.KEYDOWN:
lastKey = event.key
#check last entered key
#lastKey equals "LEFT", "RIGHT", "UP", "DOWN" --> do the required stuff!
#set x coordinate minus 50 if left was pressed
if lastKey == pygame.K_LEFT
x -= 50
#<add the other statements here>
#(...)
I would recommend to not use that many if-statements. It could get a bit confusing after some time.
Check the following question out to keep your code brief:
Replacements for switch statement in Python?
You want to change the state of your application when you press a key. So you need a variable to keep track of that state (the state is: What direction should the box move?).
Here's a complete, minimal example that does what you're looking for. Note the comments.
import pygame, sys
pygame.init()
screen = pygame.display.set_mode((640, 480))
screen_r = screen.get_rect()
clock = pygame.time.Clock()
rect = pygame.rect.Rect(0, 0, 50, 50)
# let's start at the center of the screen
rect.center = screen_r.center
# a dict to map keys to a direction
movement = {pygame.K_UP: ( 0, -1),
pygame.K_DOWN: ( 0, 1),
pygame.K_LEFT: (-1, 0),
pygame.K_RIGHT: ( 1, 0)}
move = (0, 0)
# a simple helper function to apply some "speed" to your movement
def mul10(x):
return x * 10
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
sys.exit()
# try getting a direction from our dict
# if the key is not found, we don't change 'move'
if e.type == pygame.KEYDOWN:
move = movement.get(e.key, move)
# move the rect by using the 'move_ip' function
# but first, we multiply each value in 'move' with 10
rect.move_ip(map(mul10, move))
# ensure that 'rect' is always inside the screen
rect.clamp_ip(screen_r)
screen.fill(pygame.color.Color('Black'))
pygame.draw.rect(screen, pygame.color.Color('White'), rect)
pygame.display.update()
clock.tick(60)
I use a Rect instead of keeping track of two coordinates x and y, since that allows to make use of the move_ip and clamp_ip functions to easily move the rect inside the screen.
Here are two versions, the first demonstrates how to utilize an event loop to get continuous movement (similar to Sloth's solution, but a bit simpler for beginners who don't know dictionaries yet), the second one shows how to achieve this with pygame.key.get_pressed().
Solution 1: Check which key was pressed in the event loop and change the x and y velocities to the desired values. Then add the velocities to the rect.x and rect.y positions in the while loop.
I'd actually recommend using vectors instead of the velocity_x and velocity_y variables and another one for the actual position of your sprite. pygame.Rects can't have floats as their coordinates and so a vector or separate variables for the position would be more accurate.
import pygame as pg
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
rect = pg.Rect(100, 200, 40, 60)
velocity_x = 0
velocity_y = 0
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_d:
velocity_x = 4
elif event.key == pg.K_a:
velocity_x = -4
elif event.type == pg.KEYUP:
if event.key == pg.K_d and velocity_x > 0:
velocity_x = 0
elif event.key == pg.K_a and velocity_x < 0:
velocity_x = 0
rect.x += velocity_x
rect.y += velocity_y
screen.fill((40, 40, 40))
pg.draw.rect(screen, (150, 200, 20), rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Solution 2: Call pygame.key.get_pressed to check which key is currently being held down. Check if the left, right, up or down keys are held and then adjust the position of the sprite each frame.
pygame.key.get_pressed has the disadvantage that you can't know the order of the key presses, but the code looks a bit simpler.
import pygame as pg
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
rect = pg.Rect(100, 200, 40, 60)
velocity = (0, 0)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
keys = pg.key.get_pressed()
if keys[pg.K_d]:
rect.x += 4
if keys[pg.K_a]:
rect.x -= 4
screen.fill((40, 40, 40))
pg.draw.rect(screen, (150, 200, 20), rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()