Ninja-Jump-and-run/scripts/tilemap.py
Benjamin Hinz 4e8227aead 17.04.2026
2026-04-17 18:04:33 +02:00

115 lines
4.3 KiB
Python

import pygame as pg
import json
NEIGHBOR_OFFSETS = [(0, 0), (-1, 0), (-1, -1), (-1, 1), (0, 1), (0, -1), (1, 1), (1, 0), (1, -1)]
PHYSICS_TILES = {"grass", "stone"}
AUTOTILE_TYPES = {'grass', 'stone'}
AUTOTILE_MAP = {
tuple(sorted([(1, 0), (0, 1)])): 0,
tuple(sorted([(1, 0), (0, 1), (-1, 0)])): 1,
tuple(sorted([(-1, 0), (0, 1)])): 2,
tuple(sorted([(-1, 0), (0, -1), (0, 1)])): 3,
tuple(sorted([(-1, 0), (0, -1)])): 4,
tuple(sorted([(-1, 0), (0, -1), (1, 0)])): 5,
tuple(sorted([(1, 0), (0, -1)])): 6,
tuple(sorted([(1, 0), (0, -1), (0, 1)])): 7,
tuple(sorted([(1, 0), (-1, 0), (0, 1), (0, -1)])): 8,
}
class Tilemap:
def __init__(self, game, tile_size=16):
self.game = game
self.tile_size = tile_size
self.tilemap = {}
self.offgrid_tiles = []
for i in range(10):
self.tilemap[str(3 + i) + ';10'] = {'type': 'grass', 'variant': 1, 'pos': (3 + i, 10)}
self.tilemap['10;' + str(5 + i)] = {'type': 'stone', 'variant': 1, 'pos': (10, i + 5)}
def render(self, surface:pg.Surface, offset:tuple=(0,0)):
for tile in self.offgrid_tiles:
surface.blit(self.game.assets[tile['type']][tile['variant']], (tile['pos'][0] - offset[0], tile['pos'][1] - offset[1]))
for x in range(offset[0] // self.tile_size, (offset[0] + surface.get_width()) // self.tile_size + 1):
for y in range(offset[1] // self.tile_size, (offset[1] + surface.get_height()) // self.tile_size + 1):
location_key = f"{x};{y}"
if location_key in self.tilemap:
tile = self.tilemap[location_key]
surface.blit(self.game.assets[tile['type']][tile['variant']], (tile['pos'][0] * self.tile_size - offset[0],
tile['pos'][1] * self.tile_size - offset[1]))
def tiles_around(self, pos):
tiles = []
tile_location = (int(pos[0] // self.tile_size), int(pos[1] // self.tile_size))
for offset in NEIGHBOR_OFFSETS:
check_location = str(tile_location[0] + offset[0]) + ';' + str(tile_location[1] + offset[1])
if check_location in self.tilemap:
tiles.append(self.tilemap[check_location])
return tiles
def physics_rects_around(self, pos) -> list:
# Erzeuge eine leere Liste für die Rechtecke
rects = []
# Durchlaufe alle Tiles aus der Umgebung (tiles_around)
for tile in self.tiles_around(pos):
# Prüfe, ob der Tile-Typ in PHYSICS_TILES enthalten ist
# (nur diese Tiles sollen kollidieren)
if tile["type"] in PHYSICS_TILES:
# Rechne die Tile-Position in Pixel-Koordinaten um
x_pixel = tile['pos'][0] * self.tile_size
y_pixel = tile['pos'][1] * self.tile_size
# Erzeuge ein pygame.Rect mit:
# - x_pixel
# - y_pixel
# - Breite = self.tile_size
# - Höhe = self.tile_size
rect = pg.Rect(x_pixel, y_pixel, self.tile_size, self.tile_size)
# Füge das Rechteck der Liste rects hinzu
rects.append(rect)
# Gib die Liste der Rechtecke zurück
return rects
def autotile(self):
for loc in self.tilemap:
tile = self.tilemap[loc]
neighbors = set()
for shift in [(1, 0), (-1, 0), (0, -1), (0, 1)]:
check_loc = str(tile['pos'][0] + shift[0]) + ';' + str(tile['pos'][1] + shift[1])
if check_loc in self.tilemap:
if self.tilemap[check_loc]['type'] == tile['type']:
neighbors.add(shift)
neighbors = tuple(sorted(neighbors))
if (tile['type'] in AUTOTILE_TYPES) and (neighbors in AUTOTILE_MAP):
tile['variant'] = AUTOTILE_MAP[neighbors]
def save(self, path):
with open(path, "w") as f:
json.dump({'tilemap': self.tilemap, 'tile_size': self.tile_size, 'offgrid': self.offgrid_tiles}, f)
def load(self, path):
with open(path, "r") as f:
map_data = json.load(f)
self.tilemap = map_data['tilemap']
self.tile_size = map_data['tile_size']
self.offgrid_tiles = map_data['offgrid']