Menu
CoddyTech

القلوب والضربات

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

pygame.sprite.spritecollide(knight, room.enemies, False) هي قائمة الأعداء الذين يلامسون الفارس. تُبقي False الأعداء في المجموعة.

الوحل الذي ينزلق عبر الفارس يلامسه لمدة 30 إطارًا تقريبًا. إن أخذ قلب في كل إطار سينهي اللعبة في نصف ثانية. لذلك، بعد الإصابة، يكون الفارس آمنًا لمدة 60 إطارًا، أي ثانية واحدة: يحصي knight.hurt هذه الإطارات تنازليًا، ولا تُحتسب أي ملامسة ما دامت قيمته أكبر من 0.

if knight.hurt > 0:
    knight.hurt -= 1
elif pygame.sprite.spritecollide(knight, room.enemies, False):
    knight.hearts -= 1
    knight.hurt = 60
    if knight.hearts == 0:
        state = "lost"
knight.hurt % 10 < 5. سيومض: 5 إطارات ظاهرًا و5 إطارات مختفيًا.

يعمل state كما في Sky Runner: كل ما يتحرك يقع ضمن if state == "playing":، لذا عند "lost" تبقى القلعة ساكنة.

challenge icon

التحدي

متوسط
  1. اجعل للفارس self.hearts = 3 وself.hurt = 0.
  2. عيّن state = "playing" قبل الحلقة، وضع كل التعليمات البرمجية الخاصة بالحركة والمفتاح والباب والأعداء ضمن if state == "playing":.
  3. بعد تحرّك الأعداء، أضف اختبار الضرر المذكور أعلاه.
  4. ارسم الفارس فقط عندما يتحقق knight.hurt % 10 < 5. ارسم 3 قلوب عند (8 + i * 40, 6) لقيم i من 0 إلى 2: استخدم heart_img عندما يكون i < knight.hearts، وإلا فاستخدم empty_img.
  5. عندما تكون state هي "lost"، اعرض Game over باللون (240, 240, 240)، في المنتصف عند (336, 240).

جرّب بنفسك

import random
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 Enemy(pygame.sprite.Sprite):
    def __init__(self, image, rect, dx, dy):
        super().__init__()
        self.image = image
        self.rect = rect
        self.dx = dx
        self.dy = dy

    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


class Room:
    def __init__(self, lines):
        self.walls = []
        self.enemies = pygame.sprite.Group()
        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)
                elif char == "S":
                    speed = random.choice([-2, 2])
                    self.enemies.add(Enemy(slime_img, cell_rect(col, row), speed, 0))

    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

    room.enemies.update(room.walls + [room.door])

    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)
    for enemy in room.enemies:
        draw(enemy.image, enemy.rect)
    draw(knight.image, knight.rect)
    if room.has_key:
        screen.blit(key_img, (136, 6))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

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

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