Menu
CoddyTech

Hati

Pelajaran 7 dari 20 dalam kursus Proyek Pygame: Pertahankan Benteng di Coddy.

Monster yang mencapai ksatria mengurangi satu hati. Tanpa jeda setelah terkena serangan, monster yang berdiri di atas ksatria akan mengurangi satu hati setiap frame, kelima hati dalam waktu seperduabelas detik. Jadi, serangan memulai penghitung, hurt, dan selama penghitungannya berjalan, tidak ada yang bisa melukai ksatria:

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) memberikan sprite pertama dalam group yang rect-nya bertumpang tindih, atau None.

Game sekarang memiliki status: "playing" dan "lost". Perulangan menjalankan night.update hanya saat game berlangsung, jadi saat hati habis, semuanya berhenti di tempatnya.

Selama hurt menghitung mundur, gambar ksatria hanya saat knight.hurt % 10 < 5: ksatria berkedip, dan pemain melihat alasan tidak ada yang bisa melukainya.

challenge icon

Tantangan

Sedang
  1. Beri ksatria nilai hearts = 5, max_hearts = 5, dan hurt = 0; beri Night state = "playing".
  2. Tulis take_hits seperti di atas; saat jumlah hati mencapai 0, atur self.state = "lost". Panggil fungsi ini terakhir di update.
  3. Dalam perulangan, perbarui hanya saat if night.state == "playing", dan gambar ksatria hanya saat knight.hurt % 10 < 5.
  4. Muat heart.png dan heart_empty.png sebagai heart_img dan empty_img. Untuk setiap max_hearts, gambar hati penuh atau kosong di (8 + i * 34, 6).
  5. Buat big_font = pygame.font.Font("assets/fonts/pixel.ttf", 48) dan, saat kalah, gambar "The keep has fallen" di tengah pada (360, 200).

Coba sendiri

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

Semua pelajaran di Proyek Pygame: Pertahankan Benteng

Latihan sendiri: Playground Pygame