Tiled Map Setup: Ground tiles, ruins & automated asset processing
Features: - Resized 4513 PNG assets to 40% for optimal Tiled performance - Created comprehensive tileset library (grass, dirt, trees, flowers, ruins, walls) - Generated 3 test maps: travnik_32x32, zapuscena_vas_48x48, travnik_s_objekti - Added 9 different ruined building tilesets for TownRestorationSystem integration Tools Added: - resize_assets_for_tiled.py: Batch resize all assets to 40% - generate_tiled_map.py: Auto-generate maps with placed objects - fix_tiled_map.py: Create proper tile-based maps Structure: - Slike_za_Tiled/: 4513 resized assets ready for Tiled - assets/tilesets/: 16 tileset definitions (.tsx files) - assets/maps/: 3 ready-to-use Tiled maps (.tmx files) Documentation: - docs/TILED_SETUP_GUIDE.md: Complete setup and usage guide Ready for map design in Tiled Map Editor!
This commit is contained in:
197
tools/generate_tiled_map.py
Normal file
197
tools/generate_tiled_map.py
Normal file
@@ -0,0 +1,197 @@
|
||||
"""
|
||||
Generate Tiled map with Collection of Images tileset
|
||||
Automatically places dirt, trees, and Kai character
|
||||
"""
|
||||
import os
|
||||
import random
|
||||
import xml.etree.ElementTree as ET
|
||||
from xml.dom import minidom
|
||||
|
||||
# Configurations
|
||||
SLIKE_DIR = r"c:\novafarma\Slike_za_Tiled"
|
||||
OUTPUT_MAP = r"c:\novafarma\assets\maps\auto_generated_map.tmx"
|
||||
MAP_WIDTH = 32
|
||||
MAP_HEIGHT = 32
|
||||
TILE_WIDTH = 48
|
||||
TILE_HEIGHT = 48
|
||||
|
||||
def find_assets():
|
||||
"""Find required assets in Slike_za_Tiled folder"""
|
||||
assets = {
|
||||
'dirt': [],
|
||||
'trees': [],
|
||||
'kai': None
|
||||
}
|
||||
|
||||
if not os.path.exists(SLIKE_DIR):
|
||||
print(f"❌ Error: {SLIKE_DIR} does not exist!")
|
||||
print(" Run resize_assets_for_tiled.py first!")
|
||||
return None
|
||||
|
||||
for filename in os.listdir(SLIKE_DIR):
|
||||
if not filename.endswith('.png'):
|
||||
continue
|
||||
|
||||
lower_name = filename.lower()
|
||||
filepath = os.path.join(SLIKE_DIR, filename)
|
||||
|
||||
# Find dirt/soil tiles
|
||||
if 'dirt' in lower_name or 'soil' in lower_name or 'ground' in lower_name:
|
||||
assets['dirt'].append(filepath)
|
||||
|
||||
# Find trees
|
||||
if 'tree' in lower_name:
|
||||
assets['trees'].append(filepath)
|
||||
|
||||
# Find Kai character
|
||||
if 'kai' in lower_name or 'character' in lower_name or 'player' in lower_name:
|
||||
if assets['kai'] is None: # Take first match
|
||||
assets['kai'] = filepath
|
||||
|
||||
print(f"🔍 Found assets:")
|
||||
print(f" - Dirt/Soil tiles: {len(assets['dirt'])}")
|
||||
print(f" - Trees: {len(assets['trees'])}")
|
||||
print(f" - Kai character: {'✓' if assets['kai'] else '✗'}")
|
||||
|
||||
return assets
|
||||
|
||||
def create_tiled_map():
|
||||
"""Create Tiled map with auto-placed objects"""
|
||||
|
||||
# Find assets
|
||||
assets = find_assets()
|
||||
if not assets:
|
||||
return
|
||||
|
||||
# Create map root
|
||||
map_elem = ET.Element('map', {
|
||||
'version': '1.10',
|
||||
'tiledversion': '1.11.1',
|
||||
'orientation': 'orthogonal',
|
||||
'renderorder': 'right-down',
|
||||
'width': str(MAP_WIDTH),
|
||||
'height': str(MAP_HEIGHT),
|
||||
'tilewidth': str(TILE_WIDTH),
|
||||
'tileheight': str(TILE_HEIGHT),
|
||||
'infinite': '0',
|
||||
'nextlayerid': '3',
|
||||
'nextobjectid': '20'
|
||||
})
|
||||
|
||||
# Create object layer
|
||||
objectgroup = ET.SubElement(map_elem, 'objectgroup', {
|
||||
'id': '2',
|
||||
'name': 'Objects'
|
||||
})
|
||||
|
||||
obj_id = 1
|
||||
|
||||
# Place 8 dirt tiles in center (2x4 grid)
|
||||
print("\n🌍 Placing 8 dirt tiles in center...")
|
||||
center_x = MAP_WIDTH // 2 - 1
|
||||
center_y = MAP_HEIGHT // 2 - 2
|
||||
|
||||
for i in range(8):
|
||||
if assets['dirt']:
|
||||
dirt_asset = random.choice(assets['dirt'])
|
||||
x = (center_x + (i % 2)) * TILE_WIDTH
|
||||
y = (center_y + (i // 2)) * TILE_HEIGHT
|
||||
|
||||
obj = ET.SubElement(objectgroup, 'object', {
|
||||
'id': str(obj_id),
|
||||
'name': f'dirt_{i+1}',
|
||||
'x': str(x),
|
||||
'y': str(y),
|
||||
'width': str(TILE_WIDTH),
|
||||
'height': str(TILE_HEIGHT)
|
||||
})
|
||||
ET.SubElement(obj, 'image', {
|
||||
'source': os.path.relpath(dirt_asset, os.path.dirname(OUTPUT_MAP))
|
||||
})
|
||||
obj_id += 1
|
||||
|
||||
# Place 10 trees randomly around the dirt area
|
||||
print("🌲 Placing 10 trees randomly...")
|
||||
tree_positions = []
|
||||
attempts = 0
|
||||
max_attempts = 100
|
||||
|
||||
while len(tree_positions) < 10 and attempts < max_attempts:
|
||||
attempts += 1
|
||||
tree_x = random.randint(2, MAP_WIDTH - 3) * TILE_WIDTH
|
||||
tree_y = random.randint(2, MAP_HEIGHT - 3) * TILE_HEIGHT
|
||||
|
||||
# Avoid center dirt area
|
||||
if (center_x * TILE_WIDTH <= tree_x <= (center_x + 2) * TILE_WIDTH and
|
||||
center_y * TILE_HEIGHT <= tree_y <= (center_y + 4) * TILE_HEIGHT):
|
||||
continue
|
||||
|
||||
# Avoid placing trees too close together
|
||||
too_close = False
|
||||
for px, py in tree_positions:
|
||||
if abs(tree_x - px) < TILE_WIDTH * 3 and abs(tree_y - py) < TILE_HEIGHT * 3:
|
||||
too_close = True
|
||||
break
|
||||
|
||||
if too_close:
|
||||
continue
|
||||
|
||||
if assets['trees']:
|
||||
tree_asset = random.choice(assets['trees'])
|
||||
tree_positions.append((tree_x, tree_y))
|
||||
|
||||
obj = ET.SubElement(objectgroup, 'object', {
|
||||
'id': str(obj_id),
|
||||
'name': f'tree_{len(tree_positions)}',
|
||||
'x': str(tree_x),
|
||||
'y': str(tree_y),
|
||||
'width': str(TILE_WIDTH * 2),
|
||||
'height': str(TILE_HEIGHT * 2)
|
||||
})
|
||||
ET.SubElement(obj, 'image', {
|
||||
'source': os.path.relpath(tree_asset, os.path.dirname(OUTPUT_MAP))
|
||||
})
|
||||
obj_id += 1
|
||||
|
||||
# Place Kai character near center
|
||||
if assets['kai']:
|
||||
print("🧑 Placing Kai character...")
|
||||
kai_x = (center_x + 2) * TILE_WIDTH + 60
|
||||
kai_y = center_y * TILE_HEIGHT + 80
|
||||
|
||||
obj = ET.SubElement(objectgroup, 'object', {
|
||||
'id': str(obj_id),
|
||||
'name': 'kai_player',
|
||||
'x': str(kai_x),
|
||||
'y': str(kai_y),
|
||||
'width': str(TILE_WIDTH),
|
||||
'height': str(TILE_HEIGHT)
|
||||
})
|
||||
ET.SubElement(obj, 'image', {
|
||||
'source': os.path.relpath(assets['kai'], os.path.dirname(OUTPUT_MAP))
|
||||
})
|
||||
|
||||
# Write XML to file
|
||||
os.makedirs(os.path.dirname(OUTPUT_MAP), exist_ok=True)
|
||||
|
||||
# Pretty print XML
|
||||
xml_str = ET.tostring(map_elem, encoding='unicode')
|
||||
dom = minidom.parseString(xml_str)
|
||||
pretty_xml = dom.toprettyxml(indent=' ', encoding='UTF-8')
|
||||
|
||||
with open(OUTPUT_MAP, 'wb') as f:
|
||||
f.write(pretty_xml)
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"✨ TILED MAP CREATED!")
|
||||
print(f"{'='*60}")
|
||||
print(f"📁 Location: {OUTPUT_MAP}")
|
||||
print(f"📏 Size: {MAP_WIDTH}x{MAP_HEIGHT} tiles")
|
||||
print(f"🎮 Objects placed:")
|
||||
print(f" - 8 dirt tiles (center)")
|
||||
print(f" - {len(tree_positions)} trees (random)")
|
||||
print(f" - 1 Kai character")
|
||||
print(f"\n🚀 Open in Tiled Map Editor to continue editing!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_tiled_map()
|
||||
Reference in New Issue
Block a user