Menu
CoddyTech

Tải 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 21 trên 30.

Bản ghi đã được lưu; giờ hãy đọc lại khi trò chơi bắt đầu. int(file.read()) chuyển văn bản trong tệp thành một số. Lần đầu tiên sẽ chưa có tệp nào, và open dừng chương trình với lỗi FileNotFoundError.

try và except bắt lỗi đó và thực hiện việc khác thay thế:

def load_best():
    try:
        with open("highscore.txt") as file:
            return int(file.read())
    except FileNotFoundError:
        return 0


best = load_best()

Python chạy khối lệnh try. Nếu open gây ra lỗi FileNotFoundError, chương trình chuyển đến khối lệnh except và trả về 0. Mọi lỗi khác vẫn khiến chương trình dừng lại, đúng như bạn muốn: đó là một bug thực sự.

open("highscore.txt") không có chế độ sẽ mở tệp để đọc.

Trên máy tính, hãy đóng trò chơi rồi chạy lại, điểm cao nhất từ lần trước sẽ hiện trên màn hình kết thúc trò chơi đầu tiên. Trong trình duyệt, điều tương tự xảy ra mỗi khi bạn nhấn nút "Chơi", cho đến khi tải lại trang.

challenge icon

Thử thách

Trung bình

Đọc điểm cao nhất đã lưu khi trò chơi bắt đầu.

  1. Viết hàm load_best(): hàm này trả về số trong highscore.txt, hoặc 0 nếu không có tệp đó.
  2. Đặt best = load_best() thay vì best = 0.

Nhấn “Chơi” rồi để thua: màn hình kết thúc trò chơi hiển thị điểm cao nhất, được đọc từ tệp và lưu lại. 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, best
    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"
        best = max(best, score)
        with open("highscore.txt", "w") as file:
            file.write(str(best))
    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)))
    best_text = font.render(f"Best: {best}", True, WHITE)
    screen.blit(best_text, best_text.get_rect(center=(240, 240)))


reset()
state = "menu"
best = 0

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