Trái tim và đòn đánh
Thuộc phần Dự án trong hành trình Pygame của Coddy. Bài học 27 trên 33.
Hiệp sĩ có 3 trái tim. Chạm phải slime sẽ mất một trái tim, và khi còn 0 trái tim, trò chơi kết thúc.
pygame.sprite.spritecollide(knight, room.enemies, False) là danh sách kẻ địch đang chạm vào hiệp sĩ. False giữ chúng trong nhóm.
Slime trượt qua hiệp sĩ sẽ chạm vào anh ấy trong khoảng 30 khung hình. Nếu mất một trái tim ở mỗi khung hình, trò chơi sẽ kết thúc chỉ sau nửa giây. Vì vậy, sau khi bị đánh, hiệp sĩ sẽ an toàn trong 60 khung hình, tức một giây: knight.hurt đếm ngược số khung hình này, và không tính lần chạm nào khi giá trị còn lớn hơn 0.
if knight.hurt > 0:
knight.hurt -= 1
elif pygame.sprite.spritecollide(knight, room.enemies, False):
knight.hearts -= 1
knight.hurt = 60
if knight.hearts == 0:
state = "lost"Cho người chơi biết hiệp sĩ đang an toàn: khi bị thương, chỉ vẽ hiệp sĩ nếu knight.hurt % 10 < 5. Hiệp sĩ nhấp nháy, hiện trong 5 khung hình rồi ẩn trong 5 khung hình.
state hoạt động giống như trong Sky Runner: mọi thứ chuyển động đều nằm trong if state == "playing":, nên khi ở trạng thái "lost", pháo đài sẽ đứng yên.
Thử thách
Trung bìnhKhi chạm vào slime, hiệp sĩ sẽ mất tim.
- Thêm cho hiệp sĩ
self.hearts = 3vàself.hurt = 0. - Đặt
state = "playing"; thêm phần cập nhật bên dướiif state == "playing":, kiểm tra trạng thái bị thương sau phần cập nhật kẻ địch. - Vẽ hiệp sĩ khi
knight.hurt % 10 < 5và vẽ số tim. - Khi ở trạng thái
"lost", hiển thịGame overở giữa tại tọa độ (336, 240).
Nhấn Chơi rồi chạm vào slime: một tim sẽ cạn. Sau đó nhấn Kiểm tra.
Tự làm thử
import random
import pygame
TILE = 48
MAP = [
"##############",
"#............#",
"#.P......K...#",
"#............#",
"#....##......D",
"#....##......#",
"##.#######.###",
"#............#",
"#...S....H...#",
"##############",
]
pygame.init()
screen = pygame.display.set_mode((672, 480))
pygame.display.set_caption("Rune Keep")
clock = pygame.time.Clock()
font = pygame.font.Font("assets/fonts/pixel.ttf", 32)
def load(name):
return pygame.image.load("assets/images/" + name).convert_alpha()
floor_img = load("floor.png")
wall_img = load("wall.png")
knight_img = load("knight.png")
door_img = load("dungeon_door.png")
key_img = load("key.png")
slime_img = load("slime.png")
spider_img = load("spider.png")
heart_img = load("heart.png")
empty_img = load("heart_empty.png")
sword_img = load("sword.png")
potion_img = load("potion_red.png")
def draw(image, rect):
screen.blit(image, image.get_rect(center=rect.center))
def cell_rect(col, row):
rect = pygame.Rect(0, 0, 32, 32)
rect.center = (col * TILE + 24, row * TILE + 24)
return rect
class Enemy(pygame.sprite.Sprite):
def __init__(self, image, rect, dx, dy):
super().__init__()
self.image = image
self.rect = rect
self.dx = dx
self.dy = dy
def update(self, blocks):
self.rect.x += self.dx
self.rect.y += self.dy
if self.rect.collidelist(blocks) != -1:
self.rect.x -= self.dx
self.rect.y -= self.dy
self.dx = -self.dx
self.dy = -self.dy
class Room:
def __init__(self, lines):
self.walls = []
self.enemies = pygame.sprite.Group()
self.start = (72, 72)
self.has_key = False
self.door_open = False
for row, line in enumerate(lines):
for col, char in enumerate(line):
if char == "#":
self.walls.append(pygame.Rect(col * TILE, row * TILE, TILE, TILE))
elif char == "P":
self.start = cell_rect(col, row).center
elif char == "K":
self.key = cell_rect(col, row)
elif char == "D":
self.door = pygame.Rect(col * TILE, row * TILE, TILE, TILE)
elif char == "S":
speed = random.choice([-2, 2])
self.enemies.add(Enemy(slime_img, cell_rect(col, row), speed, 0))
def blocks(self):
if self.door_open:
return self.walls
return self.walls + [self.door]
class Knight(pygame.sprite.Sprite):
def __init__(self, center):
super().__init__()
self.image = knight_img
self.rect = pygame.Rect(0, 0, 32, 32)
self.rect.center = center
def move(self, dx, dy, blocks):
self.rect.x += dx
for block in blocks:
if self.rect.colliderect(block):
if dx > 0:
self.rect.right = block.left
elif dx < 0:
self.rect.left = block.right
self.rect.y += dy
for block in blocks:
if self.rect.colliderect(block):
if dy > 0:
self.rect.bottom = block.top
elif dy < 0:
self.rect.top = block.bottom
room = Room(MAP)
knight = Knight(room.start)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys = pygame.key.get_pressed()
dx = 0
dy = 0
if keys[pygame.K_LEFT]:
dx = -3
if keys[pygame.K_RIGHT]:
dx = 3
if keys[pygame.K_UP]:
dy = -3
if keys[pygame.K_DOWN]:
dy = 3
knight.move(dx, dy, room.blocks())
knight.rect.clamp_ip(screen.get_rect())
if not room.has_key and knight.rect.colliderect(room.key):
room.has_key = True
if room.has_key and knight.rect.inflate(8, 8).colliderect(room.door):
room.door_open = True
room.enemies.update(room.walls + [room.door])
for row in range(10):
for col in range(14):
screen.blit(floor_img, (col * TILE, row * TILE))
for wall in room.walls:
screen.blit(wall_img, wall)
if not room.door_open:
screen.blit(door_img, room.door)
if not room.has_key:
draw(key_img, room.key)
for enemy in room.enemies:
draw(enemy.image, enemy.rect)
draw(knight.image, knight.rect)
if room.has_key:
screen.blit(key_img, (136, 6))
pygame.display.flip()
clock.tick(60)
pygame.quit()
Tất cả bài học trong Dự án
Tự luyện tập: Playground Pygame