Một tảng đá sau mỗi 800 ms
Thuộc phần Luật chơi trong hành trình Pygame của Coddy. Bài học 12 trên 30.
Từng viên đá một tạo nên trò chơi yên ắng. Khi có bộ hẹn giờ, một viên đá mới có thể rơi xuống sau mỗi 800 ms, trong khi những viên khác vẫn đang rơi. Hãy lưu các viên đá trong một danh sách:
SPAWN = pygame.USEREVENT + 2
...
if event.type == SPAWN and state == "play":
rocks.append(pygame.Rect(random.randint(0, 456), -24, 24, 24))Mỗi sự kiện có số riêng, vì vậy SPAWN là USEREVENT + 2, lớn hơn POINT một đơn vị. Khởi động bộ hẹn giờ này cùng với bộ hẹn giờ kia, rồi đặt rocks = [] trong reset().
Ở mỗi khung hình, di chuyển tất cả các viên đá, rồi chỉ giữ lại những viên vẫn còn trong cửa sổ:
for rock in rocks:
rock.y += 4
rocks = [rock for rock in rocks if rock.top <= 360]Nếu thiếu dòng thứ hai, mọi viên đá từng rơi xuống sẽ vẫn nằm trong danh sách. Trò chơi sẽ di chuyển và kiểm tra hàng nghìn viên đá mà không ai nhìn thấy, khiến trò chơi chậm dần khi bạn chơi lâu hơn.
Phần vẽ và kiểm tra va chạm cũng trở thành các vòng lặp trên rocks.
Thử thách
Trung bìnhCứ mỗi 800 ms, thả một tảng đá mới.
- Tạo danh sách
rocksrỗng trongreset()để chứa cácrock. - Thêm
SPAWN = pygame.USEREVENT + 2;reset()khởi động bộ hẹn giờ với khoảng thời gian 800 ms. - Khi đang chơi, sự kiện
SPAWNthêm một tảng đá kích thước 24 x 24 tại y -24, với x ngẫu nhiên từ 0 đến 456. - Di chuyển các tảng đá xuống 4 đơn vị, giữ lại những tảng có cạnh trên không vượt quá 360, vẽ và kiểm tra va chạm với từng tảng.
Nhấn Chơi: nhiều tảng đá cùng rơi xuống. 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
def reset():
global player, rock, score, lives, hit_time
player = pygame.Rect(220, 300, 40, 40)
rock = pygame.Rect(random.randint(0, 456), -24, 24, 24)
score = 0
lives = 3
hit_time = -2000
pygame.time.set_timer(POINT, 1000)
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))
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)
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, hit_time
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)
now = pygame.time.get_ticks()
if rock.colliderect(player) and now - hit_time > 1000:
hit_time = now
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 == POINT and state == "play":
score += 1
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
3Bộ hẹn giờ và các đợt
An toàn sau khi trúng đònSự kiện hẹn giờMột tảng đá sau mỗi 800 msCác đợt ngày càng lớnĐếm ngược đến lúc bắt đầuTự luyện tập: Playground Pygame