Menu
CoddyTech

Hisarın Daha Derinlerine

Coddy'nin Pygame Journey'sinin Projeler bölümünün bir parçası. Ders 28 / 31.

Hisarın üç odası var. Açık kapıdan geçen şövalye, sonraki odanın P noktasından çıkar. X yenidir: yukarı ve aşağı tırmanan bir örümcek.

MAP2 = [
    "##############",
    "#P...#...X...#",
    "#....#.......#",
    "#..S.#..###..#",
    "#....#..#K...#",
    "#.......#....#",
    "##.######X####",
    "#....S.......#",
    "#.H..........#",
    "###D##########",
]
MAP3 = [
    "##############",
    "#....#...X...#",
    "#.P..#.......#",
    "#....#..S....#",
    "#..S.....###.D",
    "#....#...#K.S#",
    "###.###X.#...#",
    "#........#...#",
    "#..H.....#...#",
    "##############",
]
ROOMS = [MAP, MAP2, MAP3]

level, şövalyenin bulunduğu odanın 0'dan başlayarak sayılan numarasıdır. Kapıdan geçmek, haritasından sonraki odayı oluşturur ve şövalyeyi başlangıç noktasına yerleştirir:

level += 1
room = Room(ROOMS[level])
knight.rect.center = room.start

Room sınıfının amacı budur: tek bir satırla duvarları, anahtarı, kapıyı, düşmanları ve iksirleriyle koca bir oda oluşturulur. Şövalye aynı nesnedir, bu yüzden canları da onunla birlikte gelir. Şimdilik son odanın kapısı hiçbir yere çıkmaz: yalnızca level < len(ROOMS) - 1 olduğu sürece ilerle.

Örümcek, dx = 0 ve bir dy değerine sahip bir Enemy'dir. Aynı update yöntemi, duvarlara geldiğinde yönünü değiştirir.

challenge icon

Görev

Zor
  1. Yukarıdaki MAP2, MAP3 ve ROOMS öğelerini MAP altına ekle.
  2. Döngüden önce level = 0 ve room = Room(ROOMS[level]) ayarla.
  3. Room içinde, X karakteri speed = random.choice([-2, 2]) ile Enemy(spider_img, cell_rect(col, row), 0, speed) ekler.
  4. Kapı açıldıktan sonra: room.door_open doğru olduğunda, şövalye room.door nesnesine dokunduğunda ve level < len(ROOMS) - 1 olduğunda, level değerini 1 artır, odayı oluştur ve şövalyeyi başlangıç noktasına taşı.
  5. f"Room {level + 1} of {len(ROOMS)}" metnini (240, 240, 240) renginde göster ve sağ üst köşesini (660, 8) konumuna yerleştir: label.get_rect(topright=(660, 8)).

Kendin dene

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")
ANGLES = {(0, -1): 0, (-1, 0): 90, (0, 1): 180, (1, 0): -90}


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.potions = []
        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))
                elif char == "H":
                    self.potions.append(cell_rect(col, row))

    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
        self.facing = (1, 0)
        self.swing = 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

    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


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
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE and state == "playing" and knight.swing == 0:
                knight.swing = 12

    if state == "playing":
        keys = pygame.key.get_pressed()
        dx = 0
        dy = 0
        if keys[pygame.K_LEFT]:
            dx = -3
            knight.facing = (-1, 0)
        if keys[pygame.K_RIGHT]:
            dx = 3
            knight.facing = (1, 0)
        if keys[pygame.K_UP]:
            dy = -3
            knight.facing = (0, -1)
        if keys[pygame.K_DOWN]:
            dy = 3
            knight.facing = (0, 1)
        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.swing > 0:
            knight.swing -= 1
            for enemy in room.enemies:
                if enemy.rect.colliderect(knight.sword()):
                    enemy.kill()

        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"

        index = knight.rect.collidelist(room.potions)
        if index != -1 and knight.hearts < 3:
            knight.hearts += 1
            room.potions.pop(index)

    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 potion in room.potions:
        draw(potion_img, potion)
    for enemy in room.enemies:
        draw(enemy.image, enemy.rect)
    if knight.hurt % 10 < 5:
        draw(knight.image, knight.rect)
    if knight.swing > 0:
        blade = pygame.transform.rotate(sword_img, ANGLES[knight.facing])
        draw(blade, knight.sword())
    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 iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Projeler bölümündeki tüm dersler

Kendi başına pratik yap: Pygame playground