Saving Tilesets

Hmm. I think showing all tileset layers as sub-layers of Tilemap layer will be best here. But it should be optional and never spend any computation resources until expanded. And implementation will require a strict Import Sprite Sheet standard.

Also there will be problem with moved and removed tiles in external tileset and it should have some solution too. Like selecting tile or multiple tiles with (Tiles) Marquee tool and then executing “Remap Tiles To” command and pointing new base tile for remapped tiles.

Is there any way to export a tileset as a spritesheet PNG file?

Except manually placing every tile once on a canvas and exporting it instead.

i think currently no

Here a little script to export the current tileset as a png file:

2 Likes

Hi folks,

I wanted some functionality to arrange tile exports in columns so I modified @dacap’s gist above. Thought I’d share. I’ll leave the planning on what features the built-in implementation should have to others.

Dialog and test looks like this

Two sample results look like these:

Tilemap 1

3 column export beginning at index 1.

Tilemap 2

4 column export beginning at index 0.

(Notice the tiles are not properly ordered in the original.)

local defaults = {
    filePath = "",
    startIndex = 0,
    count = 256,
    columns = 8
}

local dlg = Dialog { title = "Export Tileset" }

dlg:file {
    id = "filePath",
    label = "Path:",
    save = true,
    focus = false,
    filename = defaults.filePath
}

dlg:newrow { always = false }

dlg:slider {
    id = "startIndex",
    label = "Start:",
    min = 0,
    max = 255,
    value = defaults.startIndex
}

dlg:newrow { always = false }

dlg:slider {
    id = "count",
    label = "Count:",
    min = 1,
    max = 256,
    value = defaults.count
}

dlg:newrow { always = false }

dlg:slider {
    id = "columns",
    label = "Columns:",
    min = 1,
    max = 32,
    value = defaults.columns
}

dlg:newrow { always = false }

dlg:button {
    id = "confirm",
    text = "&OK",
    focus = false,
    onclick = function()
        -- Original export script:
        -- https://gist.github.com/dacap/9df368ba276a45048dc85f96b23ea818

        -- Discussion on tileset export functionality:
        -- https://community.aseprite.org/t/saving-tilesets/9398/22

        -- For determining undocumented object methods:
        -- https://stackoverflow.com/questions/2620377/lua-reflection-get-list-of-functions-fields-on-an-object

        local version = app.version
        if version.major >= 1 and version.minor >= 3 and TilesetMode then
            local sprite = app.activeSprite
            if sprite then
                local layer = app.activeLayer
                if layer then
                    local isTileMap = layer.isTilemap
                    if isTileMap then
                        local tileSet = layer.tileset
                        -- for key, value in pairs(getmetatable(tileSet)) do
                        --     print(key, value)
                        -- end

                        local tileCount = #tileSet
                        local grid = tileSet.grid

                        local tileDim = grid.tileSize
                        local tileWidth = tileDim.width
                        local tileHeight = tileDim.height

                        local args = dlg.data
                        local filePath = args.filePath or defaults.filePath
                        local startIndex = args.startIndex or defaults.startIndex
                        local count = args.count or defaults.count
                        local columns = args.columns or defaults.columns

                        local si = math.min(tileCount - 1, math.max(0, startIndex))
                        local vc = math.min(tileCount - si, math.max(1, count))

                        local imgWidth = tileWidth * columns
                        local imgHeight = tileHeight * math.ceil(vc / columns)

                        -- Original script either copies or acquires a reference
                        -- to the image specification. Not sure if that makes a
                        -- difference to new image; ImageSpec also tracks the
                        -- transparentColor.
                        -- https://github.com/aseprite/api/blob/main/api/imagespec.md#imagespec
                        local image = Image(imgWidth, imgHeight, sprite.colorMode)

                        for i = 0, vc - 1, 1 do
                            local x = i % columns
                            local y = i // columns
                            local xScaled = x * tileWidth
                            local yScaled = y * tileHeight
                            local tile = tileSet:getTile(si + i)
                            image:drawImage(tile, xScaled, yScaled)
                        end

                        if #filePath > 0 then
                            image:saveAs(filePath)
                            dlg:close()
                        else
                            app.alert("Invalid file path (empty String).")
                        end
                    else
                        app.alert("The active layer is not a tilemap.")
                    end
                else
                    app.alert("There is no active layer.")
                end
            else
                app.alert("There is no active sprite.")
            end
        else
            app.alert("Version 1.3 or later is required to use tilemaps.")
        end
    end
}

dlg:button {
    id = "cancel",
    text = "&CANCEL",
    onclick = function()
        dlg:close()
    end
}

dlg:show { wait = false }

Probably there are edge cases that will catch this out.

Thanks! Best,
Jeremy

1 Like