Menu
CoddyTech

Alle 800 ms ein Felsbrocken

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

Ein einzelner Felsbrocken ist ein ruhiges Spiel. Mit einem Timer kann alle 800 ms ein neuer Felsbrocken herunterfallen, während die anderen noch fallen. Bewahre die Felsbrocken in einer Liste auf:

SPAWN = pygame.USEREVENT + 2
...
if event.type == SPAWN and state == "play":
    rocks.append(pygame.Rect(random.randint(0, 456), -24, 24, 24))

Jedes Ereignis hat eine eigene Nummer, also ist SPAWN USEREVENT + 2, eine Nummer höher als POINT. Starte den Timer neben dem anderen und setze rocks = [] in reset().

Bewege in jedem Frame alle Felsbrocken und behalte dann nur diejenigen, die sich noch im Fenster befinden:

for rock in rocks:
    rock.y += 4
rocks = [rock for rock in rocks if rock.top <= 360]

Ohne diese zweite Zeile bleibt jeder Felsbrocken, der jemals heruntergefallen ist, in der Liste. Das Spiel würde Tausende von Felsbrocken bewegen und überprüfen, die niemand sehen kann, und mit zunehmender Spieldauer langsamer werden.

Auch das Zeichnen und die Kollisionserkennung werden zu Schleifen über rocks.

challenge icon

Aufgabe

Mittel
  1. Ersetze rock durch eine Liste namens rocks, die in reset() leer ist.
  2. Füge SPAWN = pygame.USEREVENT + 2 und einen Timer hinzu, der alle 800 ms ausgelöst wird und in reset() gestartet wird.
  3. Füge während des Spiels bei SPAWN einen 24 × 24 großen Felsen bei y -24 und einem zufälligen x-Wert von 0 bis 456 hinzu.
  4. Bewege jeden Felsen in jedem Frame um 4 nach unten, entferne diejenigen, deren Oberkante über 360 hinaus ist, zeichne alle und prüfe jeden auf eine Kollision.

Probier es selbst

import random
import pygame

pygame.init()
screen = pygame.display.set_mode((480, 360))
pygame.display.set_caption("Meteor Dodge")
clock = pygame.time.Clock()
heart = pygame.image.load("assets/images/heart.png").convert_alpha()
heart_empty = pygame.image.load("assets/images/heart_empty.png").convert_alpha()
font = pygame.font.Font("assets/fonts/pixel.ttf", 24)
big_font = pygame.font.Font("assets/fonts/pixel.ttf", 48)
WHITE = (240, 240, 240)
PLATE = (60, 60, 90)
POINT = pygame.USEREVENT + 1


def reset():
    global player, rock, score, lives, hit_time
    player = pygame.Rect(220, 300, 40, 40)
    rock = pygame.Rect(random.randint(0, 456), -24, 24, 24)
    score = 0
    lives = 3
    hit_time = -2000
    pygame.time.set_timer(POINT, 1000)


def draw_menu():
    screen.fill((30, 30, 46))
    title = big_font.render("Meteor Dodge", True, WHITE)
    title_rect = title.get_rect(center=(240, 140))
    pygame.draw.rect(screen, PLATE, title_rect.inflate(24, 12))
    screen.blit(title, title_rect)
    hint = font.render("Press Enter to play", True, WHITE)
    screen.blit(hint, hint.get_rect(center=(240, 200)))


def draw_game():
    screen.fill((30, 30, 46))
    safe = pygame.time.get_ticks() - hit_time < 1000
    if not safe or pygame.time.get_ticks() // 100 % 2 == 0:
        pygame.draw.rect(screen, (255, 196, 0), player)
    pygame.draw.rect(screen, (235, 80, 80), rock)
    score_text = font.render(f"{score:05}", True, WHITE)
    score_rect = score_text.get_rect(topright=(470, 10))
    pygame.draw.rect(screen, PLATE, score_rect.inflate(12, 8))
    screen.blit(score_text, score_rect)
    for i in range(3):
        if i < lives:
            screen.blit(heart, (10 + i * 40, 10))
        else:
            screen.blit(heart_empty, (10 + i * 40, 10))


def play_frame():
    global score, lives, state, hit_time
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        player.x -= 6
    if keys[pygame.K_RIGHT]:
        player.x += 6
    player.clamp_ip(screen.get_rect())

    rock.y += 4
    if rock.top > 360:
        rock.y = -24
        rock.x = random.randint(0, 456)
    now = pygame.time.get_ticks()
    if rock.colliderect(player) and now - hit_time > 1000:
        hit_time = now
        lives -= 1
        if lives == 0:
            state = "over"
    draw_game()


def draw_over():
    draw_game()
    message = big_font.render("Game over", True, WHITE)
    screen.blit(message, message.get_rect(center=(240, 160)))
    hint = font.render("Press R to play again", True, WHITE)
    screen.blit(hint, hint.get_rect(center=(240, 200)))


reset()
state = "menu"

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == POINT and state == "play":
            score += 1
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RETURN and state == "menu":
                state = "play"
            elif event.key == pygame.K_r and state == "over":
                reset()
                state = "play"

    if state == "menu":
        draw_menu()
    elif state == "play":
        play_frame()
    else:
        draw_over()
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Alle Lektionen in Spielregeln

Übe selbstständig: Pygame-Playground