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
Subscribe to the newsletter for updates
Tkinter templates
My youtube channel
Twitter: @pythonprogrammi - python_pygame