Mức sàn cho khoảng cách
Thuộc phần Luật chơi trong hành trình Pygame của Coddy. Bài học 16 trên 30.
Đá rơi nhanh hơn, nhưng chúng vẫn xuất hiện sau mỗi 800 ms. Đá xuất hiện thường xuyên hơn cũng khiến trò chơi khó hơn. Bắt đầu với khoảng cách một giây rồi thu ngắn khoảng cách đó theo thời gian:
interval = 1000 - seconds * 60Sau 10 giây, khoảng cách là 400 ms; sau 17 giây, khoảng cách nhỏ hơn 0, còn bộ hẹn giờ sẽ dừng nếu khoảng cách bằng hoặc nhỏ hơn 0. Trò chơi cần có một mức sàn: khoảng cách nhỏ nhất mà nó không bao giờ giảm xuống dưới. max trả về số lớn hơn trong các số được đưa vào, nên nó không bao giờ để khoảng cách giảm xuống dưới số đầu tiên:
interval = max(400, 1000 - seconds * 60)Bộ hẹn giờ giữ nguyên khoảng cách được thiết lập lúc bắt đầu. Để thay đổi khoảng cách, hãy khởi động lại bộ hẹn giờ: mỗi khi một tảng đá rơi xuống, hãy tính khoảng cách mới rồi gọi set_timer với giá trị đó. Tảng đá tiếp theo sẽ xuất hiện sau khoảng thời gian ấy.
if event.type == SPAWN and state == "play":
rocks.append(...)
seconds = (pygame.time.get_ticks() - start_time) // 1000
interval = max(400, 1000 - seconds * 60)
pygame.time.set_timer(SPAWN, interval)Thử thách
Trung bìnhTheo thời gian, hãy cho các tảng đá xuất hiện gần nhau hơn, đến một giới hạn.
- Trong
reset(), bắt đầu bộ hẹn giờSPAWNở mức 1000 ms. - Mỗi khi một tảng đá rơi xuống, đặt
interval = max(400, 1000 - seconds * 60)rồi khởi động lại bộ hẹn giờSPAWNvới giá trị đó.
Nhấn Chơi: các tảng đá xuất hiện ngày càng thường xuyên hơn, rồi giữ nguyên nhịp độ. 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, start_time
player = pygame.Rect(220, 300, 40, 40)
rocks = []
score = 0
lives = 3
start_time = pygame.time.get_ticks()
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())
seconds = (pygame.time.get_ticks() - start_time) // 1000
speed = 3 + seconds // 5
kept = []
for rock in rocks:
rock.y += speed
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