Saving Tilesets

I think a new file type would definitely work, maybe similar to the way we can make custom palettes? Btw, how did you get the dark UI?

I don’t agree about new file type.

First of all extrernal tilesets should have their own palette i.e. just to use external .ase sprite palette. This will be more than enough and generalized. And this way we can use RGB tiles inside Indexed sprite, which is Ok, until Tile map layer converted to just bitmap, where we should get its tiles converted too.

Second of all the frames of .ase file should be used as tiles indices to allow complex multi-layer tiles which flattened when loaded to be used as tiles. And you can hide/show layers in tiles sprite .ase to control what to see in Tile maps. Even tiles of tiles possible this way. But no direct editing of tiles inside where used for Tile map layer, which is preferred in this case. And with “Edit tiles…” to open source sprite of tiles. This should also auto create new frame in source sprite for new tiles of Manual tiling mode effectively inserting drawn content to first suitable visible layer (or creating new layer), and go to existing frame for Auto tiling mode (when modifcation of tile attempted by an artist).

And don’t afraid to use frame numbers as tiles indices. Because animation of tiles should generally use tiles themselves as frames i.e. tiles = frames here (except for NES-like mappers bank switching which is special case of several tilesets used to make tiles animated).

And third of all, information on how to align tiles from external .ase sprite frames i.e. (dx, dy, tile_width, tile_height) could be taken from its Grid (grid_cells_dx, grid_cells_dy, grid_cell_width, grid_cell_height). Even additional information like per-frame/tags user data (working as tile info) and non-rectangular base vectors (by adding support of non-rectangular grids with base vectors first) could be added and used for .ase tiles.

Tiles should be re-loaded and re-flattened when external .ase detected as changed. Also they should be baked into sprite as internal tileset until auto re-bake or explicit re-bake requested by an artist and/or when external .ase is not available.

i dont undestand how that would open?, like in pratice how do we use it simplyfy most of us arent familiar with such intricaces of aseprite

  1. Just specify for Tile map layer to use external .ase file as tileset
  2. Draw any content in that .ase file with frames = tiles concept in mind
  3. This content will automatically be flattened (to get rid of layers) to be used as tiles
  4. To edit external tiles you will have to edit that .ase file
  5. But Aseprite will auto-jump to new/old frame of that .ase file by opening that sprite in new tab at specific frame when you try to edit tiles at layer with external tileset

You may ask what if you want to see and edit all external tileset tiles at once — you should create separate working-on-tileset .ase with Tile map layer for this — and just switch between this working-on-tileset sprite (used to order tiles in different shapes) and tileset sprite itself (to edit tiles content).

so like a photoshop smart object? i think thats overcomplex. just saving a tileset in a specific archive type and importing in when creating a tileset layer would be way simpler in pratice and in programming i think
dunno @dacap what do you think which is easier to make

Simpler does not always mean better. Specific format will allow a lot less things to do. No easy Metatiles. No debug overlays. No tags-based debug info. No easy exporting. No easy layered tiles editing (very cool thing!). Extra efforts to code and support a whole new format.

While supporting .ase tilesets will require only a few thing to code: 1) new mode (external .ase tileset), 2) sync re-bake button and notification that sync required, 3) opening .ase tiles when an artist try to edit tile or create new tile. Almost nothing. The same internal tileset, but with function to re-bake tileset from source .ase file when it was changed and when you open sprite with such tileset used.

But surely at the end of the day, if you want to be that complex with it you could use a more complicated program like photoshop? Not sure if I’m alone here, but I’ve always perceived aseprite as a program that’s easy to use rather than being as in depth as industry-standard programs, so I personally prefer the simpler way of making a new file.

i agree, the thing i like in aseprite too, is that even the complex stuff can be easily done with a few examples like plugins, also its the way pyxel edit do it too, and he solenely focused on tiiles

But it’s simple. Draw tiles in separate .ase file. That’s all. Why be afraid of just using external .ase files?

thats clumsy i want to be able to save and open tilesets in the fly.

And you will be able to save and open tilesets in the fly. But why another format? Save to .ase (editor can generate new files), open from .ase (generated or create manually). Why do you need yet another format and extension for it? Also editor can create .ase tileset tileframes automatically and put content there, but only in case of simple one-layer .ase tiles.

This way or another you will have complications — when several your .ase projects using the same external tileset. Because when you modify this tileset (re-save it) it will affect all of your projects, even those that are not open now. And later when you open them you may find them broken. So another good option if you don’t want hard linking should be “Load tileset” instead of “Use external tileset”.

I wasn’t able to read the whole topic (this and Proposal — External tilesets using just .ase files), but about the file format for tilesets: we can easily use .aseprite format to store just tilesets, in the same way we use the .aseprite format to store only a palette (where a dummy a indexed canvas of 16 columns x required rows depending on the number of palette colors is automatically generated).

1 Like

finally a good sumary

1 Like

Good. The question is will you allow to edit these tilesets as regular sprites? WIll you allow to make multi-layer edits of these tiles and then still use them as tileset by flattening all visible layers?

Yes, we’ll allow to use flattened tilesets that are created from multi-layers. In that case the tileset will be “linked as an Import Sprite Sheet command” (or somthing similar). There are some steps:

  1. We have to improve the Import Sprite Sheet action to support more parameters (e.g. multiple grids? ignore empty cells? find tiles automatically?)
  2. Once the Import Sprite Sheet command is improved & we support linked .aseprite <-> files in some way (some work in progress), we’ll be able to specify a tileset from external files with Import Sprite Sheet parameters from the tileset dialog (this will enable a kind of metatiles).

As a final goal (maybe too ambitious, but I think it’s something required) would be to do an import/export/bi-directional tiles editing: So not only Import Sprite Sheet should work to import and external tileset, but we could edit pixels in the tileset and those changes should be reflected in the linked/external file (and those changes should be chained on other external tilesets).

A big problem about this: when multiple-layers are used for a tileset that is imported, how do we support editing those pixels? where are sent the changes to pixels? (a new layer? a target layer? should we show all tileset layers in the timeline in some way?)

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