Menu
CoddyTech

قلوب

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

الوحش الذي يصل إلى الفارس يكلّفه قلبًا. من دون توقّف بعد الإصابة، سيأخذ الوحش الواقف على الفارس قلبًا في كل إطار، أي القلوب الخمسة كلها في اثني عشر من الثانية. لذلك تبدأ الإصابة عدّادًا، hurt، وأثناء تناقصه لا يمكن لأي شيء إيذاء الفارس:

def take_hits(self):
    knight = self.knight
    if knight.hurt > 0:
        knight.hurt -= 1
    elif pygame.sprite.spritecollideany(knight, self.monsters):
        knight.hearts -= 1
        knight.hurt = 75

تعطي spritecollideany(sprite, group) أول كائن في المجموعة يتداخل مستطيله، أو None.

أصبحت اللعبة الآن ذات حالات: "playing" و"lost". لا تشغّل الحلقة night.update إلا أثناء اللعب، لذا عندما تنفد القلوب يتوقف كل شيء في مكانه.

أثناء تناقص hurt، ارسم الفارس فقط عندما تكون knight.hurt % 10 < 5: فهو يومض، ويرى اللاعب سبب عدم تأثره بأي شيء.

challenge icon

التحدي

متوسط
  1. اجعل قلوب الفارس hearts = 5، وmax_hearts = 5، وhurt = 0؛ واجعل Night في الحالة state = "playing".
  2. اكتب take_hits كما في الأعلى؛ وعندما يصل عدد القلوب إلى 0، عيّن self.state = "lost". واستدعِها أخيرًا في update.
  3. في الحلقة، حدّث فقط عند تحقق if night.state == "playing"، وارسم الفارس فقط عندما يتحقق knight.hurt % 10 < 5.
  4. حمّل heart.png وheart_empty.png في heart_img وempty_img. ولكل قلب من max_hearts، ارسم قلبًا ممتلئًا أو فارغًا عند (8 + i * 34, 6).
  5. أنشئ big_font = pygame.font.Font("assets/fonts/pixel.ttf", 48)، وعند الخسارة، ارسم "The keep has fallen" في الوسط عند (360, 200).

جرّب بنفسك

import random
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}"


def edge_point():
    side = random.randint(0, 3)
    if side == 0:
        return (random.randint(0, WIDTH), -24)
    if side == 1:
        return (random.randint(0, WIDTH), HEIGHT + 24)
    if side == 2:
        return (-24, random.randint(0, HEIGHT))
    return (WIDTH + 24, random.randint(0, HEIGHT))


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 Monster(pygame.sprite.Sprite):
    def __init__(self, kind, pos):
        super().__init__()
        self.kind = kind
        self.image = MONSTERS[kind]["image"]
        self.speed = MONSTERS[kind]["speed"]
        self.pos = pygame.Vector2(pos)
        self.rect = pygame.Rect(0, 0, 28, 28)
        self.rect.center = self.pos

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


class Night:
    def __init__(self):
        self.knight = Knight()
        self.time = 0
        self.monsters = pygame.sprite.Group()

    def spawn(self):
        if self.time % 60 == 0:
            self.monsters.add(Monster("slime", edge_point()))

    def update(self, keys):
        self.time += 1
        self.knight.move(keys)
        self.spawn()
        self.monsters.update(self.knight.rect.center)


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
    for monster in night.monsters:
        draw(monster.image, monster.rect.center)
    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