Menu
CoddyTech

وحش

الدرس 5 من 20 في دورة مشروع Pygame: الصمود في القلعة على Coddy.

يسير وحش مباشرةً نحو الفارس. يحتوي pygame.Vector2 على قيمة x وقيمة y يمكن أن تكونا عشريتين، ويمكنك جمع قيم Vector2 كاملة وطرحها:

step = pygame.Vector2(target) - self.pos
if step.length() > 0:
    self.pos += step.normalize() * self.speed
self.rect.center = self.pos

يشير step من الوحش إلى الفارس، مهما كانت المسافة بينهما. تحافظ normalize() على اتجاهه، لكنها تجعله بطول 1 بكسل، لذا عند ضربه في self.speed يتحرك الوحش بسرعته تمامًا في كل إطار، في أي اتجاه. لا يحتفظ Rect إلا بأعداد صحيحة، لذا يُخزَّن الموقع الدقيق في self.pos ويتبعه Rect.

يحصل كل نوع من الوحوش على صف واحد في جدول، لذا فإن إضافة نوع لاحقًا لا تتطلب إلا سطرًا واحدًا:

MONSTERS = {
    "slime": {"image": load("slime.png"), "speed": 1.0},
}

تأتي الوحوش من خارج النافذة. تختار edge_point() جانبًا باستخدام random.randint(0, 3) وموقعًا عشوائيًا على امتداده، على بُعد 24 بكسل.

challenge icon

التحدي

متوسط
  1. أدرج import random وأضف جدول MONSTERS الذي يحتوي على وحش الوحل.
  2. اكتب edge_point(): الجانب 0 هو (random.randint(0, WIDTH), -24)، والجانب 1 له القيمة نفسها عند HEIGHT + 24، والجانب 2 هو (-24, random.randint(0, HEIGHT))، والجانب 3 له القيمة نفسها عند WIDTH + 24.
  3. اكتب class Monster(pygame.sprite.Sprite): تحتفظ __init__(self, kind, pos) بالقيم kind وimage وspeed من الجدول، وبـpos كـVector2، وبـrect أبعاده 28 × 28 ومتمركز حوله؛ أما update(self, target) فيتحرك كما سبق.
  4. في Night: أنشئ self.monsters = pygame.sprite.Group() مع وحش وحل واحد عند edge_point()؛ واستدعِ update الدالة self.monsters.update(self.knight.rect.center). ارسم كل وحش باستخدام draw قبل الفارس.

جرّب بنفسك

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")

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()

جميع دروس مشروع Pygame: الصمود في القلعة

تدرّب بنفسك: Playground لـ Pygame