Menu
CoddyTech

Un rocher toutes les 800 ms

Fait partie de la section Règles du jeu du Journey Pygame de Coddy. Leçon 12 sur 29.

Un jeu où une seule pierre tombe à la fois est tranquille. Avec un minuteur, une nouvelle pierre peut tomber toutes les 800 ms, tandis que les autres continuent de tomber. Garde les pierres dans une liste :

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

Chaque événement a son propre numéro, donc SPAWN vaut USEREVENT + 2, juste après POINT. Démarre son minuteur en même temps que l’autre, et définis rocks = [] dans reset().

À chaque image, déplace toutes les pierres, puis ne conserve que celles qui sont encore dans la fenêtre :

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

Sans cette deuxième ligne, toutes les pierres déjà tombées restent dans la liste. Le jeu déplacerait et testerait des milliers de pierres invisibles, et ralentirait au fur et à mesure que tu joues.

Le dessin et la détection des collisions deviennent eux aussi des boucles sur rocks.

challenge icon

Défi

Moyen
  1. Remplacez rock par une liste nommée rocks, vide dans reset().
  2. Ajoutez SPAWN = pygame.USEREVENT + 2 et un minuteur qui se déclenche toutes les 800 ms, démarré dans reset().
  3. Lors de SPAWN pendant la partie, ajoutez un rocher de 24 par 24 à y -24 et à une valeur x aléatoire de 0 à 456.
  4. À chaque image, déplacez chaque rocher de 4 vers le bas, supprimez ceux dont le bord supérieur dépasse 360, dessinez-les tous et vérifiez si chacun a touché le joueur.

Essayez vous-même

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()

Toutes les leçons de Règles du jeu

Entraînez-vous par vous-même : Playground Pygame