Menu
CoddyTech

Đếm ngược đến lúc bắt đầu

Thuộc phần Luật chơi trong hành trình Pygame của Coddy. Bài học 14 trên 30.

Nhấn Enter sẽ đưa người chơi thẳng vào cảnh đá rơi. Đồng hồ đếm ngược 3, 2, 1 cho họ chút thời gian chuẩn bị. Đây là một trạng thái nữa, "countdown", trạng thái này vẽ trò chơi mà không di chuyển trò chơi, với một con số ở phía trên.

Con số được tính từ thời gian kể từ khi bắt đầu đếm ngược:

left = 3 - (pygame.time.get_ticks() - countdown_start) // 1000

Trong giây đầu tiên, // cho kết quả 0, nên left là 3, sau đó là 2, rồi 1. Khi giá trị này về 0, trò chơi bắt đầu:

def countdown_frame():
    global state
    left = 3 - (pygame.time.get_ticks() - countdown_start) // 1000
    draw_game()
    if left > 0:
        number = big_font.render(str(left), True, WHITE)
        screen.blit(number, number.get_rect(center=(240, 170)))
    else:
        state = "play"

Enter và R đều bắt đầu đếm ngược, nên một hàm nhỏ sẽ đặt trạng thái và ghi nhớ thời điểm bắt đầu:

def start_countdown():
    global state, countdown_start
    state = "countdown"
    countdown_start = pygame.time.get_ticks()
challenge icon

Thử thách

Trung bình

Đếm ngược 3, 2, 1 trước mỗi ván chơi.

  1. Viết start_countdown() và countdown_frame() như minh họa; gọi countdown_frame() khi trạng thái (state) là "countdown".
  2. Phím Enter ở menu và phím R trên màn hình kết thúc trò chơi sẽ gọi start_countdown() thay vì đặt thành "play".
  3. Hiển thị 3, 2 và 1, mỗi số trong một giây, căn giữa tại (240, 170).

Nhấn Chơi rồi nhấn Enter: 3, 2, 1, sau đó các tảng đá rơi xuống. Tiếp theo, nhấn Kiểm tra.

Tự làm thử

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
SPAWN = pygame.USEREVENT + 2


def reset():
    global player, rocks, score, lives, hit_time, wave, to_spawn
    player = pygame.Rect(220, 300, 40, 40)
    rocks = []
    wave = 1
    to_spawn = 3
    score = 0
    lives = 3
    hit_time = -2000
    pygame.time.set_timer(POINT, 1000)
    pygame.time.set_timer(SPAWN, 800)


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)
    for rock in rocks:
        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)
    wave_text = font.render(f"Wave {wave}", True, WHITE)
    screen.blit(wave_text, wave_text.get_rect(midtop=(240, 10)))
    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, rocks, wave, to_spawn
    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())

    for rock in rocks:
        rock.y += 4
    rocks = [rock for rock in rocks if rock.top <= 360]
    if to_spawn == 0 and not rocks:
        wave += 1
        to_spawn = wave + 2
    now = pygame.time.get_ticks()
    for rock in rocks:
        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 == SPAWN and state == "play" and to_spawn > 0:
            rocks.append(pygame.Rect(random.randint(0, 456), -24, 24, 24))
            to_spawn -= 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()
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Luật chơi

Tự luyện tập: Playground Pygame