python pygame doesn't recognize keyboard event - python

I want to make a program where I move a rectangle via the keyboard, but it doesn't move like it doesn't understand the event commands. I can't find what's wrong. I think the problem is the sequence of commands, but as a beginner, I can't find it. Can anyone help me? Thanks!
import pygame
import sys
from pygame.locals import *
fps = 30
fpsclock = pygame.time.Clock()
w = 640
h = 420
blue = (0, 0, 255)
white = (255, 255, 255)
x = w / 3
y = 350
boxa = 20
movex = 0
def drawwindow():
global screen
pygame.init()
screen = pygame.display.set_mode((w, h))
screen.fill(blue)
def drawbox(box):
if box.right > (w - boxa):
box.right = (w - boxa)
if box.left < 0:
box.left = 0
pygame.draw.rect(screen, white, box)
def main():
global x
global movex
drawwindow()
box1 = pygame.Rect(x, y, boxa, boxa)
drawbox(box1)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_RIGHT:
movex = +4
if event.key == K_LEFT:
movex = -4
if event.type == KEYUP:
if event.key == K_RIGHT:
movex = 0
if event.key == K_LEFT:
movex = 0
x += movex
pygame.display.update()
fpsclock.tick(fps)
if __name__ == '__main__':
main()

Keyboard events are being accepted properly. That can be verified by sticking a print statement inside one of the if event.key == ... blocks.
One of the problems is you are never redrawing the box after initially drawing it. Every iteration of the game loop should redraw the background (ideally only the area that changes, but that's for later) and the box in its new position. Something like this:
while True:
# [event handling code omitted for brevity]
x += movex
drawwindow()
drawbox(box1)
pygame.display.update()
fpsclock.tick(fps)
However there's another problem. Changing x or movex has no effect on anything, because they are not used anywhere once the main loop is entered. Rather than x += movex, the box would move if its x attribute was changed, as in the following code:
while True:
# [event handling code omitted for brevity]
box1.x += movex # this line changed
drawwindow() # this line added
drawbox(box1) # this line added
pygame.display.update()
fpsclock.tick(fps)
Running your code with the changes above, the box now moves.

Related

Can someone help implement gravity and running animation into my pygame code? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I'm making Mario with pygame for my class assessment this school term and am sorta new to pygame, i can't find anywhere how to properly implement gravity into my game, so when mario jumps, he comes back down. My second problem is trying to make a running animation, i want Mario to flick through pictures as he moves left and right in the order; 1, 2, 3, 2 (and over and over). If someone could help it would be great!
import pygame
import time
import random
pygame.init()
display_width = 800
display_height = 600
mario_width = 55
blue = (77, 240, 255)
green = (0, 186, 43)
ground_colour = (186, 150, 97)
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("Super Italiano Bros")
direction = 'right'
clock = pygame.time.Clock()
cloudImg = pygame.image.load('Cloud.png')
rightMarioImg = pygame.image.load('MarioRight.png')
rightMarioImg2 = pygame.image.load("MarioRight2.png")
rightMarioImg3 = pygame.image.load("MarioRight3.png")
leftMarioImg = pygame.image.load('MarioLeft.png')
leftMarioImg2 = pygame.image.load('MarioLeft2.png')
leftMarioImg3 = pygame.image.load('MarioLeft3.png')
def marioRight(x,y):
gameDisplay.blit(rightMarioImg, (x,y))
def marioLeft(x,y):
gameDisplay.blit(leftMarioImg, (x,y))
def cloud(x_cloud,y_cloud):
gameDisplay.blit(cloudImg, (x_cloud,y_cloud))
def cloud_two(x_cloud_two,y_cloud_two):
gameDisplay.blit(cloudImg, (x_cloud_two,y_cloud_two))
def game_loop():
global direction
x = (320)
y = (360)
x_cloud = (-200)
y_cloud = (-220)
x_cloud_two = (200)
y_cloud_two = (-170)
x_change = (0)
y_change = (0)
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
print(event)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -5
direction = 'left'
if event.key == pygame.K_RIGHT:
x_change = 5
direction = 'right'
if event.key == pygame.K_UP:
y_change = -60
if event.key == pygame.K_ESCAPE:
gameExit = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_UP:
x_change = 0
y_change = 60
x += x_change
y += y_change
if x <= 0:
x = 0
if y <= 300:
y = 300
if y >= 360:
y = 360
gameDisplay.fill(blue)
pygame.draw.rect(gameDisplay, ground_colour, (0,500,800,500))
pygame.draw.rect(gameDisplay, green, (0, 470, 800, 30))
cloud(x_cloud,y_cloud)
cloud_two(x_cloud_two,y_cloud_two)
if direction == 'right':
marioRight(x,y)
else:
marioLeft(x,y)
pygame.display.update()
clock.tick(60)
game_loop()
pygame.quit()
quit()
Fixed some issues and did a lot of code styling. When you press the up key he jumps a fixed amount and when you release it he goes back to the ground level. Now it animates itself. The image is changed every loop, which is probably super fast, so you will probably only want to increase the state every a certain amount of frames.
Summary of what I changed:
Imports are usually grouped in three different blocks (divided by a single blank line) and ordered alphabetically:
The first block contains all standard library imports
The second block contains third party libraries
The third block contains local imports to other files in this project
A file should have 0 not used imports
Function and variable names in python follow snake case notation: my_variable and my_function. "Constants" are written in snake upper case: MY_CONSTANT. Classes use camel case: MyClass.
dicts and lists allow you to store data in a more organized way. For example, we can have a MARIO_IMGS dict of lists that store all the mario images so they can be accessed like MARIO_IMGS['right'][0] (the first image is index 0 as lists start counting by 0).
Try to avoid global variables, I moved the variables that were only needed inside the loop to the game_loop function and the remaining top level statements that were not definitions isnide a if __name__ == '__main__': block that runs whenever this script is launched but not when this scrip is imported.
Functions are meant to be reused, having cloudand cloud_two functions that do exactly the same makes no sense. Call cloud(x_cloud, y_cloud) and then cloud(x_cloud_two, y_cloud_two).
Do not use unneeded brackets if they do not provide clarity, for example: x = (320) should be x = 320
You had a indentation error where you process events outside the event loop, so only the last event would be processed (except for exiting, that part was inside the loop so every event would be checked against pygame.QUIT)
Lines should be 80 chars or lower.
quit() at the end of a python file is not needed. quit() is mostly used to close the python interpreter from a terminal window.
import pygame
# Window width and height in px
DISPLAY_SIZE = (800, 600)
# Window title
TITLE = "Super Italiano Bros"
# Predefined colors
BLUE = (77, 240, 255)
GREEN = (0, 186, 43)
GROUND_COLOR = (186, 150, 97)
# Images
MARIO_IMGS = {
'right': [
pygame.image.load('MarioRight.png'),
pygame.image.load("MarioRight2.png"),
pygame.image.load("MarioRight3.png"),
pygame.image.load("MarioRight2.png"),
],
'left': [
pygame.image.load('MarioLeft.png'),
pygame.image.load('MarioLeft2.png'),
pygame.image.load('MarioLeft3.png'),
pygame.image.load('MarioLeft2.png'),
]
}
CLOUD_IMG = pygame.image.load('Cloud.png')
MAX_STATES = min(map(len, MARIO_IMGS.values())) # 3
def draw_mario(x, y, direction='right', state=0):
screen.blit(MARIO_IMGS[direction][state], (x, y))
def draw_cloud(x_cloud, y_cloud):
screen.blit(CLOUD_IMG, (x_cloud, y_cloud))
def game_loop():
# Mario position
x = 320
y = 360
x_change = 0
state = 0
direction = 'right'
# Cloud positions
x_cloud = -200
y_cloud = -220
x_cloud_two = 200
y_cloud_two = -170
# Clock used to limit the FPS
clock = pygame.time.Clock()
game_exit = False
while not game_exit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_exit = True
print(event)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -5
direction = 'left'
if event.key == pygame.K_RIGHT:
x_change = 5
direction = 'right'
if event.key == pygame.K_UP:
y = 300
if event.key == pygame.K_ESCAPE:
game_exit = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_change = 0
elif event.key == pygame.K_UP:
y = 360
x += x_change
if x < 0:
x = 0
elif x > DISPLAY_SIZE[0]:
x = DISPLAY_SIZE[0]
screen.fill(BLUE)
pygame.draw.rect(screen, GROUND_COLOR, (0, 500, 800, 500))
pygame.draw.rect(screen, GREEN, (0, 470, 800, 30))
draw_cloud(x_cloud, y_cloud)
draw_cloud(x_cloud_two, y_cloud_two)
draw_mario(x, y, direction, state)
# Increase the state to animate Mario
state += 1
state %= MAX_STATES # Start back from 0 if we have reached the end
pygame.display.update()
clock.tick(60)
if __name__ == '__main__':
pygame.init()
screen = pygame.display.set_mode(DISPLAY_SIZE)
pygame.display.set_caption(TITLE)
game_loop()
pygame.quit()
You should probably change your mario and cloud to Sprites. An Sprite is a class that has a rect and an image attributes that define how they are drawn, and then instead of using screen.blit(IMAGE, POSITION) you just tell pygame to draw the sprite and he will use those two attributes to draw the desired image at the position of the rect.

Python game over screen won't accept input

I'm trying to make it so when the game over screen shows the user can press space to get back into the game. Currently, when a game over happens, it displays the game over screen but accepts no input or at least doesn't do anything with the input. For some context, the game is basically about moving left and right to avoid obstacles. Currently, I only have one obstacle, but I just have not gotten to that yet. Thanks!
import pygame
import random
import math
pygame.init()
screenWidth = 700
screenHeight = 800
x = screenWidth / 2
y = (screenHeight / 4) * 3
width = 50
height = 50
win = pygame.display.set_mode((screenWidth, screenHeight))
pygame.display.set_caption("Test Game")
bg = pygame.image.load("background.png").convert()
gameover = pygame.image.load("gameover.png").convert()
bgx = (screenWidth / 6) * 2
bgy = 0
clock = pygame.time.Clock()
class enemy():
def __init__(self,c,y,width,height):
self.c = c
self.y = y
self.width = width
self.height = height
self.vel = 5
def draw(self, win):
if self.c == 1:
self.x = 250
#250
elif self.c == 2:
self.x = 350
#350
else:
self.x = 450
#450
self.y += self.vel
pygame.draw.rect(win, (0,0,255), (self.x,self.y,self.width,self.height))
evil = enemy(random.randint(1,3),0,50,50)
#def redrawGameWindow():
# evil.draw(win)
# pygame.display.update()
running = True
gameOver = False
while running:
clock.tick(60)
while gameOver:
win.blit(gameover, (0,0))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.quit()
if pygame.event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
gameOver = True
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.quit()
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
x+=100
if event.key == pygame.K_LEFT:
x-=100
win.fill((0,0,0))
win.blit(bg, (bgx, bgy))
evil.draw(win)
dist = math.hypot(evil.x - x, evil.y - y)
if dist <= 50:
print("Game Over!")
running = False
gameOver = True
pygame.draw.rect(win, (255,0,0), (x,y,width,height))
pygame.display.update()
#redrawGameWindow()
while gameOver:
win.blit(gameover, (0,0))
pygame.display.update()
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
gameOver = False
pygame.quit()
The main problem is that your game over scene is the loop behind the main while running: loop and there's no way to go back to the first loop when you reach it. When you touch the evil object, you set running = False, leave the main and enter the while gameOver: loop.
In this loop you also need to call pygame.event.pump(), otherwise the pygame.key.get_pressed() function doesn't work correctly and the window freezes after a while because the events are not handled. If you want to restart the game, you should rather use only the nested while gameOver loop. Actually, I would recommend that you restructure the scenes even more and use a finite state machine instead (here's an answer in which I use functions as scenes but check out the link in the comment below as well).
Here's a working version of your code. I've changed a few things and added comments to explain the changes.
while running:
# -----The game over scene.-----
while gameOver:
for event in pygame.event.get():
if event.type == pygame.QUIT:
# pygame.quit only uninitializes the pygame modules and
# doesn't quit the program.
pygame.quit()
# This will quit the whole program. You need to import sys.
sys.exit()
elif event.type == pygame.KEYUP: # event.type not pygame.event.type
if event.key == pygame.K_SPACE:
# Change it to False to break out of the loop.
gameOver = False
# Reset the game. You could reset the position of the
# `evil` object or instantiate a new one.
evil.x = 350
evil.y = 0
win.blit(gameover, (0,0))
pygame.display.update()
clock.tick(60) # You need to call tick in this loop as well.
# ------------------------------
# -----The main scene.-----
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
x += 100
elif event.key == pygame.K_LEFT:
x -= 100
win.fill((0,0,0))
win.blit(bg, (bgx, bgy))
evil.draw(win)
dist = math.hypot(evil.x - x, evil.y - y)
if dist <= 50:
print("Game Over!")
# running = False # This would stop the main loop.
gameOver = True
pygame.draw.rect(win, (255,0,0), (x,y,width,height))
pygame.display.update()
clock.tick(60)
# The other while loop was removed.
pygame.quit()

Pygame - why my game repeats instead of closing?

First of all, sorry for my noob question.
So I'm trying to learn pygame, and to do so I created a """game""' that is supposed to quit whenever someone clicks the close button ("x" button") in the top corner of the window, but instead the whole game restarts itself.
I use "Game = False" to point that the game needs to close, since everything that the game does is located in a loop that only starts with "Game = True", and at the end of the script (after the loop ends) there is a "pygame.quit()" and a "sys.exit()". This was was working fine, but recenlty I added the following lines:
if x < 0 or x > display_width - spacecore_width:
game_over()
Those are supposed to call a "game_over" function, that resets the game by calling the another function that covers the whole game (the loop is located inside of this function). But this is not supposed to happen when someone clicks the "x" button to quit the game, and I have no idea of why this also happens when such thing is done (besides working as intended, by only repeating the game if "x < 0 or x > display_width - spacecore_width".
I already know how to avoid it (I just need to replace "Game = False" with those quit commands) but I would like to know why the problem happens in the first place.
Here's the full code:
import pygame, sys, time
pygame.init()
display_width = 800
display_height = 600
DISPLAY = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("Não enconste nas bordas da tela")
WHITE = (255,255,255)
RED = (255,0,0)
SPACECORE = pygame.image.load("spacecore.png")
spacecore_width = 100
clock = pygame.time.Clock()
def text_and_rect(text,font):
text_surface = font.render(text,True,RED)
return text_surface, text_surface.get_rect()
def message_display(text):
font = pygame.font.Font("freesansbold.ttf",50)
text_surf, text_rect = text_and_rect(text,font)
text_rect.center = ((display_width/2),(display_height/2))
DISPLAY.blit(text_surf,text_rect)
pygame.display.update()
time.sleep(2)
def game_over():
message_display("Você morreu de morte morrida")
game_loop()
def player(x,y):
DISPLAY.blit(SPACECORE,(x,y))
def game_loop():
Game = True
x = display_width * 0.5
y = display_height * 0.8
mod_x = mod_y = 0
while Game == True:
pygame.display.update()
DISPLAY.fill(WHITE)
for event in pygame.event.get():
if event.type == pygame.QUIT:
Game = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
mod_x = -5
if event.key == pygame.K_RIGHT:
mod_x = 5
if event.key == pygame.K_UP:
mod_y = -5
if event.key == pygame.K_DOWN:
mod_y = 5
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
mod_x = 0
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
mod_y = 0
x += mod_x
y += mod_y
clock.tick(60)
player(x,y)
if x < 0 or x > display_width - spacecore_width:
game_over()
game_loop()
pygame.quit()
sys.exit()
Make sure you're breaking out of the loop and then calling the loop function again:
while Game == True:
# some here code
if x < 0 or x > display_width - spacecore_width:
break # leave while loop
# proceed elsewhere
game_over()
Or alternatively cut the flow back to game_loop in game_over:
def game_over():
message_display("Você morreu de morte morrida")
# game_loop() # removed
Currently you're never breaking out of loops when game is over - you're just calling other functions (which in turn active another loop) in a mutually-recursive manner, effectively this never ends any game.
Also, if you receive a quit event you may want to break out of the for loop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
Game = False
break

Pygame local variable referenced before assignment

I want to make a rectangle to run using keys but I take an error on line 34:
UnboundLocalError: local variable 'x' referenced before assignment
I can't fix that. Please help me.
Here is my code:
import pygame
import sys
from pygame.locals import *
fps = 30
fpsclock = pygame.time.Clock()
w = 640
h = 420
blue = (0, 0, 255)
white = (255, 255, 255)
x = w / 3
y = 350
boxa = 20
movex = 0
def drawwindow():
global screen
pygame.init()
screen = pygame.display.set_mode((w, h))
screen.fill(blue)
def drawbox(box):
if box.right > (w - boxa):
box.right = (w - boxa)
if box.left < 0:
box.left = 0
pygame.draw.rect(screen, white, box)
def main():
drawwindow()
box1 = pygame.Rect(x, y, boxa, boxa)
drawbox(box1)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_RIGHT:
movex = +4
if event.key == K_LEFT:
movex = -4
if event.type == KEYUP:
if event.key == K_RIGHT:
movex = 0
if event.key == K_LEFT:
movex = 0
x += movex
pygame.display.update()
fpsclock.tick(fps)
if __name__ == '__main__':
main()
The name x is in the global scope. Therefore, in order to modify its value inside the function main, you need to declare it as being global with global:
def main():
global x
...
x += movex
Note that you only need to do this if you modify a global. Accessing their values works just fine.

I am unable to make an object move from point A to point B without user input using python 2.7 with pygame

I am attempting to make a game where the 'player' (who spawns as the moon furthest to the right) can move around using the 'WASD' keys and enemies fall from the top of the screen in order to hit the player and end the game. I am currently doing this for an assignment and i have set up a test to see whether it is possible for me to do this and so far i have been able to create a 'player' object that is controllable by the 'WASD' keys and make a screen which requires players to press 'SPACE' to play the game. I am having trouble though with being able to have the enemies fall on the 'Y' axis down the screen smoothly. The picture moves down the screen only when the user presses or releases a key, this creates a jagged movement or when the user moves the mouse over the pygame screen, creating a very smooth movement.
#import necessary modules and pygame.
import pygame, sys, random
from pygame.locals import *
#set global variables
pygame.init()
WINDOWWIDTH = 800
WINDOWHEIGHT = 800
BACKGROUNDCOLOUR = (255,255,255)
TEXTCOLOUR = (0,0,0)
FPS = 30
ENEMYMINSIZE = 10
BOMBSAWAY = -1
ENEMYMAXSIZE = 40
ENEMYMINSPEED = 1
ENEMYMAXSPEED = 10
ADDNEWENEMYRATE = 5
PLAYERMOVERATE = 5
FSIZE = 48
BLUE = (0,0,255)
global PLAY
PLAY = False
global fpsClock
fpsClock = pygame.time.Clock()
# set up pygame and GUI
MainClock = pygame.time.Clock()
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
windowSurface.fill(BACKGROUNDCOLOUR)
pygame.display.set_caption('Bubble Dash')
enemy = pygame.image.load('player.jpg')
char = pygame.image.load('player.jpg')
# set up fonts
basicFont = pygame.font.SysFont(None, 48)
# set up the text
text = basicFont.render('Press any key to play!', True, (255,255,0))
textRect = text.get_rect()
textRect.centerx = windowSurface.get_rect().centerx
textRect.centery = windowSurface.get_rect().centery
# draw the text onto the surface
# set up x and y coordinates
# music
windowSurface.blit(text, textRect)
def playgame(PLAY):
x,y = 0,0
movex,movey = 0,0
charx=300
chary=200
direction = 'down'
enemyx= 10
enemyy=10
while PLAY:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == ord('m'):
pygame.mixer.music.stop()
if event.key == ord('n'):
pygame.mixer.music.play()
if event.key == ord('a'):
movex = -0.5
if event.key == ord('d'):
movex = 0.5
if event.key == ord('w'):
movey = -0.5
if event.key == ord('s'):
movey = 0.5
if event.type ==KEYUP:
if event.key == ord('a'):
movex = 0
if event.key == ord('d'):
movex = 0
if event.key == ord('w'):
movey = 0
if event.key == ord('s'):
movey = 0
if direction == 'down':
enemyy += 7
windowSurface.fill(BLUE)
windowSurface.blit(char, (charx, chary))
windowSurface.blit(enemy, (enemyx, enemyy))
pygame.display.update()
charx+=movex
chary+=movey
def playertopresskey(PLAY):
while True: # main game loop
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_SPACE:
PLAY = True
if PLAY == True:
playgame(PLAY)
pygame.display.update()
playertopresskey(PLAY)
I would like for the 'enemy' object to be able to fall from the top without the user either needing to keypress or to key up or to have to move the mouse on the screen, rather the 'enemy' would just fall from the top as soon as the subroutine is called.
You may need to tweak it a bit because it is set up for me at the moment but i deleted a few things that would give you bother. Hopefully someone can help me. Thanks.
The below is link to a picture similar to mine which you can download and replace in the code for both the 'char' and the 'enemy' variables to view this yourself for i cannot access the courses at the present time.
http://www.roleplaygateway.com/roleplay/the-five-elements/characters/miss-joy/image
I found your error, you would have caught it yourself if you would have divided your code into some functions. Here is the problem:
for event in pygame.event.get():
if event.type == QUIT:
...
if event.type == KEYDOWN:
...
if event.type == KEYUP:
...
if direction == 'down':
enemyy += 7
your code moving the enemy is called only when an event is waiting in the queue. Move it out of the loop, and you will be good to go.
You have to change indention for:
if direction == 'down':
enemyy += 7
Now it is inside for event in pygame.event.get():

Categories

Resources