Menu
CoddyTech

An toàn sau khi trúng đòn

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

Trong phiên bản này, hãy đứng bên dưới một tảng đá. Tảng đá không còn nhảy ngược lên trên cùng khi va chạm, nó rơi xuyên qua người chơi và chạm vào người chơi liên tiếp trong nhiều khung hình. Mỗi khung hình như vậy làm mất một mạng, nên chỉ một tảng đá cũng kết thúc trò chơi.

Nhiều trò chơi cho bạn một khoảng thời gian ngắn an toàn sau khi bị trúng đòn. Để biết đã trôi qua bao lâu, hãy xem đồng hồ. pygame.time.get_ticks() là số mili giây tính từ lúc gọi pygame.init(). Hãy ghi nhớ thời điểm bị trúng đòn rồi lấy thời gian hiện tại trừ đi:

now = pygame.time.get_ticks()
if rock.colliderect(player) and now - hit_time > 1000:
    hit_time = now
    lives -= 1

Đặt hit_time thành -2000 trong reset() để lần trúng đòn đầu tiên luôn được tính.

Hiển thị khoảng thời gian an toàn bằng cách nhấp nháy. get_ticks() // 100 % 2 có giá trị 0 trong một phần mười giây, sau đó là 1 rồi lại là 0, vì vậy chỉ vẽ người chơi khi giá trị này là 0 sẽ khiến người chơi nhấp nháy:

safe = pygame.time.get_ticks() - hit_time < 1000
if not safe or pygame.time.get_ticks() // 100 % 2 == 0:
    pygame.draw.rect(screen, (255, 196, 0), player)
challenge icon

Thử thách

Trung bình

Để mỗi lần va chạm chỉ mất một mạng, không phải ba.

  1. Trong reset(), đặt hit_time = -2000 (thêm biến này vào dòng global).
  2. Mỗi lần va chạm chỉ làm mất một mạng nếu đã hơn 1000 ms kể từ hit_time; sau đó đặt hit_time thành thời điểm hiện tại.
  3. Trong 1000 ms sau khi va chạm, chỉ vẽ người chơi khi pygame.time.get_ticks() // 100 % 2 == 0.

Nhấn Chơi rồi để bị va chạm: một trái tim biến mất, người chơi nhấp nháy. 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)


def reset():
    global player, rock, score, lives
    player = pygame.Rect(220, 300, 40, 40)
    rock = pygame.Rect(random.randint(0, 456), -24, 24, 24)
    score = 0
    lives = 3


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)
    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 score, 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())

    rock.y += 4
    if rock.top > 360:
        rock.y = -24
        rock.x = random.randint(0, 456)
        score += 1
    if rock.colliderect(player):
        lives -= 1
        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 == pygame.KEYDOWN:
            if event.key == pygame.K_RETURN and state == "menu":
                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