I have a very large tilemap of my game level which is a river. I want to create pixel art bridges that go across the river, but I have a reference map of the river that I need to use so the size and position is accurate. Is there a way i can download my tilemap as a PNG so I can open it in my art software? Thanks this will save me at least a few hours of counting tiles pixels.
You can write a tool script that iterates through the entire TileMap(Layer?) and saves it as a PNG. It’s also possible that there’s a plugin in the asset library that could do it as well, but I haven’t checked all of the entries there. Or you could zoom out really far in the editor and take a screenshot.
I read the documentation on tools that you attached, but I have no clue how to create a tool that will save the tile map as a PNG. I am trying with Claude but its not getting me very far. I read that tools are dangerous because they change things in editor and am a little bit worried about doing this without guidance. Is there a resourse you can point me towards or some advice so that I can impliment this properly.
I actually just went through this myself a couple of days ago while trying to make a map system for my game. With the help of some online references, I found a simple solution that lets you save your tilemap as an image.
To capture the tilemap image:
**- Create a ReferenceRect in the scene that contains your tilemap
Add the script below to it
Add your tilemap as a reference to the export variable “tilemap”
Click on “Setup Capture Area”. This automatically crops the reference rect to your tilemap
Then click capture. It saves the image of the whole tilemap inside the folder where the current scene is located.**
*You can also change the output resolution.
What this does is take multiple images of the tilemap as small chunks and stitch them together in one final image.
Hope it’s helpful.
@tool
extends ReferenceRect
@export_group(“Settings”)
@export var tilemap: TileMapLayer
@export var max_output_dimension: int = 1024
@export_group(“Controls”)
@export_tool_button(“1. Setup Capture Area”) var setup_button = setup_capture_area
@export_tool_button(“2. Capture TileMap”) var capture_button = capture_tilemap
var used_rect: Rect2i
var tile_size: Vector2i
func setup_capture_area():
if not tilemap:
printerr(“Capture Tool: Assign a TileMapLayer first!”)
return
used_rect = tilemap.get_used_rect()
tile_size = tilemap.tile_set.tile_size
global_position = tilemap.global_position + (Vector2(used_rect.position) * Vector2(tile_size))
size = Vector2(used_rect.size) * Vector2(tile_size)
func capture_tilemap():
if not tilemap or size.x <= 0: return
# 1. Calculate Scaling Logic
var current_max_dim = max(size.x, size.y)
var scale_factor: float = 1.0
if current_max_dim > max_output_dimension:
scale_factor = float(max_output_dimension) / current_max_dim
var final_width = int(size.x * scale_factor)
var final_height = int(size.y * scale_factor)
# 2. Automated Viewport Resolution
# We use the max_output_dimension as the buffer size, capped at 2048 for safety
var buffer_res = Vector2i(
min(final_width, 2048),
min(final_height, 2048)
)
var sub_viewport = SubViewport.new()
sub_viewport.size = buffer_res
sub_viewport.transparent_bg = true
sub_viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
EditorInterface.get_base_control().add_child(sub_viewport)
var camera = Camera2D.new()
sub_viewport.add_child(camera)
camera.anchor_mode = Camera2D.ANCHOR_MODE_FIXED_TOP_LEFT
camera.enabled = true
camera.zoom = Vector2(scale_factor, scale_factor)
camera.make_current()
sub_viewport.world_2d = get_viewport().world_2d
# 3. Scanning Logic
var final_image = Image.create(final_width, final_height, false, Image.FORMAT_RGBA8)
# World units covered per "stamp"
var world_step_x = buffer_res.x / scale_factor
var world_step_y = buffer_res.y / scale_factor
var steps_x = ceili(size.x / world_step_x)
var steps_y = ceili(size.y / world_step_y)
for y in range(steps_y):
for x in range(steps_x):
var world_x = global_position.x + (x * world_step_x)
var world_y = global_position.y + (y * world_step_y)
camera.global_position = Vector2(world_x, world_y)
await RenderingServer.frame_post_draw
var capture = sub_viewport.get_texture().get_image()
var dst_x = int(x * buffer_res.x)
var dst_y = int(y * buffer_res.y)
var blit_w = min(buffer_res.x, final_width - dst_x)
var blit_h = min(buffer_res.y, final_height - dst_y)
final_image.blit_rect(
capture,
Rect2(Vector2.ZERO, Vector2(blit_w, blit_h)),
Vector2(dst_x, dst_y)
)
# 4. Save Logic
var scene_path = get_tree().edited_scene_root.scene_file_path
var base_dir = scene_path.get_base_dir() if scene_path != "" else "res://"
var scene_name = scene_path.get_file().get_basename() if scene_path != "" else "map"
var final_path = base_dir + "/" + "tile_map_screenshot.png"
final_image.save_png(final_path)
# 5. Cleanup
sub_viewport.queue_free()
EditorInterface.get_resource_filesystem().scan()
print("Success! Captured %dx%d image to %s" % [final_width, final_height, final_path])
This didn’t work for me, after fixing the indentations and changing the curly quotes to regular ones, it threw the error: @export_tool_button is not allowed at this level. Thanks for trying to help.