PyGameExamplesAndAnswers

StackOverflow            reply.it reply.it

“The goal of software architecture is to minimize the human resources required to build and maintain the required system.”
Robert C. Martin, Clean Architecture


Event and application loop

See also Python PyGame Introduction

Post event

Related Stack Overflow questions:

Application loop

Related Stack Overflow questions:

If you want something to be drawn permanently, you need to draw it in the application loop

The typical PyGame application loop has to:

import pygame
pygame.init()

window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()

# main application loop
run = True
while run:
    # limit frames per second
    clock.tick(100)

    # event loop
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    # clear the display
    window.fill('black')

    # draw the scene   
    pygame.draw.circle(window, (255, 0, 0), (250, 250), 100)

    # update the display
    pygame.display.flip()

pygame.quit()
exit()

You are actually drawing on a Surface object. If you draw on the Surface associated to the PyGame display, this is not immediately visible in the display. The changes become visible, when the display is updated with either pygame.display.update() or pygame.display.flip().

See pygame.display.flip():

This will update the contents of the entire display.

📁 Minimal example - Game loop

repl.it/@Rabbid76/PyGame-MinimalApplicationLoop

📁 Minimal example - Game loop and movement

Quit

Related Stack Overflow questions:

Wait and input in application loop

Related Stack Overflow questions:

You cannot use input in the application loop. input waits for an input. While the system is waiting for input, the application loop will halt and the game will not respond. Use the KEYDOWN event instead of input:

run = True
while run:
    event_list = pygame.event.get()
    for event in event_list:
        if event.type == pygame.QUIT:
            run = False

        if event.type == pygame.KEYDOWN:
            if pygame.key == pygame.K_1:
                # [...]
            if pygame.key == pygame.K_2:
                # [...]

Another option is to get the input in a separate thread.

import pygame
import threading

pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()

color = "red"
def get_input():
    global color
    color = input('enter color (e.g. blue): ')

input_thread = threading.Thread(target=get_input)
input_thread.start()

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False          

    window_center = window.get_rect().center
    window.fill('black')
    pygame.draw.circle(window, color, window_center, 100)
    pygame.display.flip()

pygame.quit()
exit()

Delay, wait and sleep

Related Stack Overflow questions:

Game state

Tag: “gamestate”, “state of game”

Related Stack Overflow questions:

Recursiveness and restart

Related Stack Overflow questions:

You’ve overcomplicated the system. What you are actually doing is recursively instantiating a new Gui object and new application loop into an existing Gui object and application loop. If GameState is implemented correctly, it should be sufficient to create a new GameState object and continue the existing application loop instead of recursively creating a new Gui instance:

import board as b

class Gui():
    def __init__(self):
        pygame.init()

        self.gamestate = b.GameState()

    def run(self):

        running = True
        while running:

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False

                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_r:
                        self.gamestate = b.GameState()

            # [...]

if __name__ == '__main__':
    Gui().run()

The instruction self.board = s.start_position doesn’t create a new board object. self.board and s.start_position refer to the same object. If you change one of the objects, the other object seems to change in the same way since there is only one object.
Either you need to make a deep copy of the board object when you start the game or you need to reset the object when the game restarts.

A solution might be to use the Python copy module. deepcopy can create a deep copy of an object:

import copy
self.board = copy.deepcopy(s.start_position)

Note, not all object can be deep copied. For instance a pygame.Surface cannot be deep copied.

Frames per second (Clock)

See Time, timer event and clock

Event loop

Event loop and handle events

You have to handle the events in the application loop. See pygame.event.get() respectively pygame.event.pump():

For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.

Multiple event loops

pygame.event.get() get all the messages and remove them from the queue. See the documentation:

This will get all the messages and remove them from the queue. […]

If pygame.event.get() is called in multiple event loops, only one loop receives the events, but never all loops receive all events. As a result, some events appear to be missed.

Get the events once per frame and use them in multiple loops or pass the list of events to functions and methods where they are handled:

def handle_events(events):
    for event in events:
        # [...]

while run:

    event_list = pygame.event.get()

    # [...]

    # 1st event loop
    for event in event_list:
        # [...]

    # [...]

    # 2nd event loop
    for event in event_list:
        # [...]

    # [...]

    # function which handles events
    handle_events(event_list)

Wait for event

Related Stack Overflow questions:

pygame.event.wait() waits for a single event from the queue. If the queue is empty this function will wait until one is created. That makes your game lag.

Polymorphic event loop

Related Stack Overflow questions:

In pygame the event type is an enumerator constant. You have to map this constant to the corresponding class. Use a dictionary:

def Exit(event):
    # [...]

def Keydown():
    # [...]

eventhandler = {pygame.QUIT: Exit, pygame.KEYDOWN: Keydown}

For a more general approach, the handlers can be managed in a list. So multiple actions can be associated to an event type

def Exit(event):
    # [...]

def Keydown1():
    # [...]

def Keydown2():
    # [...]

eventhandler = {pygame.QUIT: [Exit], pygame.KEYDOWN: [Keydown1, Keydown2]}

The events must be delegated to the appropriate actions assigned in the event handler:

while True:
    for event in pygame.event.get():
        if event.type in eventhandler:
            for target in eventhandler[event.type]:
                target(event)

📁 Minimal example - Polymorphically event handling 1

📁 Minimal example - Polymorphically event handling 2