The Key and the Door
Part of the Projects section of Coddy's Pygame journey. Lesson 23 of 31.
The door in the right wall is locked. The knight picks up the key by walking over it, and with the key, the door opens when he touches it.
A locked door is one more thing that blocks the knight. A method on Room gives the list of Rects that stop him:
def blocks(self):
if self.door_open:
return self.walls
return self.walls + [self.door]Touching the door has a catch: move pushes the knight out of it every frame, so his Rect never overlaps the door. knight.rect.inflate(8, 8) is a copy of his Rect grown by 4 pixels on every side, and that copy does overlap the door he is pressed against.
if room.has_key and knight.rect.inflate(8, 8).colliderect(room.door):
room.door_open = TruePast the open door is the edge of the window, so keep the knight inside with clamp_ip. In a later lesson the doorway leads to the next room. Show the key at the top while he carries it.
Challenge
Medium- In
Room, setself.has_key = Falseandself.door_open = False. AKsetsself.key = cell_rect(col, row); aDsetsself.doorto the 48 by 48 Rect of its tile. - Add the
blocksmethod above and move the knight withknight.move(dx, dy, room.blocks()). - After moving:
knight.rect.clamp_ip(screen.get_rect()); when he has no key and touchesroom.key, setroom.has_key = True; then open the door as above. - Draw the door with
screen.blit(door_img, room.door)while it is closed, the key withdraw(key_img, room.key)until it is picked up, andkey_imgat(136, 6)whileroom.has_keyis true.
Try it yourself
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 Room:
def __init__(self, lines):
self.walls = []
self.start = (72, 72)
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
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
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)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
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.walls)
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)
draw(knight.image, knight.rect)
pygame.display.flip()
clock.tick(60)
pygame.quit()
All lessons in Projects
Practice on your own: Pygame playground