I am messing around with pygame and I am eventually working towards a pong clone. I implemented player movement with the arrow keys and when ever I switch from going up to immediately going down, my player freezes and won't move again until I press that direction key again. Here is my code:
import sys, pygame
pygame.init()
display_width = 640
display_height = 480
display = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("Test Game")
black = (0,0,0)
white = (255,255,255)
clock = pygame.time.Clock()
running = True
class Player:
def __init__(self,x,y,hspd,vspd,color,screen):
self.x = x
self.y = y
self.hspd = hspd
self.vspd = vspd
self.color = color
self.screen = screen
def draw(self):
pygame.draw.rect(self.screen,self.color,(self.x,self.y,32,32))
def move(self):
self.x += self.hspd
self.y += self.vspd
player = Player(0,0,0,0,black,display)
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
player.hspd = 0
if event.key == pygame.K_LEFT:
player.hspd = 0
if event.key == pygame.K_UP:
player.vspd = 0
if event.key == pygame.K_DOWN:
player.vspd = 0
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
player.hspd = 4
if event.key == pygame.K_LEFT:
player.hspd = -4
if event.key == pygame.K_UP:
player.vspd = -4
if event.key == pygame.K_DOWN:
player.vspd = 4
#Clear the screen
display.fill(white)
#Move objects
player.move()
#Draw objects
player.draw()
#Update the screen
pygame.display.flip()
print "I made it!"
pygame.quit()
sys.exit()
I suggest you work with key.get_pressed() to check for the current set of pressed keys.
In your scenario - when you press down and release up (in that order) - the speed is set to 0, so you need to inspect the keys pressed not just by the current event.
Here is a working version of the relevant part:
def current_speed():
# uses the fact that true = 1 and false = 0
currently_pressed = pygame.key.get_pressed()
hdir = currently_pressed[pygame.K_RIGHT] - currently_pressed[pygame.K_LEFT]
vdir = currently_pressed[pygame.K_DOWN] - currently_pressed[pygame.K_UP]
return hdir * 4, vdir * 4
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
player.hspd, player.vspd = current_speed()
#Clear the screen
display.fill(white)
#Move objects
player.move()
#Draw objects
player.draw()
#Update the screen
pygame.display.flip()
To expand on LPK's answer, your key down (for event.key == pygame.K_DOWN) is likely being processed before your key up (from event.key == pygame.K_UP) is processed. So while both are down (and you can confirm this), you may experience movement, until you release the up key.
your problem is here:
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
player.hspd = 0
if event.key == pygame.K_LEFT:
player.hspd = 0
if event.key == pygame.K_UP:
player.vspd = 0
if event.key == pygame.K_DOWN:
player.vspd = 0
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
player.hspd = 4
if event.key == pygame.K_LEFT:
player.hspd = -4
if event.key == pygame.K_UP:
player.vspd = -4
if event.key == pygame.K_DOWN:
player.vspd = 4
I am guessing that your key event down is still consumed when u switch the keys immediately, meaning no other key down event is getting triggered as long as the first event didn't fire its key up event yet.
EDIT: maybe its better to check if the player is moving and if so just reverse speed . Then you would only need to check the down event.
Otherwise your event will be consumed and not checked properly.
For your method you would need to store the occurred key events since the last frame and check that list.
Related
im new to programming so im following some tutorials online to make a basic game. in order to create the movement for my character, i followed this tutorial
https://www.youtube.com/watch?v=4aZe84vvE20&t=692s&ab_channel=ClearCode
after following the rotation part of the video, i tried testing it out. however, it does not work for some reason. im not sure why this is. i do not get an error, but the character stays in one place and does not rotate once i press the left and right arrow keys. could somebody have a look at my code and let me know what i have done wrong? thanks a lot
import pygame
pygame.init()
window = pygame.display.set_mode((650, 650))
pygame.display.set_caption ("Game")
green = (0,255,0)
window.fill(green)
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.og_image = pygame.image.load("player1.png")
self.image = self.og_image
self.rect = self.image.get_rect (center = (345,345))
self.angle = 0
self.rotate_speed = 1
self.direction = 0
def rotate(self):
if self.direction == 1:
self.angle -= self.rotate_speed
print(self.angle)
elif self.direction == -1:
self.angle += self.rotate_speed
print(self.angle)
self.image = pygame.transform.rotozoom(self.og_image, self.angle, 1)
self.rect = self.image.get_rect(center = (self.rect.center))
def update(self):
self.rotate()
player_1 = Player()
players = pygame.sprite.GroupSingle()
players.add(player_1)
players.draw(window)
run = True
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_RIGHT:
players.sprite.direction += 1
if event.key == pygame.K_LEFT:
players.sprite.direction -= 1
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
players.sprite.direction -= 1
if event.key == pygame.K_LEFT:
players.sprite.direction += 1
players.update()
pygame.display.update()
pygame.quit()
You have to draw the Sprites in the Group (players.draw(window)) and you have to clear the display (window.fill(green)) before drawing the objects. Note, you have to do this in the application loop rather than the event loop (It's a matter of Indentation):
run = True
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_RIGHT:
players.sprite.direction += 1
if event.key == pygame.K_LEFT:
players.sprite.direction -= 1
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
players.sprite.direction -= 1
if event.key == pygame.K_LEFT:
players.sprite.direction += 1
# INDENTATION
#<--|
players.update()
window.fill(green)
players.draw(window)
pygame.display.update()
I am currently following a pygame tutorial on a chromebook on which i have installed and linux to use IDLE. i am writing a block of code which assigns and x-axis increase or decrase to the arrow keys:
import pygame
pygame.init()
displayWidth = 800
displayHeight = 600
black = (0, 0, 0)
white = (255, 255, 255)
clock = pygame.time.Clock()
crashed = False
carImg = pygame.image.load('racecar.png')
def car(x,y):
gameDisplay.blit(carImg, (x,y))
x = (displayWidth * 0.45)
y = (displayHeight * 0.8)
x_change = 0
car_speed = 0
gameDisplay = pygame.display.set_mode((displayHeight, displayWidth))
pygame.display.set_caption('Zoomer')
clock = pygame.time.Clock()
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
x_change = -5
elif event.key == pygame.K_RIGHT:
x_change = 5
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_change = 0
x += x_change
gameDisplay.fill(white)
car(x,y)
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
when i try to run the code, the car sprite won't budge. is this due to the fact that i am on chromebook and key names are different or is it an other reason? thanks.
It doesn't work because you have wrong indentations.
You check pygame.KEYDOWN inside if ... pygame.QUIT: which is executed only when you close window.
You need all if event.type start in the same column
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
x_change = -5
elif event.key == pygame.K_RIGHT:
x_change = 5
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_change = 0
I recently started tinkering a bit with pygame and I made this little piece of code (following a tutorial):
import pygame
pygame.init()
display_width = 1920
display_height = 1080
black = (0,0,0)
white = (255,255,255)
red = (255,0,0)
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("Galvadon's Adventure")
clock = pygame.time.Clock()
galvadon = pygame.image.load("galvadonModelVersion1.0.png")
def galvadonIsHere(x,y):
gameDisplay.blit(galvadon,(x,y))
x = (display_width * 0.30)
y = (display_height * 0.2)
y_change = 0
crashed = False
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_DOWN:
y_change = 5
elif event.key == pygame.K_UP:
y_change = -5
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
y_change = 0
y += y_change
gameDisplay.fill(white)
galvadonIsHere(x,y)
print(event)
pygame.display.update()
clock.tick(30)
pygame.quit()
quit()
and to an extent this code works, the problem is that after I move the image using the up and down arrows, the image starts to respond to any mouse movement by just gliding in the direction it last moved towards. I probably missed something out, but I just can't seem to find what it is. I looked at various websites looking for the answer but I couldn't find anyone asking a similar question, hence why I make this topic.
There are couple of things that you can do better here. But lets start with fixing the issue.
'Bug' is that after you press keys, y_change is set and never reset for next engine loop. This should help you:
...
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_DOWN:
y_change = 5
elif event.key == pygame.K_UP:
y_change = -5
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
y_change = 0
y += y_change
gameDisplay.fill(white)
galvadonIsHere(x, y)
print(event)
pygame.display.update()
clock.tick(30)
y_change = 0
...
Pay close attention at the last line in my snippet. Here you reset y_change so in next engine loop, whatever happens or doesn't, it wont affect position of your image.
Refactor and improve
First, you can agree that checking if event type is KEY_DOWN and nesting checks if it is particular key button is pain to read and work with. Not to mention you check if event type is KEYUP even though you know that is KEYDOWN here:
if event.type == pygame.KEYDOWN:
...
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
y_change = 0
So, what I do is define helper function, that can be usual one or lambda. Now, many would argue that lambda functions are affecting readability but here it can certainly help you with these checks.
Example:
key_pressed = lambda event, key: event.type == pygame.KEYDOWN and event.key == key
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
break
elif key_pressed(event, pygame.K_DOWN):
y_change = 5
elif key_pressed(event, pygame.K_UP):
y_change = -5
else:
y_change = 0
y += y_change
gameDisplay.fill(white)
galvadonIsHere(x, y)
print(event)
pygame.display.update()
clock.tick(30)
y_change = 0
One more thing, pay attention at condition that there was pygame.QUIT event. break exits the for loop and then while loop ends too. This way you do not process any queued events, nor you update and blit image.
Your problem is in the while loop. The if statement for keyup is in the if statement for keydown, so it is preventing it from working. The following solves your problem:
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_DOWN:
y_change = 5
elif event.key == pygame.K_UP:
y_change = -5
else:
y_change = 0
Hope this helps.
This question already has answers here:
Pygame how to fix 'trailing pixels'?
(4 answers)
Closed 5 years ago.
I'am building a simple game using pygame ,the function of this game is to control an image and make it move up/down left/right but i got this problem, the image repeats itself when moving down or aside
import pygame
pygame.init()
display_width = 1080
display_height = 1080
screen = pygame.display.set_mode((display_width,display_height))
Clock = pygame.time.Clock()
pygame.display.set_caption('DEMO')
black=(0,0,0)
white=(255,255,255)
red=(255,0,0)
green=(0,255,0)
blue=(0,0,255)
screen.fill(white)
face=pygame.image.load('H:\\brain.jpg')
def brain(x,y):
screen.blit(face,(x,y))
x1 =(display_width*0.5)
y1 =(display_height*0.5)
x_change =0
y_change =0
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -5
elif event.key == pygame.K_RIGHT:
x_change = 5
elif event.key == pygame.K_DOWN:
y_change = 5
elif event.key == pygame.K_UP:
y_change = -5
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_change = 0
if event.type == pygame.KEYUP:
if event.key == pygame.K_DOWN or event.key == pygame.K_UP:
y_change = 0
x1 += x_change
y1 += y_change
print(event)
brain(x1,y1)
pygame.display.update()
Clock.tick(100)
pygame.quit()
quit()
You have to blit your background at every iteration of the while-loop:
while True:
for event in pygame.event.get():
#get keyboard input
screen.fill((255, 255, 255)) #or, you can draw a background image
x1 += x_change
y1 += y_change
print(event)
brain(x1,y1)
pygame.display.update()
You need to blank out the old image, as well as drawing the new one. What you see is the residue: the portion of the previous images that wasn't covered by the white (background color) of the new image.
One easy way to handle this is to expand your drawn image to include a white border at least as large as the size of the motion.
I have the game come up and the rectangle rendered.
When I press my KEYDOWN it doesn't move the rectangle, it just makes it longer.
I have tried tons of stuff. I am new to Pygame.
Any help would be amazing.
Here is the code:
import pygame
import time
import random
import math
import sys
pygame.init()
display_width = 1200
display_height = 800
white = (255,255,255)
black = (0,0,0)
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('Vertical Pong')
clock = pygame.time.Clock()
def pongBoard(x,y,):
pygame.draw.rect(gameDisplay,white,(x,y,250,25))
def gameLoop():
x = 325
y = 750
xChange = 0
inGame = True
while inGame:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a or event.key == pygame.K_LEFT:
xChange = -5
print("Left")
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_d or event.key == pygame.K_RIGHT:
xChange = 5
print("Right")
if event.type == pygame.KEYUP:
if event.key == pygame.K_d or event.key == pygame.K_RIGHT:
xChange = 0
if event.type == pygame.KEYUP:
if event.key == pygame.K_a or event.key == pygame.K_LEFT:
xChange = 0
pongBoard(x,y)
x += xChange
pygame.display.update()
clock.tick(60)
gameLoop()
pygame.quit()
quit()
So the problem is this:
The rectangle is being constantly redrawn at a different coord, but the screen is not being drawn over the rectangle to cover up the part that is not supposed to be there. In simpler terms, we need to constantly draw the background.
So now the code in the main game loop:
while inGame:
#This code below draws the background
pygame.draw.rect(gameDisplay, black, (0, 0, display_width, display_height))
That is it! The background will constantly cover up the pong ball, and the pong ball will be constantly blitted to a new position!
P.S, there is a better way to do arrow key movement here: How to get keyboard input in pygame?
it actualy does move it, but the old one just stays there, making it look like it does not move but just grows. one way to change that would be to change the old ones color to the background color
try this code it works :-)
import pygame
import time
import random
import math
import sys
pygame.init()
display_width = 1200
display_height = 800
white = (255,255,255)
black = (0,0,0)
red = (123,11,45)
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('Vertical Pong')
clock = pygame.time.Clock()
def pongBoard(x,y,xold):
pygame.draw.rect(gameDisplay,white,[x,y,250,25])
pygame.draw.rect(gameDisplay,red,[xold,y,250,25])
def gameLoop():
x = 325
y = 750
xChange = 0
inGame = True
while inGame:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a or event.key == pygame.K_LEFT:
xChange = -50
pongBoard(x,y,xold)
print("Left")
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_d or event.key == pygame.K_RIGHT:
xChange = 50
pongBoard(x,y,xold)
print("Right")
if event.type == pygame.KEYUP:
if event.key == pygame.K_d or event.key == pygame.K_RIGHT:
xChange = 0
if event.type == pygame.KEYUP:
if event.key == pygame.K_a or event.key == pygame.K_LEFT:
xChange = 0
xold = x
x += xChange
xold = x-xChange
pygame.display.update()
clock.tick(60)
gameLoop()
pygame.quit()
quit()