Menu
CoddyTech

Lưu kỷ lục

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

Đóng trò chơi là best biến mất: các biến chỉ tồn tại khi chương trình đang chạy. Để giữ lại một con số cho lần sau, hãy ghi số đó vào một tệp.

with open("highscore.txt", "w") as file:
    file.write(str(best))

open(..., "w") mở tệp để ghi. Lệnh này tạo tệp nếu chưa có và xóa hết nội dung nếu tệp đã tồn tại, nên tệp luôn chứa một con số. write nhận văn bản, vì vậy str(best) chuyển số thành văn bản trước. Khối lệnh with đóng tệp khi kết thúc, nhờ đó mọi nội dung đã ghi đều được lưu.

Tên tệp không có thư mục, nên tệp được tạo trong thư mục mà chương trình chạy. Trên máy tính, hãy chạy main.py từ thư mục chứa tệp đó. highscore.txt sẽ xuất hiện bên cạnh và vẫn ở đó.

Trong trình duyệt, các tệp của trò chơi nằm trong bộ nhớ của trang: chơi lại thì tệp vẫn còn, nhưng tải lại trang sẽ bắt đầu khi chưa có tệp.

challenge icon

Thử thách

Trung bình

Lưu điểm cao nhất vào một tệp để điểm số vẫn còn sau khi trò chơi kết thúc.

  1. Khi trò chơi kết thúc, sau khi cập nhật best, hãy mở highscore.txt với "w" trong khối with.
  2. Ghi str(best) vào tệp.

Nhấn Chơi rồi để thua: điểm cao nhất sẽ được lưu trong highscore.txt. 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)
    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