Menu
CoddyTech

Tốc độ tăng dần

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

Trò chơi không bao giờ thay đổi sẽ trở nên nhàm chán, còn trò chơi bắt đầu quá khó sẽ khiến người chơi mới bỏ cuộc. Hãy bắt đầu nhẹ nhàng rồi tăng độ khó khi người chơi trụ càng lâu.

Thời gian từ lúc trò chơi bắt đầu quyết định tốc độ. reset() ghi nhớ thời điểm đó, và mỗi khung hình tính tốc độ dựa trên thời gian này:

seconds = (pygame.time.get_ticks() - start_time) // 1000
speed = 3 + seconds // 5

seconds // 5 tăng thêm 1 sau mỗi năm giây, nên ban đầu đá rơi 3 pixel mỗi khung hình, sau năm giây là 4, sau mười giây là 5. Các bước tăng nguyên giúp tốc độ luôn là số nguyên, điều mà Rect cần: nó lưu số pixel nguyên, nên rock.y += 3.5 sẽ làm mất phần lẻ.

Enter và R đều gọi reset(), nên đồng hồ bắt đầu lại mỗi khi chơi ván mới, bất kể menu đã hiển thị bao lâu.

challenge icon

Thử thách

Trung bình

Khi bạn trụ càng lâu, đá rơi càng nhanh.

  1. Trong reset(), đặt start_time thành pygame.time.get_ticks().
  2. Trong play_frame(), tính số giây đã trôi qua kể từ start_time và speed = 3 + seconds // 5.
  3. Di chuyển từng tảng đá xuống một khoảng bằng speed thay vì 3.

Nhấn Chơi: sau vài giây, đá sẽ rơi nhanh hơn. 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
    player = pygame.Rect(220, 300, 40, 40)
    rocks = []
    score = 0
    lives = 3
    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())

    kept = []
    for rock in rocks:
        rock.y += 3
        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