Optimizing Keyboard Input in Pygame - python-2.7

I am creating a very simple game using the pygame library in which the player moves a square around the screen with the arrow keys. Unfortunately, the response of the controls are proving to be a problem.
The controls work fine until you switch direction e.g. left to right. When the player switches direction, the square the player is controlling jars to a halt.
The code for the controls looks like this in my game:
def move(self, event):
speed = 6
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
self.rect.x -= speed
if event.key == pygame.K_RIGHT:
self.rect.x += speed
if event.key == pygame.K_DOWN:
self.rect.y += speed
if event.key == pygame.K_UP:
self.rect.y -= speed
This method is located in a class called Block (I stole Block from the pygame docs) that just makes a sprite for the player alongside providing movement.
The function move() is actually called inside the main.py file like this:
while going:
clock.tick(60)
# event handling
for event in pygame.event.get():
if event.type == QUIT:
going = False
# controller input
player.move(event)
Where player is an instance of the Block class and event is just an item from the event queue of my game.
I tried using the pygame.key.get_pressed function (which did work better) but I would rather use the pygame.KEYDOWN locals alternatively because it returns the key presses as events in the event queue rather than booleans returned from a list.
If you are more investigative than your average joe you can take a look at the "full" game here: https://github.com/catmes/Project_React
You will need pygame 1.9.2 and python 2.7. Keep in my mind that the game itself is more a learning exercise so don't surprised when you see bad code (that will be fixed later of course ;) ).
Thanks for stopping by and giving this question a peek, I do appreciate your time.

I've tried out a smooth way to move around a player that works pretty well. It basically keeps track of the key presses in a list and moves the player based on the last key pressed. When you press a key it'll add a speed variable at the first position in the list, and when you release the key it'll remove that variable. In the update method you'll add the first speed variable of the list to corresponding x and y value.
import pygame
pygame.init()
screen = pygame.display.set_mode((720, 480))
class Player(pygame.sprite.Sprite):
def __init__(self):
super(Player, self).__init__()
self.rect = pygame.Rect((0, 0), (32, 32))
self.image = pygame.Surface((32, 32))
self.image.fill((255, 255, 255))
self.velocity = [0, 0] # It's current velocity.
self.speed = 4 # The speed the player will move.
self.dx = [] # Keeps track of the horizontal movement.
self.dy = [] # Keeps track of the vertical movement.
def update(self):
try:
self.rect.x += self.dx[0] # Index error if the list is empty.
except IndexError:
self.rect.x += 0
try:
self.rect.y += self.dy[0] # Index error if the list is empty.
except IndexError:
self.rect.y += 0
player = Player()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
raise SystemExit
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
player.dx.insert(0, -player.speed)
elif event.key == pygame.K_d:
player.dx.insert(0, player.speed)
elif event.key == pygame.K_w:
player.dy.insert(0, -player.speed)
elif event.key == pygame.K_s:
player.dy.insert(0, player.speed)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.dx.remove(-player.speed)
elif event.key == pygame.K_d:
player.dx.remove(player.speed)
elif event.key == pygame.K_w:
player.dy.remove(-player.speed)
elif event.key == pygame.K_s:
player.dy.remove(player.speed)
player.update()
screen.fill((0, 0, 0))
screen.blit(player.image, player.rect)
pygame.display.update()
# print player.dx, player.dy # Uncomment to see what's happening in action!

Related

Pygame, my sprite looks wierd

I'm working on this project for Literature class and I'm planning on making a game with pygame based of this book we were reading.
Here's the code:
import pygame, sys
from pygame.locals import *
pygame.init()
FPS = 30
fpsClock = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((400, 300), 0, 32)
pygame.display.set_caption('Animation')
WHITE = (255, 255, 255)
romeo = pygame.image.load('rome.png.png')
romeo = pygame.transform.scale(romeo, (50, 50))
romeox = 10
romeoy = 10
while True: # the main game loop
keys = pygame.key.get_pressed()
if keys[K_RIGHT]:
romeox += 5
if keys[K_LEFT]:
romeox -= 5
if keys[K_UP]:
romeoy -=5
if keys[K_DOWN]:
romeoy += 5
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
DISPLAYSURF.blit(romeo, (romeox, romeoy))
pygame.display.update()
fpsClock.tick(FPS)
Unfortunately, the sprite seems to leave a trail of it's own copies while it moves.
Here
Please help!
Seems like you forgot to clear the screen every iteration
# Fill surface with black color
DISPLAYSURF.fill((0, 0, 0))
Insert it above the line that blits the sprite onto the screen.

circle inside a rect with a button click

I am trying to create a game and i have to make this plexus made of buttons. Every time the player clicks the button, it should appear a small circle inside the pressed button. I tried to make a draw_circle function but when i place it on the action variable inside the buttons, I get this errror. Global name x is not defined.
import pygame
pygame.init()
display_width = 900
display_height = 600
black = (0,0,0)
white = (255,255,255)
red = (250,0,0)
green = (0,250,0)
bright_red =(200,0,0)
bright_green = (0,200,0)
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("A MATH'S GAME")
def draw_circle():
pygame.draw.circle(gameDisplay,black,(x,y),100)
def button(msg,x,y,w,h,ic,ac,action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x+w > mouse[0] > x and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay, ac,(x,y,w,h))
if click[0] == 1 and action != None:
action()
else:
pygame.draw.rect(gameDisplay, ic,(x,y,w,h))
smallText = pygame.font.SysFont("comicsansms",20)
textSurf, textRect = text_objects(msg, smallText)
textRect.center = ( (x+(w/2)), (y+(h/2)) )
gameDisplay.blit(textSurf, textRect)
def text_objects(text, font):
textSurface = font.render(text, True, black)
return textSurface, textSurface.get_rect()
def messgae_display(text):
largeText = pygame.font.Font("Arial",60)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = ((display_width/2),(display_height/2))
gameDisplay.blit(TextSurf, TextRect)
pygame.display.update()
def quitgame():
pygame.quit()
quit()
def game_intro():
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
largeText = pygame.font.SysFont("comicsansms",115)
TextSurf, TextRect = text_objects("A math's game", largeText)
TextRect.center = ((display_width/2),(display_height/2))
gameDisplay.blit(TextSurf, TextRect)
button("GO!",150,450,100,50,green,bright_green,draw_circle)
button("Quit",550,450,100,50,red,bright_red,quitgame)
pygame.display.update()
def game_loop():
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
largeText = pygame.font.SysFont("comicsansms",50)
TextSurf, TextRect = text_objects("A math's game", largeText)
TextRect.center = ((display_width/2),100)
gameDisplay.blit(TextSurf, TextRect)
button(None,200,250,50,50,bright_red,red,None)
button(None,200,300,50,50,bright_red,red,None)
button(None,200,350,50,50,bright_red,red,None)
button(None,200,400,50,50,bright_red,red,None)
button(None,200,450,50,50,bright_red,red,None)
button(None,200,500,50,50,bright_red,red,None)
button(None,250,250,50,50,bright_red,red,None)
button(None,250,300,50,50,bright_red,red,None)
button(None,250,350,50,50,bright_red,red,None)
button(None,250,400,50,50,bright_red,red,None)
button(None,250,450,50,50,bright_red,red,None)
button(None,250,500,50,50,bright_red,red,None)
button(None,300,250,50,50,bright_red,red,None)
button(None,300,300,50,50,bright_red,red,None)
button(None,300,350,50,50,bright_red,red,None)
button(None,300,400,50,50,bright_red,red,None)
button(None,300,450,50,50,bright_red,red,None)
button(None,300,500,50,50,bright_red,red,None)
button(None,350,250,50,50,bright_red,red,None)
button(None,350,300,50,50,bright_red,red,None)
button(None,350,350,50,50,bright_red,red,None)
button(None,350,400,50,50,bright_red,red,None)
button(None,350,450,50,50,bright_red,red,None)
button(None,350,500,50,50,bright_red,red,None)
button(None,400,250,50,50,bright_red,red,None)
button(None,400,300,50,50,bright_red,red,None)
button(None,400,350,50,50,bright_red,red,None)
button(None,400,400,50,50,bright_red,red,None)
button(None,400,450,50,50,bright_red,red,None)
button(None,400,500,50,50,bright_red,red,None)
button(None,450,250,50,50,bright_red,red,None)
button(None,450,300,50,50,bright_red,red,None)
button(None,450,350,50,50,bright_red,red,None)
button(None,450,400,50,50,bright_red,red,None)
button(None,450,450,50,50,bright_red,red,None)
button(None,450,500,50,50,bright_red,red,None)
button(None,500,250,50,50,bright_red,red,None)
button(None,500,300,50,50,bright_red,red,None)
button(None,500,350,50,50,bright_red,red,None)
button(None,500,400,50,50,bright_red,red,None)
button(None,500,450,50,50,bright_red,red,None)
button(None,500,500,50,50,bright_red,red,None)
button(None,550,250,50,50,bright_red,red,None)
button(None,550,300,50,50,bright_red,red,None)
button(None,550,350,50,50,bright_red,red,None)
button(None,550,400,50,50,bright_red,red,None)
button(None,550,450,50,50,bright_red,red,None)
button(None,550,500,50,50,bright_red,red,None)
button(None,600,250,50,50,bright_red,red,None)
button(None,600,300,50,50,bright_red,red,None)
button(None,600,350,50,50,bright_red,red,None)
button(None,600,400,50,50,bright_red,red,None)
button(None,600,450,50,50,bright_red,red,None)
button(None,600,500,50,50,bright_red,red,None)
button(None,650,250,50,50,bright_red,red,None)
button(None,650,300,50,50,bright_red,red,None)
button(None,650,350,50,50,bright_red,red,None)
button(None,650,400,50,50,bright_red,red,None)
button(None,650,450,50,50,bright_red,red,None)
button(None,650,500,50,50,bright_red,red,None)
pygame.display.update()
pygame.init()
game_intro()
In the line button("GO!",150,450,100,50,green,bright_green,draw_circle) you aren't giving draw_circle a value for x or y, so they are, in fact, not defined. You need to pass these down to to the draw_circle function.
Inside of your button function, pass the arguments down to draw_circle like:
if click[0] == 1 and action != None:
action(x, y)

GPIO pins and pygame

I am planning on making a game with pygame using gpio buttons. Here is the code:
from gpiozero import Button
import pygame
from time import sleep
from sys import exit
up = Button(2)
left = Button(3)
right = Button(4)
down = Button(14)
fps = pygame.time.Clock()
pygame.init()
surface = pygame.display.set_mode((1300, 700))
x = 50
y = 50
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
break
if up.is_pressed:
y -= 5
if down.is_pressed:
y += 5
if left.is_pressed:
x -= 5
if right.is_pressed:
x += 5
surface.fill((0, 0, 0))
pygame.draw.circle(surface, (255, 255, 255), (x, y), 20, 0)
pygame.display.update()
fps.tick(30)
However, when I press on the X button on the top of the window, it doesn't close. Is there a possible solution for this?
EDIT: Everyone is giving the same answer, that I am not adding a for loop to check events and quit. I did put that, here in my code:
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
break
I have also tried sys.exit().
EDIT 2: #Shahrukhkhan asked me to put a print statement inside the for event in pygame.event.get(): loop, which made the loop like this:
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
print "X pressed"
break
root#raspberrypi:~/Desktop# python game.py
X pressed
X pressed
There are two possible ways to close the pygame window .
after the end of while loop simply write
import sys
while 1:
.......
pygame.quit()
sys.exit()
2.instead of putting a break statement ,replace break in for loop immediately after while as
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
......
You need to make a event and within it you need to quit the pygame
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()

Pygame Collision with Changing sprites

Alright,
I've looked everywhere and still can't figure out PyGame collisions. I have two sprites: A lander and a moon surface. The moon is an uneven surface with a transparent background, while the lander is a sprite that changes through images with L,D,UP keypresses.
I'm doing something wrong when it comes to collision detection. Otherwise, everything in my program is going swimmingly
This is my lander class:
class ShipClass(pygame.sprite.Sprite):
def __init__(self, image_file, position):
pygame.sprite.Sprite.__init__(self)
self.imageMaster = pygame.image.load(image_file)
self.image = self.imageMaster
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.imageMaster)
self.rect.topleft = position
## This doesn't work
# def checkCollision(self, Mask):
# if Mask.overlap_area
def update(self):
self.rect = self.image.get_rect()
And this is my lunar surface class:
class MoonSurface(pygame.sprite.Sprite):
def __init__(self, image_file, position):
pygame.sprite.Sprite.__init__(self)
self.imageMaster = pygame.image.load(image_file).convert_alpha()
self.image = self.imageMaster
self.rect = self.image.get_rect()
self.rect.topleft = position
What do I need to add to this so that while in my game if the lander hits the moon it sets variable stop to 1 and crash to 1? I plan on also adding landing pads with the MoonSurface class and when it hits them only sets stop to 1.
My sprite is moved as follows:
speed = [0,0]
position = [width-524,height-718]
gravity = 0.05
StartingFuel = 100
done = False
while not done:
event = pygame.event.poll()
if event.type == QUIT:
pygame.quit()
sys.exit(0)
elif event.type == pygame.KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit(0)
else:
lander = ShipClass('lander.png', position)
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
lander = ShipClass('landerLeft.png', position)
# lander.set_colorkey(BLACK)
speed[0] += 0.3
fuel -= 1
if keys[pygame.K_RIGHT]:
lander = ShipClass('landerRight.png', position)
# lander.set_colorkey(BLACK)
speed[0] -= 0.3
fuel -= 1
if keys[pygame.K_UP]:
lander = ShipClass('landerUp.png', position)
# lander.set_colorkey(BLACK)
speed[1] -= 0.4
fuel -= 1
speed[1] += gravity
position[0] += speed[0]
position[1] += speed[1]
if position[0] < 0:
position[0] = width
elif position[0] > width:
position[0] = 0
screen.blit(lander.image, position)
It looks like you want to check if the two objects overlap. Luckily Pygame has a built- in function colliderect which checks if two rectangles are overlapping. I've put it into a class method here, as I saw your objects had Pygame rectangle variables named rect:
def overlaps (self, other_rect):
return self.rect.colliderect(other_rect)
Here's the documentation: pygame.Rect

How to reset time back to 0 in pygame?

I am trying to make a game in pygame where you need to click the image to gain a score.My program works but i still need to fix 2 bugs:
Bug 1
When the user presses 'c' that is play again,the **time does not reset back to 0. I want it back to 0 , so that every 10 seconds the game gets over and you cant play it anymore.
Bug 2
The time starts even if the user does not press 'c' , i.e to play game.So by the time user reads the rules (assuming they took 5 seconds to read rules) , and then they press 'c' they only have 5 more seconds to play.I want it to do it so that only when the user presses 'c' the game time starts.
This is my game run up source code:
def runGame():
#Initial score is 0
score=0
gameExit = False
gameOver = False
while not gameExit:
while gameOver == True:
#Game Over message
gameDisplay.fill(black)
message_to_screen("Game over",
red,
y_displace=-50,
size="large")
message_to_screen("Press C to play again or Q to quit.",
white,
y_displace=50,
size="medium")
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
gameExit = True
gameOver = False
if event.key == pygame.K_c: #If user press 'c' , game starts
gameLoop()
gameDisplay.fill((white))
#Possible Error from here
# ~~~~~~~~~~~~~~~
#My time function
time=pygame.time.get_ticks()/1000
if time==11: #Checks if time hit 10 (i took as 11 as it should complete the 10th second)
global time
global score
score=0
time=0
gameOver=True
#~~~~~~~~~~~~~ Till here
for event in pygame.event.get():
if event.type==pygame.QUIT:
running=False
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT:
# Set the x, y postions of the mouse click
x, y = event.pos
if foodrect.collidepoint(x, y):
foodrect.center=(random.randint(5,1060),random.randint(5,700))
print "New Position: ",foodrect.center
score+=1
continue
gameDisplay.blit(foodimg,foodrect)
showtime=font.render("Time: "+str(time),0,(0,0,0))
gameDisplay.blit(showtime,(950,10))
scoredisplay(score)
pygame.display.update()
My Game Loop
def gameLoop():
clock.tick(FPS)
runGame()
pygame.quit()
quit()
gameLoop()
Full Code
As requested by Herman Yanush.
import pygame
import time
import random
pygame.init()
foodimg=pygame.image.load("food.png")
foodrect=foodimg.get_rect()
#Colours
white = (255,255,255)
black = (0,0,0)
red = (255,0,0)
LEFT =1
#Game Display
display_width = 1080
display_height = 720
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('Click It! ~ Snapnel Productions')
gameDisplay.fill((white))
font=pygame.font.SysFont("Arial",30)
#Clock
clock = pygame.time.Clock()
FPS = 30
cellSize=10
#Font Size
exsmallfont = pygame.font.SysFont("comicsansms", 17)
smallfont = pygame.font.SysFont("comicsansms", 25)
medfont = pygame.font.SysFont("comicsansms", 50)
largefont = pygame.font.SysFont("comicsansms", 80)
#The score function - displays the score on top right
def scoredisplay(scoredef=0):
text=smallfont.render("Score :%s" %(scoredef) ,True ,white)
gameDisplay.blit(text,[0,0])
def scoredisplay1(musicname):
text=exsmallfont.render("Now Playing :%s" %(musicname) ,True ,white)
gameDisplay.blit(text,[690,5])
#Starting Of the game
def game_intro():
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key ==pygame.K_c:
intro = False
if event.key ==pygame.K_q:
pygame.quit()
quit()
gameDisplay.fill(black)
#Game Initial display message
message_to_screen("Click It!",
red,
-200,
size="large")
message_to_screen("This Game is created by Shrapnel",
white,
-30,
size="small")
message_to_screen(" the food to gain a point!",
white,
10,
size="small")
message_to_screen("Press 'C' to play the game or 'Q' to quit.",
white,
150,
size="small")
pygame.display.update()
clock.tick(15)
#Text Size
def text_objects(text,color, size):
if size=="small":
textSurface=smallfont.render(text, True ,color)
elif size=="medium":
textSurface=medfont.render(text, True ,color)
elif size=="large":
textSurface=largefont.render(text, True ,color)
return textSurface,textSurface.get_rect()
#Message to screen
def message_to_screen(msg,color,y_displace=0,size="small"):
textSurf,textRect=text_objects(msg,color,size)
textRect.center = (display_width / 2),(display_height / 2)+y_displace
gameDisplay.blit(textSurf,textRect)
#Score Display
def scoredisplay(scoredef=0):
text=smallfont.render("Score :%s" %(scoredef) ,True ,black)
gameDisplay.blit(text,[0,0])
score=0
global a
a=10
#print score
def runGame():
score=0
gameExit = False
gameOver = False
thing_startx = random.randrange(0,display_width)
thing_starty = -600
thing_speed = 7
thing_width = 100
thing_height = 100
while not gameExit:
deadZones=[]
while gameOver == True:
#Game Over message
gameDisplay.fill(black)
message_to_screen("Game over",
red,
y_displace=-50,
size="large")
message_to_screen("Press C to play again or Q to quit.",
white,
y_displace=50,
size="medium")
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
gameExit = True
gameOver = False
if event.key == pygame.K_c:
gameLoop()
gameDisplay.fill((white))
time=pygame.time.get_ticks()/1000
if time==11:
global time
global score
score=0
time=0
gameOver=True
#print score
for event in pygame.event.get():
if event.type==pygame.QUIT:
running=False
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT:
# Set the x, y postions of the mouse click
x, y = event.pos
if foodrect.collidepoint(x, y):
foodrect.center=(random.randint(5,1060),random.randint(5,700))
print "New Position: ",foodrect.center
score+=1
continue
gameDisplay.blit(foodimg,foodrect)
showtime=font.render("Time: "+str(time),0,(0,0,0))
gameDisplay.blit(showtime,(950,10))
#scoredisplay(score)
pygame.display.update()
def gameLoop():
clock.tick(FPS)
runGame()
pygame.quit()
quit()
game_intro()
gameLoop()
Thanks in advance to help.
I think I might have a solution that at least I use. While sometimes it is nice to use pygame.time.get_ticks, there is an easier solution. Before your while loop set a variable called frame to 0. Then at the end of your while loop type:
if gameEnd == True:
frame=0
score=0
else:
frame+=1
Then all you have to do is only draw the picture when frame>0. You can also easily manipulate frame. Message me if you need any more help.