Một con quái vật
Bài học 5 trên 25 trong khóa học Dự án Pygame: Trấn giữ thành trì của Coddy.
Một con quái vật đi thẳng về phía hiệp sĩ. pygame.Vector2 lưu một giá trị x và một giá trị y có thể là số thập phân, và bạn có thể cộng, trừ các Vector2 hoàn chỉnh:
step = pygame.Vector2(target) - self.pos
if step.length() > 0:
self.pos += step.normalize() * self.speed
self.rect.center = self.posstep hướng từ quái vật đến hiệp sĩ, dù họ cách xa nhau đến đâu. normalize() giữ nguyên hướng nhưng đưa độ dài về 1 pixel, nên khi nhân với self.speed, quái vật di chuyển đúng bằng tốc độ của nó trong mỗi khung hình, theo bất kỳ hướng nào. Rect chỉ lưu số nguyên, vì vậy vị trí chính xác nằm trong self.pos và Rect đi theo vị trí đó.
Mã khởi đầu hiện có MONSTERS, một bảng có một hàng cho mỗi loại quái vật, nên sau này bạn chỉ cần thêm một dòng để bổ sung loại mới. Quái vật thuộc loại "slime" lấy hình ảnh bằng MONSTERS["slime"]["image"].
Thử thách
Trung bìnhMột con slime đi thẳng về phía hiệp sĩ.
- Viết
class Monster(pygame.sprite.Sprite):__init__(self, kind, pos)lưukind,imagevàspeedtừMONSTERS, lưuposdưới dạng Vector2 và tạo mộtrectkích thước 28 x 28, lấy tâm tại vị trí đó. - Viết
update(self, target)như trên. - Trong
Night, tạoself.monsters, một Group chứaMonster("slime", (0, 0)), rồi gọiself.monsters.update(self.knight.rect.center)trongupdate. - Dùng
drawđể vẽ từng quái vật trước hiệp sĩ.
Nhấn “Chơi”: một con slime đi từ góc trên bên trái thẳng về phía hiệp sĩ. Sau đó nhấn “Kiểm tra”.
Tự làm thử
import pygame
WIDTH, HEIGHT = 720, 480
TILE = 48
NIGHT = 60 * 60 * 4
WHITE = (240, 240, 240)
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Hold the Keep")
clock = pygame.time.Clock()
font = pygame.font.Font("assets/fonts/pixel.ttf", 24)
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")
MONSTERS = {
"slime": {"image": load("slime.png"), "speed": 1.0},
}
courtyard = pygame.Rect(TILE, TILE, WIDTH - 2 * TILE, HEIGHT - 2 * TILE)
background = pygame.Surface((WIDTH, HEIGHT))
for row in range(HEIGHT // TILE):
for col in range(WIDTH // TILE):
x = col * TILE
y = row * TILE
if courtyard.collidepoint(x, y):
background.blit(floor_img, (x, y))
else:
background.blit(wall_img, (x, y))
def draw(image, center):
screen.blit(image, image.get_rect(center=center))
def clock_text(frames):
seconds = frames // 60
return f"{seconds // 60}:{seconds % 60:02d}"
class Knight(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = knight_img
self.rect = pygame.Rect(0, 0, 24, 24)
self.rect.center = courtyard.center
self.speed = 3
def move(self, keys):
if keys[pygame.K_LEFT]:
self.rect.x -= self.speed
if keys[pygame.K_RIGHT]:
self.rect.x += self.speed
if keys[pygame.K_UP]:
self.rect.y -= self.speed
if keys[pygame.K_DOWN]:
self.rect.y += self.speed
self.rect.clamp_ip(courtyard)
class Night:
def __init__(self):
self.knight = Knight()
self.time = 0
def update(self, keys):
self.time += 1
self.knight.move(keys)
night = Night()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
night.update(pygame.key.get_pressed())
screen.blit(background, (0, 0))
knight = night.knight
draw(knight.image, knight.rect.center)
left = font.render(clock_text(max(0, NIGHT - night.time)) + " until dawn", True, WHITE)
screen.blit(left, left.get_rect(center=(WIDTH // 2, 24)))
pygame.display.flip()
clock.tick(60)
pygame.quit()
Tất cả bài học trong Dự án Pygame: Trấn giữ thành trì
Tự luyện tập: Playground Pygame