Pygame Learning The Basics

Pygame from the start

Function style how to open a pygame window. How you can structure your program.

Avoid this style

I going to show how to avoid globals. This style here is the wrong way to learn. It create bad and unreadable code. Global namespace flooded with to many variables names. It become a nightmare to maintain and hard to add more features.

import pygame

# Intialize all pygame modules
pygame.init()
# Set window title
pygame.display.set_caption('Example')
# Create pygame main surface
surface = pygame.display.set_mode((800, 600))
# Idles and controls framerate
clock = pygame.time.Clock()
# Times between frames in milliseconds
delta = 0
# How many frames per second
fps = 30

running = True
# Main loop
while running:
    # Event loop
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Drawing
    surface.fill(pygame.Color('gray40'))
    # Render to screen
    pygame.display.flip()
    # Idle by frame rate and get time between frames.
    delta = clock.tick(fps)

Let wrap it up.

Here a start. Yet it needs more structure.

import pygame

def main():
    pygame.init()
    pygame.display.set_caption('Example')
    surface = pygame.display.set_mode((800, 600))
    clock = pygame.time.Clock()
    delta = 0
    fps = 30

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

        surface.fill(pygame.Color('gray40'))
        pygame.display.flip()
        delta = clock.tick(fps)

main()

We can split the mainloop from main. It looks good but it still not enough. When you start added code. It be in a couple of blobs.

import pygame

def main():
    pygame.init()
    pygame.display.set_caption('Example')
    surface = pygame.display.set_mode((800, 600))
    clock = pygame.time.Clock()
    delta = 0
    fps = 30

    mainloop(surface, clock, fps)

def mainloop(surface, clock, fps):
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        surface.fill(pygame.Color('gray40'))
        pygame.display.flip()
        delta = clock.tick(fps)

main()

Breaking up main loop.

Now we have enough structure. Simple state structure for flipping to next scene. Yet! It still missing something.

import pygame

def main():
    pygame.init()
    pygame.display.set_caption('Example')
    surface = pygame.display.set_mode((800, 600))
    clock = pygame.time.Clock()
    fps = 30

    mainloop(surface, clock, fps)

def mainloop(surface, clock, fps):
    delta = 0
    on_event = eventloop
    running = True
    while running:
        running, on_event, on_draw = on_event(running)

        on_draw = on_draw(surface)
        delta = clock.tick(fps)

def eventloop(running):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                return running, eventloop2, drawloop2

    return running, eventloop, drawloop

def drawloop(surface):
    surface.fill(pygame.Color('gray40'))
    pygame.display.flip()

def eventloop2(running):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                return running, eventloop, drawloop

    return running, eventloop2, drawloop2

def drawloop2(surface):
    surface.fill(pygame.Color('dodgerblue'))
    pygame.display.flip()

main()

Python dict container.

Here we go. Now we have a boiler plate to create are programs. There many ways to make your boiler plate.

import pygame

engine = {}

def main():
    pygame.init()
    pygame.display.set_caption('Example')
    engine['surface'] = pygame.display.set_mode((800, 600))
    engine['clock'] = pygame.time.Clock()
    engine['delta'] = 0
    engine['fps'] = 30

    engine['on_event'] = eventloop
    engine['on_draw'] = drawloop

    mainloop()

def mainloop():
    engine['running'] = True
    while engine['running']:
        engine['on_event']()

        engine['on_draw'](engine['surface'])
        engine['delta'] = engine['clock'].tick(engine['fps'])

def eventloop():
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            engine['running'] = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                engine['on_draw'] = drawloop2
                engine['on_event'] = eventloop2

def drawloop(surface):
    surface.fill(pygame.Color('gray40'))
    pygame.display.flip()

def eventloop2():
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            engine['running'] = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                engine['on_draw'] = drawloop
                engine['on_event'] = eventloop

def drawloop2(surface):
    surface.fill(pygame.Color('dodgerblue'))
    pygame.display.flip()

main()

Python class container.

Another boiler plate style. Using a class as a container. Using it like python dict without the strings.

import pygame

class Engine:
    pass

def main():
    pygame.init()
    pygame.display.set_caption('Example')
    Engine.surface = pygame.display.set_mode((800, 600))
    Engine.clock = pygame.time.Clock()
    Engine.delta = 0
    Engine.fps = 30

    Engine.on_event = eventloop
    Engine.on_draw = drawloop

    mainloop()

def mainloop():
    Engine.running = True
    while Engine.running:
        Engine.on_event()

        Engine.on_draw(Engine.surface)
        Engine.delta = Engine.clock.tick(Engine.fps)

def eventloop():
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            Engine.running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                Engine.on_draw = drawloop2
                Engine.on_event = eventloop2

def drawloop(surface):
    surface.fill(pygame.Color('gray40'))
    pygame.display.flip()

def eventloop2():
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            Engine.running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                Engine.on_draw = drawloop
                Engine.on_event = eventloop

def drawloop2(surface):
    surface.fill(pygame.Color('dodgerblue'))
    pygame.display.flip()

main()