I have some pygame code here I made:
#############################################################################
# IMPORTS
#############################################################################
import pygame, sys
from pygame.locals import *
#############################################################################
# PRE-INITIALIZATION
#############################################################################
pygame.init()
#############################################################################
# CONSTANTS
#############################################################################
SW = 300
SH = 300
#############################################################################
WHITE = (255,255,255)
LIGHTEST_GRAY = (230,230)
LIGHT_GRAY = (205,205,205)
SORTLIGHT_GRAY = (180,180,180)
GRAY = (155,155,155)
SORTDARK_GRAY = (130,130,130)
DARK_GRAY = (105,105,105)
DARKEST_GRAY = (80,80,80)
BLACK_GRAY = (55,55,55)
LIGHT_BLACK = (30,30,30)
SORTLIGHT_BLACK = (5,5,5)
BLACK = (0,0,0)
RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)
YELLOW = (255,255,0)
#############################################################################
SYS_FONT = pygame.font.SysFont(None, 30)
#############################################################################
# GLOBAL VARIABLES
#############################################################################
state = ""
#############################################################################
# CLASSES
##############################################################################
#############################################################################
# FUNCTIONS
#############################################################################
def addTuples(a,b):
for i in range(len(a)):
a[i] += b[i]
def set_state(newstate="",init_function=None):
global state
state=newstate
if init_function!=None:init_function()
return state
def get_state():
return state
#############################################################################
def initSplashScreen():
screen.fill(BLACK)
def initGameScreen():
pass
#############################################################################
def quitEvent():
pygame.quit()
sys.exit()
def updateEvent():
checkEvents()
if get_state() == "splash":
drawSplashScreen()
elif get_state() == "game":
drawGameScreen()
#############################################################################
def checkEvents():
for event in pygame.event.get():
if event.type == QUIT:
quitEvent()
def checkSplashScreenEvents():
print("naff")
for event in pygame.event.get():
if event.type == KEYUP:
print("saff")
set_state("game",initGameScreen)
def checkGameScreenEvents():
for event in pygame.event.get():
if event.type == KEYUP:
if event.key == K_ESCAPE:
set_state("pause")
def checkPauseScreenEvents():
for event in pygame.event.get():
if event.type == KEYDOWN:
set_state("game")
#############################################################################
def drawText(text,color,loc):
text_obj = SYS_FONT.render(text, True, color)
screen.blit(text_obj,loc)
#############################################################################
def drawSplashScreen():
checkSplashScreenEvents()
drawText("Grid Game",RED,(95,50))
drawText("Press SPACE to begin!",YELLOW,(35,100))
def drawGameScreen():
checkGameScreenEvents()
screen.fill(BLACK)
drawText("Game",BLUE,(95,50))
def drawPauseScreen():
checkPauseScreenEvents()
drawText("Paused",GREEN,(115,50))
drawText("Press ANY KEY to continue!",YELLOW,(15,100))
#############################################################################
# INITIALIZATION
#############################################################################
screen = pygame.display.set_mode((SW,SH))
pygame.display.set_caption("Grid")
set_state("splash",initSplashScreen)
#############################################################################
# PROGRAM STARTS HERE
#############################################################################
while True:
updateEvent()
pygame.display.update()
When I run the program I can press 'space' key and it says 'game' in blue on the screen with a black background.
However, when I remove the print statement in checkSplashScreenEvents function: print("naff") the program no longer works correctly. When I press 'space' about twenty times it works after 10 seconds or so...
The print statements were only used for testing to make sure the function was called earlier on when I was creating this program.
I thought the python Idle IDE might have been glitching out so I got out of Idle and when back in. This did not fix the problem.
Does anyone know what is going on here, why this is happening?
And how to fix it?
Thank you, in advance.
You are not making ANY pause between frames or event checking- the CPU and I/O are overloaded at maximum. The print statement would provide a brief relief to the system.
Try just adding a pygame.time.delay(30) or so immediately after calling display.update().
Now, with a little more calm, the real problem is that you are making calls to pygame.event.get in more than one location in your code, and doing that in your loop. That call is destructive in a way that it does consume any pending events. The print would introduce a small pause between calls to .get so that a KEYUP event eventually has a chance to sneak-in between the calls to event.get in your checkEvents method and the one in checkSplashScreenEvents method.
You'd better reorganize your code so that you call event.get in ONE single place- otherwise your code will be unmaintainable (it is already hard to follow, and there is almost nothing in there) - for example, try to set an event callback for each of the game states - the callback gets a list of the ongoing events - from a single getEvents method.
For the code as it is to run, just replace your checkEvents for one with a non-destructive way to check for a quit event - for example:
def checkEvents():
if pygame.event.peek(QUIT):
quitEvent()
Related
In my pygame script, I have a function to work with some images that I have to use in a while loop. The problem is that I only want to run the function once for each image. So, I want to run self.convert_img(self.img_one) once, then proceed to self.convert_img(self.img_two) and so on, untill all images are processed. Afterwards, I would like the function to stop, such that I can change the scene.
Here is a mock of my code right now:
import pygame
class Main:
def __init__(self):
pygame.init()
self.window = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
self.clock = pygame.time.Clock()
self.count = 5
# Load Images
self.img_one = pygame.image.load(os.path.join("images", "img_one.png"))
self.img_two = pygame.image.load(os.path.join("images", "img_two.png"))
def run(self):
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
img_one_transorm = self.convert_img(self.img_one)
img_two_transorm = self.convert_img(self.img_two)
## img three transform
self.clock.tick(30)
def convert_img(self, arg1):
self.window.blit(arg1, convert_img_rect)
pygame.display.update()
if self.count > 0:
## convert image function
create a count that increases by one then a bunch of if statements
like:
if count == 1:
img_one_transorm=self.convert_img(self.img_one)
count+=1
elif count == 2:
img_two_transorm=self.convert_img(self.img_two)
count+=1
did i understand this correctly?
import pygame
import time
# WINDOW SETUP
window = pygame.display.set_mode((900, 500))
pygame.display.set_caption("Pong")
time.sleep(5)
FPS = 60
# RGB VALUE VARIABLES
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
def background(window):
window.fill(WHITE)
pygame.display.update()
# FRAMERATE AND EVENT LOOP INITIALIZATION
def main():
run = True
clock = pygame.time.Clock()
while run:
clock.tick(FPS)
background(window)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if running == False:
pygame.quit()
Above, is my code. I'm trying to make a pong game with pygame. The text editor I am coding with is Visual Studios Code (VSCODE).
Firstly, you need to call the main. You should also make you're code nice and easy to read when possible. I imported pygame as pg which makes typing pygame functions a bit faster as you have less to type. Also, it's better to use global variables to hold variables that won't change through the program, like screen width, height, colours, etc. Then make sure you initialise the module.
As well as that, the only update you have is in background(). You should put the update at the bottom of the loop and remove it from background(). This way everything above will update each loop.
I apologise for not adding you're FPS counter in here as well but I think this should be enough to help you get you're window running with more readable code and a more efficient loop.
import pygame as pg
# Global Variables
screen_width = 900
screen_height = 500
screen = pg.display
window = screen.set_mode((screen_width, screen_height))
colour = 'red'
def main():
# Initialise module
pg.init()
pg.display.set_caption('PONG')
running = True
while running:
# This is a better way of writing your loop
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
# Call background function
background()
# Updates window
# place this inside the loop near the bottom
# so everything is updated at the end of each loop
screen.flip()
def background():
window.fill(colour)
# Remember to call your main function
# This if statement is good practise but not required
# You can just place main() here
if __name__ == '__main__':
main()
So I'm trying to implement a class for a basic game. It woked without the class but now instead of spawning the "coin" it pops up and then immediatly dissapears. No idea as it's in the main loop. I have a moving "player" that works fine.
Here's my code:
class Coin_Class:
def __init__(self):
coin_1 = pygame.Rect(425, 30, 40, 40)
pygame.draw.rect(WIN, YELLOW, coin_1)
pygame.display.update()
# def coin_collect():
# if player.colliderect():
# coin_1.x = random.randint(0, 800)
# coin_1.y = random.randint(0, 250)
# pygame.event.post(pygame.event.Event(coin_collected))
# global score
# score += 1
# print(score)
coin_class = Coin_Class()
# main function loop
def main():
score_check = 0
clock = pygame.time.Clock()
run = True
while run:
# game speed
clock.tick(FPS)
# initialise pygame
pygame.init()
# checking all the events in pygame and looping them
for event in pygame.event.get():
# checking quit function is pressed
if event.type == pygame.QUIT:
run = False
pygame.quit()
exit()
keys_pressed = pygame.key.get_pressed() # recognise key presses
player_movement(keys_pressed, player) # movement function
draw_window() # create window function
coin_class
main()
# runs the main file
if __name__ == "__main__":
main()
Add a draw method to the coin class (also class names per PEP 8 should be in CapitalCase not Capital_Snake_Case so CoinClass):
class Coin_Class:
def __init__(self):
self.coin_1 = pygame.Rect(425, 30, 40, 40)
...
def draw(self):
pygame.draw.rect(WIN, YELLOW, self.coin_1)
And in the loop instead of using
coin_class
you would now use
coin_class.draw()
The rest can stay the same, except remove pygame.init() from the loop and put it somewhere at the start of the code after imports
How can one inject events into a running pygame from a pytest test module?
The following is a minimal example of a pygame which draws a white rectangle when J is pressed and quits the game when Ctrl-Q is pressed.
#!/usr/bin/env python
"""minimal_pygame.py"""
import pygame
def minimal_pygame(testing: bool=False):
pygame.init()
game_window_sf = pygame.display.set_mode(
size=(400, 300),
)
pygame.display.flip()
game_running = True
while game_running:
# Main game loop:
# the following hook to inject events from pytest does not work:
# if testing:
# test_input = (yield)
# pygame.event.post(test_input)
for event in pygame.event.get():
# React to closing the pygame window:
if event.type == pygame.QUIT:
game_running = False
break
# React to keypresses:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
# distinguish between Q and Ctrl-Q
mods = pygame.key.get_mods()
# End main loop if Ctrl-Q was pressed
if mods & pygame.KMOD_CTRL:
game_running = False
break
# Draw a white square when key J is pressed:
if event.key == pygame.K_j:
filled_rect = game_window_sf.fill(pygame.Color("white"), pygame.Rect(50, 50, 50, 50))
pygame.display.update([filled_rect])
pygame.quit()
if __name__ == "__main__":
minimal_pygame()
I want to write a pytest module which would automatically test it. I have read that one can inject events into running pygame. Here I read that yield from allows a bidirectional communication, so I thought I must implement some sort of a hook for pygame.events being injected from the pytest module, but it is not as simple as I thought, so I commented it out. If I uncomment the test hook under while game_running, pygame does not even wait for any input.
Here is the test module for pytest:
#!/usr/bin/env python
"""test_minimal_pygame.py"""
import pygame
import minimal_pygame
def pygame_wrapper(coro):
yield from coro
def test_minimal_pygame():
wrap = pygame_wrapper(minimal_pygame.minimal_pygame(testing=True))
wrap.send(None) # prime the coroutine
test_press_j = pygame.event.Event(pygame.KEYDOWN, {"key": pygame.K_j})
for e in [test_press_j]:
wrap.send(e)
Pygame can react to custom user events, not keypress or mouse events. Here is a working code where pytest sends a userevent to pygame, pygame reacts to it and sends a response back to pytest for evaluation:
#!/usr/bin/env python
"""minimal_pygame.py"""
import pygame
TESTEVENT = pygame.event.custom_type()
def minimal_pygame(testing: bool=False):
pygame.init()
game_window_sf = pygame.display.set_mode(
size=(400, 300),
)
pygame.display.flip()
game_running = True
while game_running:
# Hook for testing
if testing:
attr_dict = (yield)
test_event = pygame.event.Event(TESTEVENT, attr_dict)
pygame.event.post(test_event)
# Main game loop:
pygame.time.wait(1000)
for event in pygame.event.get():
# React to closing the pygame window:
if event.type == pygame.QUIT:
game_running = False
break
# React to keypresses:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
# distinguish between Q and Ctrl-Q
mods = pygame.key.get_mods()
# End main loop if Ctrl-Q was pressed
if mods & pygame.KMOD_CTRL:
game_running = False
break
# React to TESTEVENTS:
if event.type == TESTEVENT:
if event.instruction == "draw_rectangle":
filled_rect = game_window_sf.fill(pygame.Color("white"), pygame.Rect(50, 50, 50, 50))
pygame.display.update([filled_rect])
pygame.time.wait(1000)
if testing:
# Yield the color value of the pixel at (50, 50) back to pytest
yield game_window_sf.get_at((50, 50))
pygame.quit()
if __name__ == "__main__":
minimal_pygame()
Here's the test code:
#!/usr/bin/env python
"""test_minimal_pygame.py"""
import minimal_pygame
import pygame
def pygame_wrapper(coro):
yield from coro
def test_minimal_pygame():
wrap = pygame_wrapper(minimal_pygame.minimal_pygame(testing=True))
wrap.send(None) # prime the coroutine
# Create a dictionary of attributes for the future TESTEVENT
attr_dict = {"instruction": "draw_rectangle"}
response = wrap.send(attr_dict)
assert response == pygame.Color("white")
It works, However, pytest, being a tool for stateless unit tests rather than integration tests, makes the pygame quit after it gets the first response (teardown test). It is not possible to continue and do more tests and assertions in the current pygame session. (Just try to duplicate the last two lines of the test code to resend the event, it will fail.) Pytest is not the right tool to inject a series of instructions into pygame to bring it to a precondition and then perform a series of tests.
That's at least what I heard from people on the pygame discord channel. For automated integration tests they suggest a BDD tool like Cucumber (or behave for python).
I need to kb input onto a pygame screen
at the moment it appears on the idle shell
any advice would be appreciated.
This code is extracted from a larger program
mostly screen based but i need to input some
data (numeric) from the kb at times
import sys
import pygame
from pygame.locals import *
pygame.init()
N= ''
screen = pygame.display.set_mode((600,600))
font= pygame.font.Font(None,40)
screen.fill((255,255,255))
pygame.display.flip
pygame.display.update()
def score(C,y):
SetWnd = font.render( C,True,(0,0,255))
screen.blit(SetWnd, (15, 100+y))
pygame.display.update()
def start():
while True:
name=''
for evt in pygame.event.get():
if evt.type == KEYDOWN:
if evt.unicode.isalnum(): # unicode
name+=evt.unicode
print name,
elif evt.key == K_BACKSPACE:
name = name[:-1]
print name,
elif evt.key == K_RETURN:
return N
elif evt.type == QUIT:
pygame.quit()
sys.exit()
def Pchange(c,y):
block = font.render(N, True, (0,0,0))
rect = block.get_rect()
rect.move_ip(75,100 + y)
screen.blit(block,rect)
pygame.display.flip()
score('wind', 0)
score('elev',20)
N = start()
Pchange(N,0)
Pchange(N,20)
Firstly you draw the score twice, which i assume works well.
The problem lies in you start function.
You are not calling any draw or update function in your while loop.
In your event foreach, you add a digit to name, and exit the while loop when enter is pressed. Then you draw twice with Pchange, but you are the function does not use the right parameters. You have:
def Pchange(c,y):
block = font.render(N, True, (0,0,0))
you are using the global N which is ''. So to fix that, you need to change N to c.
The next problem is that the game quits right after pressing enter. Since you pasted only a part of the program, this might not be the case. If it is, make another while loop, and just wait for the ESC key to call pygame.quit() and sys.exit()