Menu
CoddyTech

Walls Stop the Knight

Part of the Projects section of Coddy's Pygame journey. Lesson 24 of 33.

The knight walks with the arrow keys, 3 pixels a frame, and the walls stop him the way platforms did in Moss Caves: move along x, push out of any wall he overlaps, then the same along y. Doing one axis at a time tells you which side he hit.

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

Read the arrows each frame into dx and dy, as in Catch the Blocks, and pass the room's walls:

keys = pygame.key.get_pressed()
dx = 0
dy = 0
if keys[pygame.K_LEFT]:
    dx = -3
# Right, Up and Down the same way
knight.move(dx, dy, room.walls)
challenge icon

Challenge

Hard

Make the knight walk, stopped by the walls.

  1. Give Knight the move(dx, dy, blocks) method above.
  2. Each frame, set dx to -3 with Left or 3 with Right, and dy to -3 with Up or 3 with Down, 0 otherwise.
  3. Then call knight.move(dx, dy, room.walls).

Press Play and walk into a wall: the knight stops against it. Then Check.

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


room = Room(MAP)
knight = Knight(room.start)

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

    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