#!python # -*- coding: utf-8 -*- u""" Make road tilemap. cursor key or WASD : scroll Windows10 x64 21H1 + Python 3.9.7 64bit + pygame 2.0.1 """ import json import pygame import sys import random SCRW, SCRH = 512, 512 class RoadRoller: HLINE = 1 VLINE = 2 def __init__(self, x, y, direc, tilemap): self.active = True self.x = x self.y = y self.oldx = x self.oldy = y self.direc = direc self.tilemap = tilemap self.map_w = len(tilemap[0]) self.map_h = len(tilemap) self.count = random.randint(4, 16) * 2 self.set_tile() def set_new_direction(self): self.count = random.randint(4, 16) * 2 if random.random() > 0.6: if random.random() < 0.5: self.direc = (self.direc + 1) % 4 else: self.direc = (self.direc - 1) % 4 def in_area(self): _x, _y = self.x, self.y if _x < 0 or _y < 0 or _x >= self.map_w or _y >= self.map_h: return False return True def get_tileid(self, x, y): if x < 0 or y < 0: return -1 if x >= self.map_w or y >= self.map_h: return -1 return self.tilemap[y][x] def move(self): self.oldx = self.x self.oldy = self.y if self.direc == 0: self.x += 1 elif self.direc == 1: self.y += 1 elif self.direc == 2: self.x -= 1 elif self.direc == 3: self.y -= 1 else: print("Error: direc=%d" % self.direc) def set_tile(self): x, y = self.x, self.y m = self.tilemap if self.direc == 0 or self.direc == 2: m[y][x] = RoadRoller.HLINE else: m[y][x] = RoadRoller.VLINE def update(self): if not self.active: return self.move() if not self.in_area(): self.active = False return if self.get_tileid(self.x, self.y) != 0: if random.random() > 0.85: self.active = False return if self.in_area(): self.set_tile() self.count -= 1 if self.count <= 0: self.set_new_direction() def create_objs(): objs = [] xi = random.randint(0, 8) * 2 while xi < map_w: objs.append(RoadRoller(xi, 0, 1, bgtilemap)) xi += random.randint(2, 8) * 2 xi = random.randint(0, 8) * 2 while xi < map_w: objs.append(RoadRoller(xi, map_h - 1, 3, bgtilemap)) xi += random.randint(2, 8) * 2 yi = random.randint(0, 8) * 2 while yi < map_h: objs.append(RoadRoller(0, yi, 0, bgtilemap)) yi += random.randint(2, 8) * 2 yi = random.randint(0, 8) * 2 while yi < map_h: objs.append(RoadRoller(map_w - 1, yi, 2, bgtilemap)) yi += random.randint(2, 8) * 2 return objs def clear_map(tilemap): for y in range(len(tilemap)): for x in range(len(tilemap[0])): tilemap[y][x] = 0 def get_block(m, mw, x, y, w, h): lst = [] for yi in range(h): for xi in range(w): lst.append(m[y + yi][x + xi]) return lst def put_block(m, mw, x, y, w, h, replst): i = 0 for yi in range(h): for xi in range(w): m[y + yi][x + xi] = replst[i] i += 1 def replace_map(m, mw, mh, repdata): w, h = 3, 3 for y in range(0, mh - (h - 1)): for x in range(0, mw - (w - 1)): cb = get_block(m, mw, x, y, w, h) for b in repdata: bo = b[0] br = b[1] if cb == bo: put_block(m, mw, x, y, w, h, br) # break def replace_map_slow(m, mw, mh, repdata): w, h = 3, 3 for b in repdata: bo = b[0] br = b[1] for y in range(0, mh - (h - 1)): for x in range(0, mw - (w - 1)): cb = get_block(m, mw, x, y, w, h) if cb == bo: put_block(m, mw, x, y, w, h, br) pygame.init() screen = pygame.display.set_mode((SCRW, SCRH), pygame.DOUBLEBUF) map_w = 64 map_h = 64 tile_w = 8 tile_h = 8 bgtilemap = [[0 for i in range(map_w)] for j in range(map_h)] clear_map(bgtilemap) # load pattern replace dictionary json dicjson = "rep_dic0.json" with open(dicjson, 'r') as f: rep_dic0 = json.load(f) # load bg chip image imgname = "road_chip.png" img = pygame.image.load(imgname).convert_alpha() # split bg chip image bgchips = [] w, h = tile_w, tile_h xc = img.get_width() // w yc = img.get_height() // h for yi in range(yc): for xi in range(xc): x, y = xi * w, yi * h bgchips.append(img.subsurface(pygame.Rect(x, y, w, h))) xofs, yofs = 0, 0 xofs_max = max(0, map_w - (SCRW // tile_w)) yofs_max = max(0, map_h - (SCRH // tile_h)) # create roadroller object objs = create_objs() running = True recreate = False not_fix = True clock = pygame.time.Clock() # Main loop while running: # update # check event for ev in pygame.event.get(): if ev.type == pygame.QUIT: running = False if ev.type == pygame.KEYDOWN: if ev.key == pygame.K_ESCAPE or ev.key == pygame.K_q: # Push ESC or Q key running = False if ev.key == pygame.K_SPACE: recreate = True if recreate: clear_map(bgtilemap) objs.clear() objs = create_objs() recreate = False not_fix = True for o in objs: o.update() _objs = [o for o in objs if o.active] objs = _objs if len(objs) == 0 and not_fix: # junction replace not_fix = False for i in range(5): replace_map(bgtilemap, map_w, map_h, rep_dic0) # scroll BG pygame.event.pump() pressed = pygame.key.get_pressed() if pressed[pygame.K_LEFT] or pressed[pygame.K_a]: xofs -= 1 if xofs < 0: xofs = 0 if pressed[pygame.K_RIGHT] or pressed[pygame.K_d]: xofs += 1 if xofs > xofs_max: xofs = xofs_max if pressed[pygame.K_UP] or pressed[pygame.K_w]: yofs -= 1 if yofs < 0: yofs = 0 if pressed[pygame.K_DOWN] or pressed[pygame.K_s]: yofs += 1 if yofs > yofs_max: yofs = yofs_max # draw start screen.fill((40, 60, 200)) # clear screen # draw tilemap BG xc = SCRW // tile_w yc = SCRH // tile_h for yi in range(yc): for xi in range(xc): xii = xi + xofs yii = yi + yofs if xii >= map_w or yii >= map_h: continue i = bgtilemap[yii][xii] x = xi * tile_w y = yi * tile_h screen.blit(bgchips[i], (x, y)) pygame.display.flip() clock.tick_busy_loop(60) cap = "Tilemap BG - %5.2f FPS , objs : %d" % (clock.get_fps(), len(objs)) pygame.display.set_caption(cap) pygame.quit() sys.exit()