Slime tuần tra
Thuộc phần Dự án trong hành trình Pygame của Coddy. Bài học 26 trên 33.
Mỗi S trên bản đồ là một con slime trượt dọc theo hàng của nó, 2 pixel mỗi khung hình, rồi quay lại khi gặp tường.
Sprite Enemy tự lưu tốc độ của nó trong dx và dy, nên sau này cùng một lớp có thể tạo ra những quái vật di chuyển lên xuống. Phương thức update di chuyển nó, và nếu lúc đó nó chồng lên một khối thì nó lùi lại rồi đổi hướng:
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.dyrect.collidelist(rects) trả về chỉ số của Rect đầu tiên trong danh sách bị chồng lên, hoặc -1 nếu không có Rect nào bị chồng lên.
Slime nằm trong một Group, room.enemies. room.enemies.update(room.walls + [room.door]) gọi update trên từng slime với danh sách đó. Cửa luôn nằm trong danh sách, nên slime không bao giờ đi ra ngoài qua cửa đang mở.
random.choice([-2, 2]) khiến mỗi slime bắt đầu di chuyển sang trái hoặc phải, vì vậy không có hai ván chơi nào giống nhau.
Thử thách
Trung bìnhThêm slime tuần tra trong phòng.
- Thêm
import randomvà lớpEnemyở phía trên:__init__(self, image, rect, dx, dy)giữ cả bốn giá trị, cùng vớiupdate. - Trong
Room, tạoself.enemies = pygame.sprite.Group(); ký tựSthêmEnemy(slime_img, cell_rect(col, row), speed, 0), vớispeed = random.choice([-2, 2]). - Sau phần xử lý cửa, gọi
room.enemies.update(room.walls + [room.door]); vẽ kẻ địch trước hiệp sĩ.
Nhấn Chơi: slime tuần tra theo hàng của nó. Sau đó nhấn Kiểm tra.
Tự làm thử
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 Room:
def __init__(self, lines):
self.walls = []
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)
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
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)
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