Problem when using keyboard commands in pygame - python

While doing some game programming, I ran into a problem with the keyboard commands. In my code, I have a food bar and a money bank variable named money_bar. The food bar in my game would increase when I press a key, say f, in my game, and also the game deduct say $10 from my money_bar when I press f.
The food bar shows the current amount of food I have, which is supposed to decrease every second. However, it appears that none of my keyboard commands in the event() are working. May I know what is the problem in my code?
This is my food_bar and `money_bar initialisation:
def __init__(self):
pygame.init()
self.clock = pygame.time.Clock()
self.living = 1
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(TITLE)
self.time = pygame.time.get_ticks()
pygame.key.set_repeat(500, 100)
self.all_sprites = pygame.sprite.Group()
self.console = Console(self, 0)
self.player = Player(self, 390, 595)
self.work = Work(self, 450, 250)
self.food_station = Food_Station(self, 750, 200)
self.food = Food(self, 25, 20)
self.education = Education(self, 300, 10)
self.school = School(self, 100, 200)
self.family = Family(self, 600, 10)
self.money = Money(self, 800, 15)
initial_food = 100
self.food_bar = initial_food
initial_money = 0
self.money_bar = initial_money
initial_education = "Student"
self.education_level = initial_education
initial_family = 3
self.family_member = 3
This is where i run the main algorithm:
def run(self):
self.playing = True
self.hunger()
while self.playing:
self.dt = self.clock.tick(FPS) / 1000
self.events()
self.draw()
self.update()
and here's how i check for events(including keyboard commands)
def events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.quit()
if event.type == self.HUNGEREVENT:
self.food_bar = self.food_bar - 10
self.all_sprites.update()
pygame.display.flip()
if event.type == pygame.K_f:
self.money_bar = self.money_bar - 10
self.food_bar = self.food_bar + 15
self.all_sprites.update()
pygame.display.flip()
if event.type == pygame.K_ESCAPE:
self.quit()
Thanks in advance

While pygame.K_f is a key enumerator constant (see pygame.key) the content of event.type is event enumerator constant (see pygame.event).
If you want to determine if a certain key is pressed, the you've to verify if the event type is pygame.KEYDOWN (or pygame.KEYUP for button release) and if the .key attribute of the event is equal the key enumerator. e.g.:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.quit()
# [...]
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_f:
# [...]

Related

pygame set_timer() doesn't work inside a class

So apparently pygame.time.set_timer() doesn't work when used inside a class and I just can make it behave as a timer. Inside a class it will be called every frame
import pygame
pygame.init()
screen = pygame.display.set_mode((100, 100))
FPS = 60
clock = pygame.time.Clock()
class Bird():
def __init__(self):
self.count = 1
self.count_timer = pygame.USEREVENT + 1
pygame.time.set_timer(self.count_timer, 1000)
def go(self):
if event.type == self.count_timer:
print(self.count)
self.count += 1
b = Bird()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
b.go()
pygame.display.update()
clock.tick(FPS)
pygame.quit()
If the timer is outside of a class it works as expected and will be called once per second
import pygame
pygame.init()
screen = pygame.display.set_mode((100, 100))
FPS = 60
clock = pygame.time.Clock()
count = 1
count_timer = pygame.USEREVENT + 1
pygame.time.set_timer(count_timer, 1000)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == count_timer:
print(count)
count += 1
pygame.display.update()
clock.tick(FPS)
pygame.quit()
Can anyone explain what needs to be done to make it work inside of a class? Thank you
This has nothing to do with set_timer. However, you have to call the go method in the event loop for each event:
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
b.go() # <--- INSTERT
# b.go() <--- DELETE
I recommend passing the event object as an argument to the method:
class Bird():
# [...]
def go(self, e):
if e.type == self.count_timer:
print(self.count)
self.count += 1
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
b.go(event)
# [...]
Or pass the full list of events to the method:
import pygame
pygame.init()
screen = pygame.display.set_mode((100, 100))
FPS = 60
clock = pygame.time.Clock()
class Bird():
def __init__(self):
self.count = 1
self.count_timer = pygame.USEREVENT + 1
pygame.time.set_timer(self.count_timer, 1000)
def go(self, event_list):
if self.count_timer in [e.type for e in event_list]:
print(self.count)
self.count += 1
b = Bird()
running = True
while running:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
running = False#
b.go(event_list)
pygame.display.update()
clock.tick(FPS)
pygame.quit()

pygame add object with mouse click

Trying to add more boids by using mouse click ingame.
elif event.type == pygame.MOUSEBUTTONUP:
self.boids_group.add(boids.Boids(rect=pygame.Rect(random()*self.width, random()*self.height, 40, 40)))
I get an error msg that there is no object Boids in Boids, however this is the method i am adding boids to the game at start.
self.boids_group = pygame.sprite.Group()
for i in range(cfg.boidNum):
self.boids_group.add(boids.Boids(rect=pygame.Rect(random()*self.width, random()*self.height, 40, 40)))
And the cfg.boidNum is set to 19 at start, and its adding 19 boids, but not adding more when i push the mousebutton.
If anyone can guide me in the right direction....
The error msg i get when i try running is: line 69, there is no object Boids in Boids, line 69 ref to the mouse input line.
#!/usr/bin/env python
from random import random
from pygame.locals import *
import boids, predator, diver, pygame, sys
import config as cfg
bgimage = pygame.image.load("ramfjord.png")
class Ramfjorden:
def __init__(self, width=1024, height=760):
pygame.init()
self.width = width
self.height = height
self.screen = pygame.display.set_mode((self.width, self.height))
pygame.display.set_caption('Ramfjorden')
def loadSprites(self):
self.predator_group = pygame.sprite.Group()
for i in range(cfg.predatorNum):
self.predator_group.add(predator.Predator(rect=pygame.Rect(random()*self.width, random()*self.height, 70, 70)))
self.boids_group = pygame.sprite.Group()
for i in range(cfg.boidNum):
self.boids_group.add(boids.Boids(rect=pygame.Rect(random()*self.width, random()*self.height, 40, 40)))
self.diver_group = pygame.sprite.Group()
self.diver_group.add(diver.Diver(rect=pygame.Rect(300, 300, 150, 231)))
def collision(self, sprite1, sprite2):
if sprite1 == sprite2:
return False
else:
return pygame.sprite.collide_circle(sprite1, sprite2)
def mainLoop(self):
fps = pygame.time.Clock()
self.loadSprites()
while True:
self.screen.blit(bgimage, (0,0))
self.predator_group.draw(self.screen)
self.boids_group.draw(self.screen)
self.diver_group.draw(self.screen)
for predator in self.predator_group.sprites():
predator.update(ramfjord=self)
for boids in self.boids_group.sprites():
boids.update(ramfjord=self)
for predator in self.predator_group.sprites():
predator.swim(ramfjord=self)
for boids in self.boids_group.sprites():
boids.swim(ramfjord=self)
spriteHitList = pygame.sprite.groupcollide(self.predator_group, self.boids_group, False, True, collided=self.collision)
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.event.post(pygame.event.Event(QUIT))
elif event.type == pygame.MOUSEBUTTONUP:
self.boids_group.add(boids.Boids(rect=pygame.Rect(random()*self.width, random()*self.height, 40, 40)))
pygame.display.update()
fps.tick(30)
def main():
ramfjord = Ramfjorden()
ramfjord.mainLoop()
if __name__ == "__main__":
main()

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()

How to make multiple Instances from a class move at the same time in gyame

import pygame, os, sys
from pygame import *
pygame.init()
RED = ( 255, 0, 0)
class WPawn():
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.transform.scale(pygame.image.load('wpawn.png'), (100,100))
self.move_x = 0
self.move_y = 0
def event_handler(self):
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_UP:
self.move_y = -100
if event.key == K_DOWN:
self.move_y = 100
elif event.type == KEYUP:
if event.key in (K_UP, K_DOWN):
self.move_y = 0
def update(self, screen):
self.y = self.y + self.move_y
def draw(self, screen):
screen.blit(self.image, (self.x, self.y))
class Game():
def __init__(self):
screen_width = 900
screen_height = 900
self.screen = pygame.display.set_mode((screen_width,screen_height))
pygame.display.set_caption ("Chess Project")
self.pawn1 = WPawn(50,650)
self.pawn2 = WPawn(150,650)
self.pawn3 = WPawn(250,650)
self.pawn4 = WPawn(350,650)
self.pawn5 = WPawn(450,650)
self.pawn6 = WPawn(550,650)
self.pawn7 = WPawn(650,650)
self.pawn8 = WPawn(750,650)
def play(self):
clock = pygame.time.Clock()
RUNNING = True
while RUNNING:
for event in pygame.event.get():
if event.type == pygame.QUIT:
RUNNING = False
clock.tick(30)
self.pawn1.event_handler()
self.pawn2.event_handler()
self.pawn3.event_handler()
self.pawn4.event_handler()
self.pawn5.event_handler()
self.pawn6.event_handler()
self.pawn7.event_handler()
self.pawn8.event_handler()
self.pawn1.update(self.screen)
self.pawn2.update(self.screen)
self.pawn3.update(self.screen)
self.pawn4.update(self.screen)
self.pawn5.update(self.screen)
self.pawn6.update(self.screen)
self.pawn7.update(self.screen)
self.pawn8.update(self.screen)
self.screen.fill(RED)
self.pawn1.draw(self.screen)
self.pawn2.draw(self.screen)
self.pawn3.draw(self.screen)
self.pawn4.draw(self.screen)
self.pawn5.draw(self.screen)
self.pawn6.draw(self.screen)
self.pawn7.draw(self.screen)
self.pawn8.draw(self.screen)
pygame.display.update()
Game().play()
pygame.quit()
The KEY_UP event (or whatever) is consumed when you process it. So whichever WPawn class gets the event first will move.
Generally an event-driven program has only one event loop.
It may be easier to make this work by handling the event processing outside of the game objects. Have the single event loop handle record the key press, then move each WPawn that you wish to be moved.
Perhaps something like:
event = pygame.event.get()
if event.type == K_DOWN:
foreach pawn in all_pawns:
pawn.moveDown()
elif event.type == K_UP:
foreach pawn in all_pawns:
pawn.moveUp()

Insert score in a button game function

I have this code for a button that is pressable:
def button(msg,xloc,yloc,xlong,ylong,b1,b2,action=None):
hover = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if xloc < hover [0] < xloc+xlong and yloc< hover [1] < yloc+ylong:
pygame.draw.rect(display, b1, (xloc ,yloc ,xlong,ylong))
if click [0]==1 and action != None:
action()
else:
pygame.draw.rect(gameDisplay, inactiveButton, (xloc ,yloc ,xlong,ylong))
label = pygame.font.SysFont("arial",16)
textSurf, textBox = textMsg(msg, label)
textBox.center = ((xloc +(300)),((yloc +(150))
gameDisplay.blit(textSurf,textBox)
and the code for the scoring is:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
score+=1
print (score)
I would like have a score that – upon pressing the correct button in the choices in order to answer in a quiz game – will be displayed and incremented by 1. How can I do that?
Here's the simplest way to implement a button that I know. Create a rect for the button and draw it with pygame.draw.rect (or blit an image). For the collision detection, check if the event.pos of a pygame.MOUSEBUTTONDOWN event collides with the rect and then just increment the score variable.
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
GRAY = pg.Color('gray15')
BLUE = pg.Color('dodgerblue1')
def main():
clock = pg.time.Clock()
font = pg.font.Font(None, 30)
button_rect = pg.Rect(200, 200, 50, 30)
score = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
if button_rect.collidepoint(event.pos):
print('Button pressed.')
score += 1
screen.fill(GRAY)
pg.draw.rect(screen, BLUE, button_rect)
txt = font.render(str(score), True, BLUE)
screen.blit(txt, (260, 206))
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
Addendum: Actually, I would implement a button with the help of classes, sprites and sprite groups. If you don't know how classes and sprites work, I'd recommend to check out Program Arcade Games (chapter 12 and 13).
import pygame as pg
pg.init()
GRAY= pg.Color('gray12')
BLUE = pg.Color('dodgerblue1')
FONT = pg.font.Font(None, 30)
# The Button is a pygame sprite, that means we can add the
# instances to a sprite group and then update and render them
# by calling `sprite_group.update()` and `sprite_group.draw(screen)`.
class Button(pg.sprite.Sprite):
def __init__(self, pos, callback):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((50, 30))
self.image.fill(BLUE)
self.rect = self.image.get_rect(topleft=pos)
self.callback = callback
def handle_event(self, event):
"""Handle events that get passed from the event loop."""
if event.type == pg.MOUSEBUTTONDOWN:
if self.rect.collidepoint(event.pos):
print('Button pressed.')
# Call the function that we passed during the
# instantiation. (In this case just `increase_x`.)
self.callback()
class Game:
def __init__(self):
self.screen = pg.display.set_mode((800, 600))
self.clock = pg.time.Clock()
self.x = 0
self.button = Button((200, 200), callback=self.increase_x)
self.buttons = pg.sprite.Group(self.button)
self.done = False
# A callback function that we pass to the button instance.
# It gets called if a collision in the handle_event method
# is detected.
def increase_x(self):
"""Increase self.x if button is pressed."""
self.x += 1
def run(self):
while not self.done:
self.handle_events()
self.run_logic()
self.draw()
self.clock.tick(30)
def handle_events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
for button in self.buttons:
button.handle_event(event)
def run_logic(self):
self.buttons.update()
def draw(self):
self.screen.fill(GRAY)
self.buttons.draw(self.screen)
txt = FONT.render(str(self.x), True, BLUE)
self.screen.blit(txt, (260, 206))
pg.display.flip()
if __name__ == "__main__":
Game().run()
pg.quit()

Categories

Resources