Menu
CoddyTech

לבבות ומכות

חלק מהיחידה פרויקטים במסלול ה-Pygame של Coddy. שיעור 25 מתוך 31.

לאביר יש 3 לבבות. מגע של slime גוזל אחד, וכשמגיעים ל־0 המשחק מסתיים.

pygame.sprite.spritecollide(knight, room.enemies, False) היא רשימת האויבים שנוגעים באביר. False משאיר אותם בקבוצה.

slime שמחליק דרך האביר נוגע בו במשך כ־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, 1, 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()

כל השיעורים ביחידה פרויקטים

תרגלו בעצמכם: Pygame אונליין