Py-Map-Builder 1.6 – new features.. coming soon

New features in Py-Map-Builder

This are the adds to the code to create maps.

Otherwise I solved the problems with names of the layers from 2 to 1 to 0 to 00, now they are called 1, 2, 3, 4, so there is not too much overthinking about their order of appearance. In the examples below there are still the old names though.

1. Fixed issue with deleting layers

When you deleted a layer with keys 1 2 3 4, it gave you an error, fixed with this function called when clicking the keys.

In the while loop

            if event.key == K_1:
                map2 = map_to_list()
            if event.key == K_2:
                map1 = map_to_list()
            if event.key == K_3:
                map0 = map_to_list()
            if event.key == K_4:
                map00 = map_to_list()

The function called

def map_to_list():
    "Creates a list of 16 lists with 29 empty spaces as items"
    map1 = [
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    ]
    return map1

2. Added feature to copy a tile

When you press c, it copies the tiles you are on, so you do not need to use the mouse wheel to reach the tile. Very handy.

Added this to the while loop for the user interaction.

            if event.key == K_c:
                if layer == "2":
                    cnt2 = copy_block(x, y, map2)
                if layer == "1":
                    cnt1 = copy_block(x, y, map1)
                if layer == "0":
                    cnt0 = copy_block(x, y, map0)
                if layer == "00":
                    cnt00 = copy_block(x, y, map00)

The key c triggers the new function copy_block:

def copy_block(x, y, mp):
    x, y = get_x_y()
    # gets the number in the layer representing that tile
    copied_tile_id = mp[y][x]
    # return
    return copied_tile_id

A very big window

Finally we can have a much bigger screen, being the tiles just 16×16 pixel, it is nice to have a screen that is bigger.

I got a main surface that is 3 times the display surface…

pygame.init()
w, h = WSIZE = ((464 * 3, 256 * 3))
screen = pygame.display.set_mode((w, h))
display = pygame.Surface((464, 256))

Everything is blitted on the display surface first, like in this function that shows the tiles of the map

def show_map(mp1):
    "Take the map list with letters and blit them as tiles on the display surface"
    for y, line in enumerate(mp1):
        # for each carachter 
        for x, c in enumerate(line):
            for n in letters:
                if c != 0:
                    display.blit(tile[c], (x * 16, y * 16))

 

 

The code of the map editor

You can find all the code and assets in https://github.com/formazione/map_editor

from pygame.locals import *
import pygame
import os
from glob import glob
import pickle
from time import time
from tkinter import filedialog


def load_images(folder):
    "creates 4 list of images/surfaces for the 4 layers"
    listtiles2 = [x for x in glob(folder + "2\\*.png")]
    listtiles1 = [x for x in glob(folder + "1\\*.png")]
    listtiles0 = [x for x in glob(folder + "0\\*.png")]
    listtiles00 = [x for x in glob(folder + "00\\*.png")]
    tile20 = [pygame.image.load(x) for x in listtiles2]
    tile1 = [pygame.image.load(x) for x in listtiles1]
    tile0 = [pygame.image.load(x) for x in listtiles0]
    tile00 = [pygame.image.load(x) for x in listtiles00]
    return tile20, tile1, tile0, tile00


def init_display():
    "Initializing pygame, fonts... display and screen"
    global screen, tile20, tile1, tile0, tile00, display
    global WINDOW_SIZE, cnt, clock, text
    global font

    pygame.init()
    font = pygame.font.SysFont("Arial", 12)
    WINDOW_SIZE = (464 * 3, 256 * 3)
    screen = pygame.display.set_mode(WINDOW_SIZE, 0, 32)
    display = pygame.Surface((464, 256))
    tile20, tile1, tile0, tile00 = load_images("imgs")
    text = font.render("Pygame MAP EDITOR", 0, (222, 255, 0))
    clock = pygame.time.Clock()


cnt1 = 0
cnt2 = 0
cnt0 = 0
cnt00 = 0

def blit_tiles(mp1):
    "Display all the bricks, called by while loop"

    global tile, letters

    associations = [
        [map2, tile20, letters2],
        [map1, tile1, letters1],
        [map0, tile0, letters0],
        [map00, tile00, letters00]]

    for assoc in associations:
        if mp1 == assoc[0]:
            tile, letters = assoc[1:]


    for y, line in enumerate(mp1):
        # for each carachter 
        for x, c in enumerate(line):
            for n in letters:
                if c != 0:
                    display.blit(tile[c], (x * 16, y * 16))


def map_to_list():
    "Creates a list of 16 lists with 29 empty spaces as items"
    map1 = [
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    ]
    return map1


#map0[0][0] = "w"

pygame.init()
init_display()
pos = x, y = pygame.mouse.get_pos()
# letter = "spazio"
num_file = len(os.listdir())
map_name = f"map{num_file}.png"



# This places a brick or a tile when hit button left
# deletes bricks/blit_tiles when hit button right
def place_block(actual_tile, x, y, mp):
    x, y = get_x_y()
    mp[y][x] =  actual_tile

def copy_block(x, y, mp):
    x, y = get_x_y()
    # gets the number in the layer representing that tile
    copied_tile_id = mp[y][x]
    # return
    return copied_tile_id


def get_x_y():
    x, y = pygame.mouse.get_pos()
    pos = x, y = x // 48, y // 48
    return pos


def save_map():
    with open("map2.pkl", "wb") as file:
        pickle.dump(map2, file)
    with open("map1.pkl", "wb") as file:
        pickle.dump(map1, file)
    with open("map0.pkl", "wb") as file:
        pickle.dump(map0, file)
    with open("map00.pkl", "wb") as file:
        pickle.dump(map00, file)


def store_maps():
    tm = time()
    with open(f"pkl\\map2.pkl", "wb") as file:
        pickle.dump(map2, file)
    with open(f"pkl\\map1.pkl", "wb") as file:
        pickle.dump(map1, file)
    with open(f"pkl\\map0.pkl", "wb") as file:
        pickle.dump(map0, file)
    with open(f"pk\\map00.pkl", "wb") as file:
        pickle.dump(map00, file)


def load_map(filename, mp1):
    if filename in os.listdir():
        with open(filename, "rb") as file:
            mp = pickle.load(file)
    else:
        mp = mp1
    return mp


# Letter used for the blit_tiles, automatically chosen depending on the images in folder imgs
# just add images in the folder imgs and the code will assign a letter

letters2 = [x for x in range(len(tile20))]
letters1 = [x for x in range(len(tile1))]
letters0 = [x for x in range(len(tile0))]
letters00 = [x for x in range(len(tile00))]
# Create the map

map2 = map_to_list()
map1 = map_to_list()
map0 = map_to_list()
map00 = map_to_list()

map00 = load_map("map00.pkl", map00)
map0 = load_map("map0.pkl", map0)
map1 = load_map("map1.pkl", map1)
map2 = load_map("map2.pkl", map2)

print(map2)

layer = "2"


def show_tiles_num(cnt, x, y):
    xnum = font.render(str(layer) + ") " + str(cnt), 1, pygame.Color("White"))
    display.blit(xnum, (x-30, y))


def pointer_tiles():
    # =========== SHOW TILE NEXT THE POINTER ===========
    x, y = pygame.mouse.get_pos()
    x -= 14
    y -= 14
    x, y = x // 3, y // 3
    if layer == "2":
        show_tiles_num(cnt2, x, y)
        display.blit(tile20[cnt2], (x, y))
    elif layer == "1":
        show_tiles_num(cnt1, x, y)
        display.blit(tile1[cnt1], (x, y))
    elif layer == "0":
        show_tiles_num(cnt0, x, y)
        display.blit(tile0[cnt0], (x, y))
    elif layer == "00":
        show_tiles_num(cnt00, x, y)
        display.blit(tile00[cnt00], (x, y))
    # ==================================================

def tilesrollup(cnt, letters):
    cnt += 1
    if cnt > len(letters) - 1:
        cnt = 0
    return cnt


def tilesrolldown(cnt, letters):
    cnt -= 1
    if cnt < 0:
        cnt = len(letters) - 1
    return cnt


show_layer00 = 1
show_layer0 = 1
show_layer1 = 1
show_layer2 = 1


loop = 1
while loop:
    # clear and  
    display.fill((25, 75, 150))
    if show_layer00:
        blit_tiles(map00)
    if show_layer0:
        blit_tiles(map0)
    if show_layer1:
        blit_tiles(map1)
    if show_layer2:
        blit_tiles(map2)
    display.blit(text, (0, 0))
    
    for event in pygame.event.get():
        if event.type == QUIT:
            save_map()
            loop = 0
        if event.type == pygame.KEYDOWN:
            if event.key == K_ESCAPE:
                save_map()
                loop = 0
            # Save screen with 's'
            if event.key == K_s:
                pygame.image.save(screen, map_name)
                os.startfile(map_name)
            
            if event.key == K_c:
                if layer == "2":
                    cnt2 = copy_block(x, y, map2)
                if layer == "1":
                    cnt1 = copy_block(x, y, map1)
                if layer == "0":
                    cnt0 = copy_block(x, y, map0)
                if layer == "00":
                    cnt00 = copy_block(x, y, map00)

            
            # toggle layers ============================ 1.5

            if event.key == K_l:
                show_layer2 = False if show_layer2 else True
            if event.key == K_k:
                show_layer1 = False if show_layer1 else True
            if event.key == K_j:
                show_layer0 = False if show_layer0 else True
            if event.key == K_h:
                show_layer00 = False if show_layer00 else True

            # Shows mouse position =======================
            if event.key == K_m:
                x, y = pygame.mouse.get_pos()
                text = font.render(f"{x},{y}", 1, (244, 0, 0))


            if event.key == K_1:
                map2 = map_to_list()
            if event.key == K_2:
                map1 = map_to_list()
            if event.key == K_3:
                map0 = map_to_list()
            if event.key == K_4:
                map00 = map_to_list()


            ################### CHANGE LAYER ########################
            #                       p, o, i, u shows the 4 tiles and set the layer

            if event.key == K_p:
                layer = "2"
                text = font.render("Layer 1 ............. 1p 2o 3i 4u", 1, (222, 0, 0))


            elif event.key == K_o:
                layer = "1"
                text = font.render("Layer 2 ............... 1p 2o 3i 4u", 1, (222, 222, 0))
            elif event.key == K_i:
                layer = "0"
                text = font.render("Layer 3 ............... 1p 2o 3i 4u", 1, (0, 222, 222))
            elif event.key == K_u:
                layer = "00"
                text = font.render("Layer 4 ................. 1p 2o 3i 4u", 1, (0, 222, 222))


        # Place block with left mouse button
        if pygame.mouse.get_pressed()[0]:
            if layer == "2":
                place_block(letters2[cnt2], x, y, map2)
            elif layer == "1":
                place_block(letters1[cnt1], x, y, map1)
            elif layer == "0":
                place_block(letters0[cnt0], x, y, map0)
            elif layer == "00":
                place_block(letters00[cnt00], x, y, map00)

        # Place empty space with right mouse button
        elif pygame.mouse.get_pressed()[2]:
                    if layer == "2":
                        place_block(0, x, y, map2)
                    elif layer == "1":
                        place_block(0, x, y, map1)
                    elif layer == "0":
                        place_block(0, x, y, map0)
                    elif layer == "00":
                        place_block(0, x, y, map00)
        
        # Choose tiles scrolling the middle mouse wheel
        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 4:
                if layer == "2":
                    cnt2 = tilesrollup(cnt2, letters2)
                if layer == "1":
                    cnt1 = tilesrollup(cnt1, letters1)
                if layer == "0":
                    cnt0 = tilesrollup(cnt0, letters0)
                if layer == "00":
                    cnt00 = tilesrollup(cnt00, letters00)
            if event.button == 5:
                if layer == "2":
                    cnt2 = tilesrolldown(cnt2, letters2)

                if layer == "1":    
                    cnt1 = tilesrolldown(cnt1, letters1)

                if layer == "0":
                    cnt0 = tilesrolldown(cnt0, letters0)
                if layer == "00":
                    cnt00 = tilesrolldown(cnt00, letters00)


    pointer_tiles()
    screen.blit(pygame.transform.scale(display, WINDOW_SIZE), (0, 0))
    pygame.display.update()
    clock.tick(30)

pygame.quit()

The code of the “game” that uses the map (layer 1 only)

import pygame
from glob import glob
import sys
from pygame.locals import *
import os
import pickle


pygame.init()
w, h = WSIZE = ((464 * 3, 256 * 3))
screen = pygame.display.set_mode((w, h))
display = pygame.Surface((464, 256))

def load_tiles(folder: str) -> list:
    "Load tiles from a folder... with a number at the end"
    listtiles2 = [x for x in glob(folder + "\\*.png")]
    tile2 = [pygame.image.load(x) for x in listtiles2]
    return tile2


def load_map(filename: str, mp1: list):
    "Resume a list with the data (letter) for the tiles to be displayed on display surface"
    if filename in os.listdir():
        with open(filename, "rb") as file:
            mp = pickle.load(file)
    else:
        mp = mp1
    return mp


def show_map(mp1):
    "Take the map list with letters and blit them as tiles on the display surface"
    for y, line in enumerate(mp1):
        # for each carachter 
        for x, c in enumerate(line):
            for n in letters:
                if c != 0:
                    display.blit(tile[c], (x * 16, y * 16))


class Sprite(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super(Sprite, self).__init__()
        self.x = x
        self.y = y
        self.dogwalking = glob("imgs/walk/*.png")
        self.dogidling = glob("imgs/idle/*.png")
        self.load_images()

    def load(self, x):
        return pygame.image.load(x).convert_alpha()

    def flip(self, x):
        return pygame.transform.flip(self.load(x), 1, 0)

    def load_images(self):
        self.list = [self.load(f) for f in self.dogwalking]
        self.listflip = [self.flip(f) for f in self.dogwalking]
        self.list_idle = [self.load(f) for f in self.dogidling]
        self.list_idleflip = [self.flip(f) for f in self.dogidling]
        self.counter = 0
        self.image = self.list[0]
        self.rect = pygame.Rect(self.x, self.y, 16, 16)
        self.dir = ""
        self.prov = ""
        g.add(self)

    def update_counter(self, vel, img_list):
        self.counter += vel
        if self.counter >= len(img_list):
            self.counter = 0
        self.image = img_list[int(self.counter)]

    def update(self):
        global moveUp, moveLeft, moveRight, moveLeft, faceRight

        if moveRight:
            self.update_counter(.1, self.list)
            self.prov = self.dir

        if moveLeft:
            self.update_counter(.1, self.listflip)
            # self.image = self.listflip[int(self.counter)]
            self.prov = self.dir

        if self.dir == "":
            self.update_counter(.05, self.list_idle)

            if faceRight:
                self.image = self.list_idleflip[int(self.counter)]

            else:
                self.image = self.list_idle[int(self.counter)]




tile = load_tiles("imgs2")
alphab = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm.,:;@#°^[]<>()&%$£€ABC1234567890òàèéù+-ì={}§!?/|"
letters = [x for x in alphab[0:len(tile)]]
map2 = []
map2 = load_map("map2.pkl", map2)


g = pygame.sprite.Group()
# ================================= Sprite Player ====
player = Sprite(50, 128)
clock = pygame.time.Clock()

moveLeft = False
moveRight = False
moveUp = False
moveDown = False
faceRight = False
MOVESPEED = 1


while True:
# Check for events.
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN:
            # Change the keyboard variables.
            if event.key == K_LEFT or event.key == K_a:
                moveRight = False
                moveLeft = True
                faceRight = True
            if event.key == K_RIGHT or event.key == K_d:
                moveLeft = False
                moveRight = True
                faceRight = False
                player.image = player.list[int(player.counter)]
            if event.key == K_UP or event.key == K_w:
                moveDown = False
                moveUp = True
            if event.key == K_DOWN or event.key == K_s:
                moveUp = False
                moveDown = True

        # KEYUP

        if event.type == KEYUP:
            player.counter = 0
            if event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
            if event.key == K_LEFT or event.key == K_a:
                moveLeft = False
            if event.key == K_RIGHT or event.key == K_d:
                moveRight = False
            if event.key == K_UP or event.key == K_w:
                moveUp = False
            if event.key == K_DOWN or event.key == K_s:
                moveDown = False

# Draw the white background onto the surface.
    display.fill((50, 75, 100))


    show_map(map2)
    # Move the player.
    if moveDown and player.rect.bottom < h:
        player.rect.top += MOVESPEED
    if moveUp and player.rect.top > 0:
        player.rect.top -= MOVESPEED
    if moveLeft and player.rect.left > -35:
        player.rect.left -= MOVESPEED
        # try:
        #     player.counter += .1
        player.image = player.listflip[int(player.counter)]
        # except:
        #     player.counter = 0
            # player.image = player.listflip[int(player.counter)]
    if moveRight and player.rect.right < w + 35:
        player.rect.right += MOVESPEED
        # try:
            # player.counter -= .1
        player.image = player.list[int(player.counter)]
        # except:
        #     player.counter = 0
        #     player.image = player.list[int(player.counter)]

    # Draw the player onto the surface.
    g.draw(display)
    g.update()
    screen.blit(pygame.transform.scale(display, (w, h)), (0,0))
    # Draw the window onto the screen
    pygame.display.update()
    clock.tick(60)

pygame.quit()

GH repository https://github.com/formazione/map_editor

Previous post


Subscribe to the newsletter for updates
Tkinter templates
Avatar My youtube channel

Twitter: @pythonprogrammi - python_pygame

Videos

Speech recognition game

Pygame's Platform Game

Other Pygame's posts

Published by pythonprogramming

Started with basic on the spectrum, loved javascript in the 90ies and python in the 2000, now I am back with python, still making some javascript stuff when needed.