Melarikan Diri dari Menara
Termasuk bagian Proyek dalam perjalanan Pygame di Coddy. Pelajaran 29 dari 31.
Lewat pintu ruangan terakhir, ksatria berhasil keluar: game menampilkan You escaped!. Setelah salah satu dari kedua akhir, tekan R untuk memulai lagi di ruangan pertama.
Sekarang, pengujian pintu memiliki dua hasil:
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.startMemulai lagi hanya memerlukan sedikit kode karena class menampung game. Room baru membangun ulang ruangan pertama dengan kunci, pintu, dan semua slime kembali ke tempatnya. Knight baru memiliki 3 hati, tidak terluka, dan tidak sedang mengayunkan senjata.
elif event.key == pygame.K_r and state != "playing":
level = 0
room = Room(ROOMS[level])
knight = Knight(room.start)
state = "playing"Mainkan dari awal. Jika sebuah ruangan terlalu sulit atau terlalu cepat, ubah peta: pindahkan slime, tambahkan ramuan, gambar ruangan baru, lalu tambahkan ke ROOMS. Tidak ada hal lain yang perlu diubah karena setiap aturan membaca peta. Slime di koridor selebar satu tile tidak bisa dilewati, jadi harus dilawan; slime di aula terbuka bisa dihindari.
Tantangan
Sedang- Ubah pemeriksaan pintu seperti di atas: di ruangan terakhir, atur state menjadi
"won". - Saat state bernilai
"won", tampilkanYou escaped!di tengah pada (336, 240). - Setelah salah satu akhir permainan, tampilkan
Press R to play againdi tengah pada (336, 290). - Tekan R saat state bukan
"playing"untuk memulai lagi seperti di atas, menggunakanKEYDOWN.
Coba sendiri
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()
Semua pelajaran di Proyek
3Menara Rune
Ruang Pertama MenaraKesatria BerjalanLatihan sendiri: Playground Pygame