Menu
CoddyTech

The Knight Walks

Part of the Projects section of Coddy's Pygame journey. Lesson 22 of 31.

The knight starts on the P of the map and walks with the arrow keys, 3 pixels a frame.

His picture is a whole tile, 48 by 48, and so are the gaps between walls. A 48 pixel Rect would fit a gap only when lined up to the exact pixel. So the knight's Rect is smaller than his picture, 32 by 32, and the picture is drawn centred on it. The Rect is what collides; the picture is what you see.

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

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
    # then the same for dy, with bottom and top
challenge icon

Challenge

Hard
  1. Add the draw and cell_rect functions above.
  2. In Room, set self.start = (72, 72) before the loop, and for a P, self.start = cell_rect(col, row).center.
  3. Write class Knight(pygame.sprite.Sprite): its __init__(self, center) sets self.image = knight_img and a 32 by 32 self.rect centred on center. Give it the move(dx, dy, blocks) method above, with the y part.
  4. Make knight = Knight(room.start) after the room.
  5. Each frame, set dx to -3 with Left or 3 with Right, dy to -3 with Up or 3 with Down (0 otherwise), then call knight.move(dx, dy, room.walls).
  6. Draw the knight after the walls: draw(knight.image, knight.rect).

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")


class Room:
    def __init__(self, lines):
        self.walls = []
        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))


room = Room(MAP)

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)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Projects

Practice on your own: Pygame playground