Menu
CoddyTech

Ein Menü für Himmelsläufer

Teil des Abschnitts Spielregeln der Pygame-Journey von Coddy. Lektion 29 von 30.

Sky Runner bezieht die Regeln aus diesem Abschnitt, beginnend mit einem Menü. Ein neues Spiel braucht alles wieder in seinem Ausgangszustand, also kommt das in reset(): ein neuer Runner, eine leere Gruppe, die Geschwindigkeit und der Punktestand sowie beide Timer, die erneut gestartet werden.

Die Leertaste erfüllt jetzt zwei Aufgaben, eine pro Zustand:

if state == "play":
    runner.jump()
elif state == "menu":
    reset()
    state = "play"

Das Menü benötigt zwei zentrierte Zeilen und der Game-Over-Bildschirm wird drei benötigen. Eine kleine Funktion zeichnet daher eine Zeile zentriert bei x 320:

def show(text, text_font, y):
    picture = text_font.render(text, True, INK)
    screen.blit(picture, picture.get_rect(center=(320, y)))

Der Punktestand gehört zu einem Spiel, also wird er erst angezeigt, wenn eines gestartet wurde, nicht im Menü.

challenge icon

Aufgabe

Mittel

Gib Sky Runner ein Menü.

  1. Schreibe reset() für den Runner, die Gruppe, speed, score und beide Timer; rufe es vor der Schleife auf und setze state = "menu".
  2. Verwende im Menü show() für Sky Runner in big_font und für Press Space to start; zeige den Punktestand nur außerhalb des Menüs an.
  3. Leertaste im Menü ruft reset() auf und setzt state = "play".

Drücke „Spielen“: Die Leertaste startet einen Lauf. Dann „Prüfen“.

Probier es selbst

import random
import pygame

pygame.init()
screen = pygame.display.set_mode((640, 320))
pygame.display.set_caption("Sky Runner")
clock = pygame.time.Clock()
font = pygame.font.Font("assets/fonts/pixel.ttf", 32)

SKY = (140, 200, 240)
GROUND = (90, 160, 80)
GROUND_Y = 260
GRAVITY = 1
SPAWN = pygame.USEREVENT + 1
POINT = pygame.USEREVENT + 2


class Runner(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.frames = [
            pygame.image.load("assets/images/player.png").convert_alpha(),
            pygame.image.load("assets/images/player_walk.png").convert_alpha(),
        ]
        self.image = self.frames[0]
        self.step = 0
        self.rect = self.image.get_rect(midbottom=(100, GROUND_Y))
        self.vy = 0

    def jump(self):
        if self.rect.bottom == GROUND_Y:
            self.vy = -16

    def update(self):
        self.vy += GRAVITY
        self.rect.y += self.vy
        if self.rect.bottom >= GROUND_Y:
            self.rect.bottom = GROUND_Y
            self.vy = 0
        if self.rect.bottom == GROUND_Y:
            self.step += 1
            self.image = self.frames[self.step // 8 % 2]
        else:
            self.image = self.frames[1]


class Obstacle(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = random.choice(obstacle_images)
        self.rect = self.image.get_rect(midbottom=(664, GROUND_Y))

    def update(self):
        self.rect.x -= speed
        if self.rect.right < 0:
            self.kill()


obstacle_images = [
    pygame.image.load("assets/images/spike_ball.png").convert_alpha(),
    pygame.image.load("assets/images/blob.png").convert_alpha(),
]
runner = Runner()
obstacles = pygame.sprite.Group()
speed = 6
pygame.time.set_timer(SPAWN, 1500)
pygame.time.set_timer(POINT, 100)
score = 0
state = "play"

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN and event.key in (pygame.K_SPACE, pygame.K_UP):
            if state == "play":
                runner.jump()
        if event.type == SPAWN and state == "play":
            obstacles.add(Obstacle())
            pygame.time.set_timer(SPAWN, random.randint(1000, 2000))
        if event.type == POINT and state == "play":
            score += 1

    if state == "play":
        speed = min(14, 6 + score // 100)
        runner.update()
        obstacles.update()
        if pygame.sprite.spritecollideany(runner, obstacles, pygame.sprite.collide_mask):
            state = "over"

    screen.fill(SKY)
    pygame.draw.rect(screen, GROUND, (0, GROUND_Y, 640, 60))
    screen.blit(runner.image, runner.rect)
    obstacles.draw(screen)
    score_text = font.render(f"{score:05}", True, (40, 50, 80))
    screen.blit(score_text, score_text.get_rect(topright=(630, 10)))
    if state == "over":
        message = font.render("Game over", True, (40, 50, 80))
        screen.blit(message, message.get_rect(center=(320, 140)))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Alle Lektionen in Spielregeln

Übe selbstständig: Pygame-Playground