Menu
CoddyTech

Herzen und Treffer

Teil des Abschnitts Projekte der Pygame-Journey von Coddy. Lektion 25 von 31.

Der Ritter hat 3 Herzen. Die Berührung eines Schleims nimmt ihm ein Herz, und bei 0 ist das Spiel vorbei.

pygame.sprite.spritecollide(knight, room.enemies, False) ist die Liste der Gegner, die den Ritter berühren. False lässt sie in der Gruppe.

Ein Schleim, der durch den Ritter gleitet, berührt ihn etwa 30 Frames lang. Würde ihm in jedem Frame ein Herz abgezogen, wäre das Spiel nach einer halben Sekunde vorbei. Nach einem Treffer ist der Ritter also 60 Frames lang, eine Sekunde, geschützt: knight.hurt zählt sie herunter, und solange der Wert über 0 liegt, wird keine Berührung gezählt.

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"

Zeige dem Spieler, dass er geschützt ist: Zeichne den Ritter während der Verletzungszeit nur, wenn knight.hurt % 10 < 5. Er blinkt: 5 Frames lang sichtbar, 5 unsichtbar.

state funktioniert wie in Sky Runner: Alles, was sich bewegt, steht unter if state == "playing":, damit die Festung bei "lost" stillsteht.

challenge icon

Aufgabe

Mittel
  1. Gib dem Ritter self.hearts = 3 und self.hurt = 0.
  2. Setze state = "playing" vor der Schleife und platziere den gesamten Code für Bewegung, Schlüssel, Tür und Gegner unter if state == "playing":.
  3. Füge nach der Bewegung der Gegner die obige Prüfung auf Verletzungen hinzu.
  4. Zeichne den Ritter nur, solange knight.hurt % 10 < 5 gilt. Zeichne 3 Herzen bei (8 + i * 40, 6) für i mit den Werten 0, 1, 2: heart_img, solange i < knight.hearts gilt, andernfalls empty_img.
  5. Wenn state den Wert "lost" hat, zeige Game over in (240, 240, 240), zentriert bei (336, 240).

Probier es selbst

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

Alle Lektionen in Projekte

Übe selbstständig: Pygame-Playground