Saving Tilesets

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

3 Likes