Menu
CoddyTech

Điểm cao nhất và khởi động lại an toàn

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

Màn hình kết thúc trò chơi hiển thị điểm cao nhất và bắt đầu trò chơi mới khi nhấn phím Space. best được đặt một lần trước vòng lặp, bên ngoài reset(), nên trò chơi mới vẫn giữ giá trị này.

Trên màn hình kết thúc trò chơi, phím Space giờ đây đảm nhiệm thêm một việc thứ ba:

elif pygame.time.get_ticks() - over_time > 500:
    reset()
    state = "play"

Space là phím nhảy, nên người chơi thường nhấn phím này ngay lúc va chạm. Nếu không chờ, trò chơi sẽ khởi động lại trước khi họ kịp thấy kết quả. Ghi nhớ thời điểm va chạm trong over_time và chỉ chấp nhận phím Space trên màn hình kết thúc trò chơi sau 500 ms.

challenge icon

Thử thách

Khó

Hiển thị điểm cao nhất và khởi động lại an toàn.

  1. Sau state = "menu", đặt best = 0 và over_time = 0. Khi va chạm, đặt best = max(best, score) và cập nhật over_time thành thời điểm hiện tại.
  2. Màn hình kết thúc trò chơi hiển thị Game over, Best: 00120 và Press Space to play again.
  3. Ở đó, phím cách gọi reset() để chơi lại, nhưng chỉ khi đã qua 500 ms kể từ lúc va chạm.

Nhấn Chơi rồi để nhân vật va chạm: điểm cao nhất sẽ hiện ra, và phím cách sẽ bắt đầu lại. Sau đó nhấn Kiểm tra.

Tự làm thử

import random
import pygame

pygame.init()
screen = pygame.display.set_mode((640, 320))
pygame.display.set_caption("Sky Runner")
clock = pygame.time.Clock()
font = pygame.font.Font("assets/fonts/pixel.ttf", 32)
big_font = pygame.font.Font("assets/fonts/pixel.ttf", 48)
INK = (40, 50, 80)

SKY = (140, 200, 240)
GROUND = (90, 160, 80)
GROUND_Y = 260
GRAVITY = 1
SPAWN = pygame.USEREVENT + 1
POINT = pygame.USEREVENT + 2


class Runner(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.frames = [
            pygame.image.load("assets/images/player.png").convert_alpha(),
            pygame.image.load("assets/images/player_walk.png").convert_alpha(),
        ]
        self.image = self.frames[0]
        self.step = 0
        self.rect = self.image.get_rect(midbottom=(100, GROUND_Y))
        self.vy = 0

    def jump(self):
        if self.rect.bottom == GROUND_Y:
            self.vy = -16

    def update(self):
        self.vy += GRAVITY
        self.rect.y += self.vy
        if self.rect.bottom >= GROUND_Y:
            self.rect.bottom = GROUND_Y
            self.vy = 0
        if self.rect.bottom == GROUND_Y:
            self.step += 1
            self.image = self.frames[self.step // 8 % 2]
        else:
            self.image = self.frames[1]


class Obstacle(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = random.choice(obstacle_images)
        self.rect = self.image.get_rect(midbottom=(664, GROUND_Y))

    def update(self):
        self.rect.x -= speed
        if self.rect.right < 0:
            self.kill()


obstacle_images = [
    pygame.image.load("assets/images/spike_ball.png").convert_alpha(),
    pygame.image.load("assets/images/blob.png").convert_alpha(),
]


def reset():
    global runner, obstacles, speed, score
    runner = Runner()
    obstacles = pygame.sprite.Group()
    speed = 6
    score = 0
    pygame.time.set_timer(SPAWN, 1500)
    pygame.time.set_timer(POINT, 100)


def show(text, text_font, y):
    picture = text_font.render(text, True, INK)
    screen.blit(picture, picture.get_rect(center=(320, y)))


reset()
state = "menu"

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN and event.key in (pygame.K_SPACE, pygame.K_UP):
            if state == "play":
                runner.jump()
            elif state == "menu":
                reset()
                state = "play"
        if event.type == SPAWN and state == "play":
            obstacles.add(Obstacle())
            pygame.time.set_timer(SPAWN, random.randint(1000, 2000))
        if event.type == POINT and state == "play":
            score += 1

    if state == "play":
        speed = min(14, 6 + score // 100)
        runner.update()
        obstacles.update()
        if pygame.sprite.spritecollideany(runner, obstacles, pygame.sprite.collide_mask):
            state = "over"

    screen.fill(SKY)
    pygame.draw.rect(screen, GROUND, (0, GROUND_Y, 640, 60))
    screen.blit(runner.image, runner.rect)
    obstacles.draw(screen)
    if state == "menu":
        show("Sky Runner", big_font, 110)
        show("Press Space to start", font, 170)
    else:
        score_text = font.render(f"{score:05}", True, INK)
        screen.blit(score_text, score_text.get_rect(topright=(630, 10)))
    if state == "over":
        message = font.render("Game over", True, (40, 50, 80))
        screen.blit(message, message.get_rect(center=(320, 140)))
    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