Menu
CoddyTech

الوحوش الهلامية في دورية

جزء من قسم المشاريع في رحلة Pygame على Coddy. الدرس 24 من 31.

كل S على الخريطة هو وحش هلامي ينزلق على امتداد صفه بمقدار بكسلين في كل إطار، ويعود عند اصطدامه بجدار.

يحتفظ الكائن الرسومي Enemy بسرعته الخاصة، dx وdy، لذا يمكن للفئة نفسها لاحقًا إنشاء وحوش تتحرك إلى أعلى وأسفل. تحرّكه دالته update، وإذا تداخل الآن مع كتلة، فإنه يتراجع ويعكس اتجاهه:

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

تعطي rect.collidelist(rects) فهرس أول Rect في القائمة يتداخل معه، أو -1 إذا لم يتداخل مع أيٍّ منها.

تعيش الوحوش الهلامية في مجموعة، room.enemies. يستدعي room.enemies.update(room.walls + [room.door]) الدالة update لكل وحش هلامي، مع تمرير تلك القائمة إليها. يكون الباب ضمن القائمة دائمًا، لذا لا يضل أي وحش هلامي طريقه إلى خارج باب مفتوح.

تبدأ random.choice([-2, 2]) حركة كل وحش هلامي إلى اليسار أو اليمين، لذا لا تتطابق أي لعبتين.

challenge icon

التحدي

متوسط
  1. أضف import random في الأعلى.
  2. اكتب class Enemy(pygame.sprite.Sprite) مع __init__(self, image, rect, dx, dy)، بحيث يحتفظ بالقيم الأربع كسمات، وأضف طريقة update المذكورة أعلاه.
  3. في Room، عيّن self.enemies = pygame.sprite.Group(). عند وجود S، أضف Enemy(slime_img, cell_rect(col, row), speed, 0) مع speed = random.choice([-2, 2]).
  4. في كل إطار، بعد رسم الباب، استدعِ room.enemies.update(room.walls + [room.door]).
  5. ارسم كل عدو باستخدام draw(enemy.image, enemy.rect)، قبل الفارس.

جرّب بنفسك

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()
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس المشاريع

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