Menu
CoddyTech

Hisardan Kaçış

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

Son odanın kapısından geçen şövalye dışarı çıkar: oyun You escaped! mesajını gösterir. Her iki bitişten sonra da R, oyunu ilk odada yeniden başlatır.

Kapı kontrolünün artık iki sonucu var:

if room.door_open and knight.rect.colliderect(room.door):
    if level == len(ROOMS) - 1:
        state = "won"
    else:
        level += 1
        room = Room(ROOMS[level])
        knight.rect.center = room.start

Sınıflar oyunu yönettiği için yeniden başlatma işlemi kısadır. Yeni bir Room, anahtarı, kapısı ve tüm slime’ları yerlerine dönmüş hâlde ilk odayı yeniden oluşturur. Yeni bir Knight 3 kalbe sahip olur; yaralanmamış ve saldırı yapmıyor olur.

elif event.key == pygame.K_r and state != "playing":
    level = 0
    room = Room(ROOMS[level])
    knight = Knight(room.start)
    state = "playing"

Oyunu baştan oyna. Bir oda çok zorsa ya da çok kolaysa haritaları değiştir: bir slime’ı hareket ettir, bir iksir ekle, yeni bir oda çiz ve onu ROOMS listesine ekle. Başka hiçbir şeyi değiştirmen gerekmez, çünkü tüm kurallar haritayı okur. Genişliği bir karo olan bir koridordaki slime’ın yanından geçilemez, onunla savaşmak gerekir; açık bir salondaki slime’dan ise kaçınılabilir.

challenge icon

Görev

Orta
  1. Kapı denetimini yukarıdaki gibi değiştir: son odada durumu "won" olarak ayarla.
  2. Durum "won" olduğunda, You escaped! metnini (336, 240) konumunda ortalanmış şekilde göster.
  3. Her iki bitişten sonra Press R to play again metnini (336, 290) konumunda ortalanmış şekilde göster.
  4. Durum "playing" değilken gerçekleşen bir R KEYDOWN olayı, yukarıdaki gibi yeniden başlatır.

Kendin dene

import random
import pygame

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

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 == "X":
                    speed = random.choice([-2, 2])
                    self.enemies.add(Enemy(spider_img, cell_rect(col, row), 0, speed))
                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


level = 0
room = Room(ROOMS[level])
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
        if room.door_open and knight.rect.colliderect(room.door) and level < len(ROOMS) - 1:
            level += 1
            room = Room(ROOMS[level])
            knight.rect.center = room.start

        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))
    label = font.render(f"Room {level + 1} of {len(ROOMS)}", True, (240, 240, 240))
    screen.blit(label, label.get_rect(topright=(660, 8)))
    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()

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

Kendi başına pratik yap: Pygame playground