Menu
CoddyTech

Mức sàn cho khoảng cách

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

Đá rơi nhanh hơn, nhưng chúng vẫn xuất hiện sau mỗi 800 ms. Đá xuất hiện thường xuyên hơn cũng khiến trò chơi khó hơn. Bắt đầu với khoảng cách một giây rồi thu ngắn khoảng cách đó theo thời gian:

interval = 1000 - seconds * 60

Sau 10 giây, khoảng cách là 400 ms; sau 17 giây, khoảng cách nhỏ hơn 0, còn bộ hẹn giờ sẽ dừng nếu khoảng cách bằng hoặc nhỏ hơn 0. Trò chơi cần có một mức sàn: khoảng cách nhỏ nhất mà nó không bao giờ giảm xuống dưới. max trả về số lớn hơn trong các số được đưa vào, nên nó không bao giờ để khoảng cách giảm xuống dưới số đầu tiên:

interval = max(400, 1000 - seconds * 60)

Bộ hẹn giờ giữ nguyên khoảng cách được thiết lập lúc bắt đầu. Để thay đổi khoảng cách, hãy khởi động lại bộ hẹn giờ: mỗi khi một tảng đá rơi xuống, hãy tính khoảng cách mới rồi gọi set_timer với giá trị đó. Tảng đá tiếp theo sẽ xuất hiện sau khoảng thời gian ấy.

if event.type == SPAWN and state == "play":
    rocks.append(...)
    seconds = (pygame.time.get_ticks() - start_time) // 1000
    interval = max(400, 1000 - seconds * 60)
    pygame.time.set_timer(SPAWN, interval)
challenge icon

Thử thách

Trung bình

Theo thời gian, hãy cho các tảng đá xuất hiện gần nhau hơn, đến một giới hạn.

  1. Trong reset(), bắt đầu bộ hẹn giờ SPAWN ở mức 1000 ms.
  2. Mỗi khi một tảng đá rơi xuống, đặt interval = max(400, 1000 - seconds * 60) rồi khởi động lại bộ hẹn giờ SPAWN với giá trị đó.

Nhấn Chơi: các tảng đá xuất hiện ngày càng thường xuyên hơn, rồi giữ nguyên nhịp độ. Sau đó 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, start_time
    player = pygame.Rect(220, 300, 40, 40)
    rocks = []
    score = 0
    lives = 3
    start_time = pygame.time.get_ticks()
    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))
    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)
    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 rocks, lives, state
    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())

    seconds = (pygame.time.get_ticks() - start_time) // 1000
    speed = 3 + seconds // 5
    kept = []
    for rock in rocks:
        rock.y += speed
        if rock.colliderect(player):
            lives -= 1
        elif rock.top <= 360:
            kept.append(rock)
    rocks = kept
    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":
            rocks.append(pygame.Rect(random.randint(0, 456), -24, 24, 24))
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RETURN and state == "menu":
                reset()
                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()

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

Tự luyện tập: Playground Pygame