Menu
CoddyTech

Ayunkan Pedang

Termasuk bagian Proyek dalam perjalanan Pygame di Coddy. Pelajaran 26 dari 31.

Tombol Space mengayunkan pedang knight selama 12 frame, ke arah yang dihadapinya. Slime yang tersentuh bilah pedang akan lenyap.

Knight menyimpan arah terakhir ia berjalan di knight.facing: (1, 0) berarti kanan, (0, -1) berarti atas. Bilah pedang berupa kotak berukuran 36 kali 36 yang berjarak 34 piksel dari titik tengahnya ke arah tersebut:

def sword(self):
    box = pygame.Rect(0, 0, 36, 36)
    box.centerx = self.rect.centerx + self.facing[0] * 34
    box.centery = self.rect.centery + self.facing[1] * 34
    return box

knight.swing menghitung mundur frame ayunan hingga 0, dan tombol Space memulai ayunan baru hanya saat nilainya 0, jadi menahan tombol Space tidak akan membuat bilah pedang terus terayun. Selama ayunan berlangsung, setiap musuh yang menyentuh kotak akan dihapus dengan enemy.kill(), yang mengeluarkan sprite dari setiap grup tempatnya berada.

sword.png mengarah ke atas. pygame.transform.rotate memutarnya berlawanan arah jarum jam sesuai sudut untuk setiap arah:

ANGLES = {(0, -1): 0, (-1, 0): 90, (0, 1): 180, (1, 0): -90}
challenge icon

Tantangan

Sulit
  1. Tambahkan ANGLES di bawah gambar.
  2. Berikan knight self.facing = (1, 0), self.swing = 0 dan method sword di atas.
  3. Setiap tombol panah juga menetapkan knight.facing: Kiri (-1, 0), Kanan (1, 0), Atas (0, -1), Bawah (0, 1).
  4. Tekan Space saat state == "playing" dan knight.swing == 0 untuk memicu KEYDOWN yang menetapkan knight.swing = 12.
  5. Sebelum pemeriksaan hurt: selama knight.swing > 0, kurangi nilainya sebanyak 1 dan panggil kill() pada setiap musuh yang rect-nya bertabrakan dengan knight.sword().
  6. Saat mengayunkan pedang, gambar pygame.transform.rotate(sword_img, ANGLES[knight.facing]) dengan titik tengah di knight.sword(), setelah knight.

Coba sendiri

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
        self.hearts = 3
        self.hurt = 0

    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)
state = "playing"

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    if state == "playing":
        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])

        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"

    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)
    if knight.hurt % 10 < 5:
        draw(knight.image, knight.rect)
    for i in range(3):
        if i < knight.hearts:
            screen.blit(heart_img, (8 + i * 40, 6))
        else:
            screen.blit(empty_img, (8 + i * 40, 6))
    if room.has_key:
        screen.blit(key_img, (136, 6))
    if state == "lost":
        message = font.render("Game over", True, (240, 240, 240))
        screen.blit(message, message.get_rect(center=(336, 240)))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Proyek

Latihan sendiri: Playground Pygame