Skip to content

Tilemaps and Aseprite

Aseprite files, their use in a running game, and TileMap nodes.

Files: aseprite.odin, aseprite_nodes.odin, tilemap.odin, inflate.odin.

Aseprite files (.aseprite, .ase; https://www.aseprite.org, the pixel art editor), read with the published format (Aseprite’s docs/ase-file-specs.md), so sprites, animations and tilemaps drawn in Aseprite play in the engine.

Aseprite_Load / Aseprite_Parse read a file into plain data (Aseprite):

  • The sprite: size, color mode (RGBA, grayscale or indexed: every image is turned into RGBA pixels here, through the palette for indexed ones), its grid and its user data.
  • Layers in Aseprite’s order, bottom first: image, group and tilemap layers, with their parent group (parent), visibility, opacity, blend mode and user data.
  • Frames with their duration (milliseconds; each frame has its own) and cels: a layer’s image in that frame (pixels, RGBA, at x, y), or a tilemap layer’s tiles. Linked cels are copies of the cel they link to.
  • Tags (named frame ranges: Aseprite’s animations), with direction, repeats and color; slices with their keys (9-patch centers, pivots); tilesets (tile size, the tiles’ pixels as one column of tiles, tile 0 the empty one, and each tile’s user data).
  • User data: text, a color and properties (AsepriteValue) of the sprite, layers, cels, tags, slices, tilesets and tiles. Only the user’s own properties are kept (not extensions’).

Aseprite_FramePixels composites a frame exactly as Aseprite exports it (checked byte for byte against Aseprite 1.3.17’s own exports: aseprite_matches_aseprite): Aseprite’s render plan (layers numbered in tree order, groups flattened unless the file says to composite them, sorted by number plus each cel’s z-index), hidden layers and those in hidden groups left out, reference layers too, cel opacity times layer opacity, Aseprite’s 19 blend modes with its integer arithmetic and its default “new blending” (blend_funcs.cpp, ported), tilemap layers drawn from their tileset with each tile’s flips. Aseprite 1.3.17 never writes the compose-groups flag (its compose-groups preference only changes its own canvas), so files are flattened as its exports are; the composited path, for files that do set it, couldn’t be checked against Aseprite. Aseprite_SheetPixels lays every frame out on a grid (an animation’s sheet). Textures from Aseprite files use nearest filtering: they’re pixel art.

Everything a file holds lives in its own arena: Aseprite_Destroy frees it all. Reading never trusts the file: anything truncated or malformed is an error, not a crash.

Aseprite files in a running game, beyond their pixels (aseprite.odin reads them):

  • Slices (Aseprite_SliceAt: a slice’s key for a frame):
    • a Sprite’s or AnimatedSprite’s pivotSlice puts that slice’s pivot on the node’s origin, frame by frame (Sprite_Origin);
    • a Sprite’s nineSlice draws that 9-patch slice stretched to nineSize (the corners keep their size, the edges and middle stretch);
    • a CollisionShape’s slice takes that slice’s rectangle, frame by frame (hitboxes, hurtboxes, a sword’s reach): it follows the nearest Sprite or AnimatedSprite showing an Aseprite file (an ancestor, or a child of an ancestor), turns off where the slice is hidden, and rebuilds its body’s shapes when it changes.
  • Frame events (Aseprite_FrameEvents): a cel’s text in Aseprite (its Properties > User Data), or its event property, on any layer (hidden ones too: an “events” layer works), is an event of that frame; an AnimatedSprite playing the file’s tags sends it through animation.onFrameEvent (data: the name) when the frame shows.
  • Layers (asepriteLayers, the scene file’s layers): a Sprite or AnimatedSprite draws only the named layers (a group’s name brings its layers), so a body and its sword can be two nodes. Their textures are keyed <file>?layers=Body,Sword (with #sheet before it for a sheet).
  • Game_LoadAseprite keeps each file the game reads (Game.aseprites); development runs read a saved file again (Game_ReloadChangedAseprites), so slices and events follow Aseprite too.

TileMap nodes: the tilemap layers of an Aseprite file in a scene. This is the start of the engine’s tilemaps, and it takes Aseprite’s model as its own: a tileset is tiles of one size, tile 0 the empty one; a tilemap layer is a grid of 32-bit tile values, the tile’s index in its tileset (ASEPRITE_TILE_ID) with flip bits (ASEPRITE_TILE_FLIP_X, _Y, _D); tiles carry user data properties. The editor’s tile painting writes the same data (painted layers, below).

<node kind="TileMap" name="Ground" tilemap="assets/level.aseprite" layers="Ground,Walls"/>
  • Settings (TileMapSettings): the file, the frame its tilemaps are taken from (frame), the tilemap layers built (layers, names separated by commas; none: every visible one), and tileCollision (default on).
  • What it builds: a TileMapLayer child per tilemap layer, bottom first, at its cel’s place, drawn in one batch (tile_batch.odin) with the layer’s and cel’s opacity; layers hidden in Aseprite are left out unless named.
  • Collision from the tiles’ properties (set in Aseprite: a tile’s Properties, or the tileset’s for every tile of it): collision = “solid”, “one-way” or “area” (solid = true is solid too); an area property names the Area its tiles go in (and makes them area tiles). Solid and one-way tiles become one StaticBody, “Collision”, their rectangles merged (one-way shapes have oneWay); area tiles an Area per name (“Area” without one). The bodies get the node’s collisionLayer, collisionMask, friction and restitution.
  • Animated (animate, tag): it plays the file’s frames (or a tag’s) at their durations, each layer showing its cel in that frame (water, torches); collision stays on frame’s tiles, so bodies aren’t rebuilt as it plays.
  • Code: TileMap_TileAt (the tile under a point), TileMap_CellAt (the cell), TileMap_TileProperty, TileMap_TileCollision, and TileMap_SetTile (a broken block, a door: drawn at once, an animated layer in every frame; its bodies rebuilt on the next update, so a script holding one of them must look it up again).
  • Development runs (Game.devKeys) read a saved file again and rebuild the node’s layers and collision (twice a second).

A strict zlib inflater (RFC 1950 / RFC 1951) for data the engine doesn’t trust (Aseprite files’ images): it writes into a buffer of the size the caller expects and never past it, treats running out of input as an error, and checks the Adler-32 sum. core:compress/zlib feeds zeros after the end of its input and grows its output without bound, so a damaged file could make it allocate gigabytes (seen: a minute per bad image, then a crash).

AsepriteError :: enum {
None,
// The file couldn't be read
Unreadable,
// Not an Aseprite file (its magic numbers are wrong)
Not_Aseprite,
// It ends early, or a chunk runs past its frame
Truncated,
// A compressed image didn't inflate to its size
Bad_Image,
// A color depth other than 8, 16 or 32 bits
Unsupported_Depth,
// Larger than the engine reads (16384 pixels across)
Too_Large,
}

What went wrong reading an Aseprite file

enum. Source: aseprite.odin:44

AsepriteColorMode :: enum {
RGBA,
Grayscale,
Indexed,
}

A sprite’s color mode

enum. Source: aseprite.odin:64

AsepriteLayerType :: enum {
Image,
Group,
Tilemap,
}

What a layer is

enum. Source: aseprite.odin:71

AsepriteBlendMode :: enum {
Normal,
Multiply,
Screen,
Overlay,
Darken,
Lighten,
Color_Dodge,
Color_Burn,
Hard_Light,
Soft_Light,
Difference,
Exclusion,
Hue,
Saturation,
Color,
Luminosity,
Addition,
Subtract,
Divide,
}

How a layer is blended onto the layers under it (Aseprite’s order and names)

enum. Source: aseprite.odin:78

AsepriteDirection :: enum {
Forward,
Reverse,
Ping_Pong,
Ping_Pong_Reverse,
}

Which way a tag plays its frames

enum. Source: aseprite.odin:101

AsepritePoint :: distinct [2]i32

A property’s point value (POINT)

distinct type. Source: aseprite.odin:109

AsepriteSize :: distinct [2]i32

A property’s size value (SIZE)

distinct type. Source: aseprite.odin:111

AsepriteRect :: distinct [4]i32

A property’s rectangle value (RECT): x, y, width, height

distinct type. Source: aseprite.odin:113

AsepriteUUID :: distinct [16]u8

A property’s UUID value

distinct type. Source: aseprite.odin:115

AsepriteValue :: union {
bool,
i64,
f64,
string,
AsepritePoint,
AsepriteSize,
AsepriteRect,
AsepriteUUID,
[]AsepriteValue,
map[string]AsepriteValue,
}

A property’s value: integers of every size as i64, fixed point and floats as f64, vectors as slices, nested maps as maps (all the file’s arena).

union. Source: aseprite.odin:121

AsepriteUserData :: struct {
// Its text ("" for none)
text: string,
// Its color, when `hasColor`
color: [4]u8,
// Whether it has a color
hasColor: bool,
// The user's properties by name (nil when none; extensions' properties aren't kept)
properties: map[string]AsepriteValue,
}

User data Aseprite attaches to a sprite, layer, cel, tag, slice, tileset or tile

struct. Source: aseprite.odin:135

AsepriteLayer :: struct {
// Its name
name: string,
// Image, group or tilemap
type: AsepriteLayerType,
// The group it's in (-1: the top level)
parent: int,
// Visible in Aseprite (its eye); a hidden group hides what it holds
visible: bool,
// Aseprite's background layer (opaque, at the bottom)
background: bool,
// A reference layer (never exported, so never drawn here)
reference: bool,
// How it's blended onto what's under it
blendMode: AsepriteBlendMode,
// Its opacity (255 when the file doesn't set layer opacity)
opacity: u8,
// A tilemap layer's tileset (index in `Aseprite.tilesets`; -1 for other layers)
tileset: int,
// Its user data
userData: AsepriteUserData,
}

A layer (bottom first in Aseprite.layers; its index is the one cels refer to)

struct. Source: aseprite.odin:147

AsepriteCel :: struct {
// The layer (index in `Aseprite.layers`)
layer: int,
// Where its top-left corner is in the sprite, in pixels
x, y: int,
// Its size: pixels for images, tiles for tilemaps
width, height: int,
// Its opacity
opacity: u8,
// Moves it up (+) or down (-) among the layers in this frame (Aseprite's z-index)
zIndex: int,
// An image's pixels: RGBA, row by row (nil on tilemap layers)
pixels: []u8,
// A tilemap's tiles, row by row: each a tile index with flip bits (ASEPRITE_TILE_*)
tiles: []u32,
// Its user data
userData: AsepriteUserData,
}

A layer’s content in one frame: an image (pixels, RGBA, width x height at x, y in the sprite) or, on a tilemap layer, tiles (tiles, width x height tiles from x, y).

struct. Source: aseprite.odin:174

AsepriteFrame :: struct {
// Milliseconds
duration: int,
// The layers' cels in this frame (layers without one are empty)
cels: []AsepriteCel,
}

A frame: how long it shows, and its cels

struct. Source: aseprite.odin:203

AsepriteTag :: struct {
// Its name ("run")
name: string,
// The first and last frame (inclusive)
from, to: int,
// Which way it plays
direction: AsepriteDirection,
// Times it plays (0: unspecified, which Aseprite plays forever in its UI and once when exporting)
repeat: int,
// Its color in Aseprite's timeline
color: [4]u8,
// Its user data
userData: AsepriteUserData,
}

A tag: a named range of frames, which is how Aseprite names animations

struct. Source: aseprite.odin:211

AsepriteSliceKey :: struct {
// The frame it applies from (to the next key, or the end)
frame: int,
// Its rectangle in the sprite: x, y, width, height (0 wide: hidden from this frame)
bounds: [4]int,
// A 9-patch slice's center, relative to `bounds` (when `Aseprite.slices[i].nineSlice`)
center: [4]int,
// Its pivot, relative to `bounds`' origin (when `Aseprite.slices[i].hasPivot`)
pivot: [2]int,
}

A slice’s shape from a frame on

struct. Source: aseprite.odin:227

AsepriteSlice :: struct {
// Its name
name: string,
// It's a 9-patch (its keys have a center)
nineSlice: bool,
// Its keys have a pivot
hasPivot: bool,
// Its rectangle from frame to frame
keys: []AsepriteSliceKey,
// Its user data
userData: AsepriteUserData,
}

A slice: a named rectangle (a hitbox, a 9-patch, a pivot) over frames

struct. Source: aseprite.odin:239

AsepriteTileset :: struct {
// The id tilemap layers refer to it by
id: int,
// Its name
name: string,
// A tile's size in pixels
tileWidth, tileHeight: int,
// How many tiles (tile 0, the empty one, included)
count: int,
// The number Aseprite shows for tile 1 (UI only; 1 by default)
baseIndex: int,
// The tiles' pixels, RGBA: one column, tile i at row `i * tileHeight` (nil for a tileset
// kept in an external file)
pixels: []u8,
// An external tileset's file (from the external files chunk; "" when it's in this file)
externalFile: string,
// Each tile's user data (by index; shorter than `count` when the last tiles have none)
tileUserData: []AsepriteUserData,
// Its user data
userData: AsepriteUserData,
}

A tileset: same-sized tiles that tilemap layers place by index

struct. Source: aseprite.odin:253

Aseprite :: struct {
// The sprite's size in pixels
width, height: int,
// Its color mode in Aseprite (its pixels here are all RGBA)
colorMode: AsepriteColorMode,
// Its grid: x, y, width, height (width 0: no grid)
grid: [4]int,
// The palette, RGBA
palette: [][4]u8,
// Indexed sprites: the palette entry that's transparent in layers other than the background
transparentIndex: int,
// Layers, bottom first
layers: []AsepriteLayer,
// Frames, in order
frames: []AsepriteFrame,
// Tags (animations)
tags: []AsepriteTag,
// Slices
slices: []AsepriteSlice,
// Tilesets
tilesets: []AsepriteTileset,
// The sprite's own user data
userData: AsepriteUserData,
// Layer blend modes and opacity apply to groups too (composited apart first)
groupBlending: bool,
// Where everything lives
arena: MemoryArena,
}

An Aseprite file, read by Aseprite_Load or Aseprite_Parse. Everything in it is in its arena (Aseprite_Destroy).

struct. Source: aseprite.odin:279

TileMapSettings :: struct {
// `tilemap`: the Aseprite file, relative to the project folder
file: string,
// `frame`: the frame its tilemaps come from
frame: int,
// `layers`: the tilemap layers built, by name, separated by commas ("": every visible one)
layers: string,
// `tileCollision`: tiles with collision properties become bodies (default true)
collision: bool,
// `animate`: plays the file's frames (or `tag`'s) at their durations; collision stays on
// `frame`'s tiles
animate: bool,
// `tag`: the tag it plays ("": every frame)
tag: string,
// `<tiles>` children: layers painted in the editor (or by code), each replacing the file's
// tilemap layer of its name, or a new layer on one of the file's tilesets (format 13)
painted: [dynamic]TileLayerData,
}

A TileMap node’s settings (scene files’ attributes)

struct. Source: tilemap.odin:42

TileLayerData :: struct {
// The layer's name
layer: string,
// A new layer's tileset (index in the file's tilesets)
tileset: int,
// Its top-left corner, in pixels (in the TileMap node's space)
x, y: int,
// Its grid's size in tiles
columns, rows: int,
// The tile values, row by row (columns * rows)
tiles: [dynamic]u32,
}

A tilemap layer painted in the editor, kept in the scene file:

<tiles layer="Ground" tileset="0" x="0" y="0" columns="40" rows="12" data="AQAAAAEAAAA..."/>

data is the tile values (Aseprite’s: ASEPRITE_TILE_ID with the flip bits), row by row, as little-endian 32-bit numbers in base64 (TileLayer_Encode). A layer named like one of the file’s tilemap layers replaces its tiles in every frame (the file’s layer keeps its tileset, opacity and place among the others); any other name is a new layer, drawn over the file’s, on the file’s tileset tileset (an index in Aseprite.tilesets).

struct. Source: tilemap.odin:72

TileCollisionKind :: enum {
None,
Solid,
One_Way,
Area,
}

What a tile becomes in the physics world (TileMap_TileCollision)

enum. Source: tilemap.odin:150

TileMapNode :: struct {
// The node part: transform, hierarchy, hooks
using node: Node,
// Its settings (strings in the node's arena)
settings: TileMapSettings,
// The file as read (its own: freed with the node)
ase: ^Aseprite,
// The project folder its path is relative to (node arena)
projectDir: string,
// The physics settings its bodies get
physics: TileMapPhysics,
// The file's time when read, and seconds to the next look (development runs)
modified: i64,
watchTimer: f64,
// The nodes it made (layers, bodies), replaced when the file is read again
built: [dynamic]^Node,
// Its tile layers, bottom first
tileLayers: [dynamic]^TileMapLayer,
// Its bodies (rebuilt when `TileMap_SetTile` changes collision: `collisionDirty`)
bodies: [dynamic]^Node,
collisionDirty: bool,
// The file's frames it plays (`animate`: every frame, or its tag's; else just `frame`), the one
// showing (index in `frames`) and how long it has shown
frames: []int,
shown: int,
clock: f64,
// Files replaced by rebuilds: freed once their layers are gone (the next update)
retired: [dynamic]^Aseprite,
}

A TileMap node: its layers and collision are its children

struct. Source: tilemap.odin:158

TileMapPhysics :: struct {
layer, mask: u64,
friction, restitution: f32,
}

The physics settings a TileMap’s bodies get (the node’s)

struct. Source: tilemap.odin:189

TileMapLayer :: struct {
// The node part: transform, hierarchy, hooks
using node: Node,
// Its tiles
using batch: TileBatch,
// The layer in the file (index in `Aseprite.layers`), and its name
layerIndex: int,
layerName: string,
// Its grid as it shows now: tiles across and down, and the tile values (the file's)
cells: [2]int,
tiles: []u32,
// The tileset (in the file)
tileset: ^AsepriteTileset,
// Its cel in the TileMap's `frame`: what collision comes from (nil when it has none there)
base: ^AsepriteCel,
// Its cel and tiles in each frame the TileMap plays (by index in `TileMapNode.frames`)
frames: []TileMapLayerFrame,
// The layer's and its cels' opacity
opacity: u8,
}

A tilemap layer of a TileMap node, drawn in one batch

struct. Source: tilemap.odin:195

TileMapLayerFrame :: struct {
cel: ^AsepriteCel,
quads: [dynamic]TileQuad,
}

A layer’s cel in one frame, and its tiles to draw (filled in place: never more than its cells)

struct. Source: tilemap.odin:217

Aseprite_Load :: proc(path: string) -> (^Aseprite, AsepriteError)

Reads an Aseprite file through the platform layer (a data pack first, then the disk).

Inputs

  • path: The file

Returns

Example

ase, err := e.Aseprite_Load("assets/hero.aseprite")
if err == .None {
defer e.Aseprite_Destroy(ase)
for tag in ase.tags do e.Debug_Text(scene, {0, 0}, tag.name)
}

procedure. Source: aseprite.odin:324

Aseprite_Parse :: proc(data: []u8) -> (^Aseprite, AsepriteError)

Reads an Aseprite file’s bytes.

Inputs

  • data: The file’s contents (not kept)

Returns

procedure. Source: aseprite.odin:339

Aseprite_Destroy :: proc(ase: ^Aseprite)

Frees an Aseprite file’s data and everything in it.

Inputs

  • ase: The file (nil does nothing)

procedure. Source: aseprite.odin:359

Aseprite_IsFile :: proc(path: string) -> bool

Whether a path is an Aseprite file, by its extension (.aseprite or .ase, any case)

procedure. Source: aseprite.odin:366

Aseprite_FindLayer :: proc(ase: ^Aseprite, name: string) -> int

A layer by name.

Inputs

  • ase: The file
  • name: The layer’s name

Returns

  • Its index in ase.layers, or -1

procedure. Source: aseprite.odin:381

Aseprite_FindTag :: proc(ase: ^Aseprite, name: string) -> ^AsepriteTag

A tag by name.

Inputs

  • ase: The file
  • name: The tag’s name

Returns

  • The tag, or nil

procedure. Source: aseprite.odin:396

Aseprite_Cel :: proc(ase: ^Aseprite, frame, layer: int) -> ^AsepriteCel

A layer’s cel in a frame.

Inputs

  • ase: The file
  • frame: The frame’s index
  • layer: The layer’s index

Returns

  • The cel, or nil when the layer is empty in that frame

procedure. Source: aseprite.odin:412

Aseprite_LayerShown :: proc(ase: ^Aseprite, layer: int) -> bool

Whether a layer is drawn: it’s visible, so are the groups it’s in, and it isn’t a reference layer.

Inputs

  • ase: The file
  • layer: The layer’s index

procedure. Source: aseprite.odin:425

Aseprite_FramePixels :: proc(ase: ^Aseprite, frame: int, allocator := context.allocator, layers: []int = nil) -> []u8

A frame as Aseprite shows it: the shown layers composited bottom up (see the file’s overview).

Inputs

  • ase: The file
  • frame: The frame’s index
  • allocator: For the pixels
  • layers: Only these layers (by index; nil: every shown layer). A listed layer is drawn even when hidden in Aseprite

Returns

  • RGBA pixels, ase.width x ase.height, row by row (all transparent for a frame out of range)

procedure. Source: aseprite.odin:445

Aseprite_SheetPixels :: proc(ase: ^Aseprite, allocator := context.allocator, layers: []int = nil) -> (pixels: []u8, columns, rows: int)

Every frame on one picture: a grid of columns x rows cells of the sprite’s size, frame i at column i % columns, row i / columns (an animation’s sheet).

Inputs

Returns

  • RGBA pixels (columns * ase.width x rows * ase.height), and the grid’s columns and rows

procedure. Source: aseprite.odin:464

Aseprite_SheetGrid :: proc(ase: ^Aseprite) -> (columns, rows: int)

The grid Aseprite_SheetPixels lays frames out on: about square, kept under ASEPRITE_MAX_SIZE.

Inputs

  • ase: The file

Returns

  • Columns and rows

procedure. Source: aseprite.odin:487

Aseprite_CreateTexture :: proc(renderer: ^sdl.Renderer, ase: ^Aseprite, frame := 0) -> ^sdl.Texture

A texture of a frame (or of every frame as a sheet), with nearest filtering.

Inputs

  • renderer: The renderer
  • ase: The file
  • frame: The frame (-1: the sheet of every frame, Aseprite_SheetPixels)

Returns

  • The texture (the caller’s), or nil

procedure. Source: aseprite.odin:506

Aseprite_TilePixels :: proc(tileset: ^AsepriteTileset, tile: u32, allocator := context.allocator) -> []u8

A tile’s pixels from a tileset, with the tile’s flips applied.

Inputs

  • tileset: The tileset
  • tile: The tile value (index and flip bits)
  • allocator: For the pixels

Returns

  • RGBA pixels, tileWidth x tileHeight (a diagonal flip of a non-square tile is left out), or nil for the empty tile or an index past the tileset

procedure. Source: aseprite.odin:537

Aseprite_Property :: proc(data: AsepriteUserData, name: string) -> (AsepriteValue, bool)

A user data’s property, by name.

Inputs

  • data: The user data
  • name: The property’s name

Returns

  • The value, and whether it’s there

procedure. Source: aseprite.odin:560

Game_LoadAseprite :: proc(game: ^Game, path: string) -> ^Aseprite

A file the game read, kept for its life (nil, logged, when it can’t be read; asked again, it stays nil until it’s saved again in a development run).

Inputs

  • game: The game
  • path: The file (as the resource manager has it: the project folder joined)

Returns

  • The file’s data (the game’s: don’t free it), or nil

procedure. Source: aseprite_nodes.odin:44

Game_ForgetAseprites :: proc(game: ^Game)

Frees the Aseprite files the game keeps (Game_Destroy does it; editors’ preview games too).

Inputs

  • game: The game

procedure. Source: aseprite_nodes.odin:66

Game_ReloadChangedAseprites :: proc(game: ^Game)

Reads again the Aseprite files the game keeps that were saved since (nothing keeps a file’s pointer: nodes look it up when they need it). Development runs call it twice a second; editors for their previews.

Inputs

  • game: The game

procedure. Source: aseprite_nodes.odin:79

Aseprite_SliceAt :: proc(ase: ^Aseprite, name: string, frame: int) -> (key: AsepriteSliceKey, ok: bool)

A slice’s shape in a frame: its key for the last frame at or before it.

Inputs

  • ase: The file
  • name: The slice’s name
  • frame: The frame

Returns

  • The key, and false when there’s no such slice, it starts later, or it’s hidden in that frame (0 wide or high)

procedure. Source: aseprite_nodes.odin:106

Aseprite_FrameEvents :: proc(ase: ^Aseprite, frame: int) -> string

A frame’s events: its cels’ text and event properties, on every layer (in layer order).

Inputs

  • ase: The file
  • frame: The frame

Returns

  • The events, comma-separated (“” for none; temp allocated)

procedure. Source: aseprite_nodes.odin:126

Aseprite_LayersNamed :: proc(ase: ^Aseprite, names: string) -> []int

The layers named (a group’s name brings every layer in it), for drawing only them.

Inputs

  • ase: The file
  • names: Layer names separated by commas (“” for none)

Returns

  • Their indices (temp allocated), nil when none are named

procedure. Source: aseprite_nodes.odin:150

Sprite_Origin :: proc(node: ^Node) -> [2]f32

The point of a sprite’s picture drawn at the node’s origin: its pivot slice’s pivot in that frame, else the picture’s middle.

Inputs

  • node: A Sprite or AnimatedSprite

Returns

  • The point, in the picture’s pixels

procedure. Source: aseprite_nodes.odin:175

TileLayer_Encode :: proc(tiles: []u32, allocator := context.allocator) -> string

Tile values as the scene file’s data: little-endian 32-bit numbers in base64.

Inputs

  • tiles: The tile values
  • allocator: For the text

Returns

  • The text

procedure. Source: tilemap.odin:95

TileLayer_Decode :: proc(text: string, count: int, allocator := context.allocator) -> ([dynamic]u32, bool)

A scene file’s data as tile values (TileLayer_Encode’s inverse).

Inputs

  • text: The base64 text
  • count: How many tiles it should hold (columns * rows)
  • allocator: For the tiles

Returns

  • The tiles (always count of them: missing ones empty), and false when the text is damaged

procedure. Source: tilemap.odin:113

TileLayerData_Destroy :: proc(layer: ^TileLayerData, allocator := context.allocator)

Frees a painted layer’s strings and tiles

procedure. Source: tilemap.odin:124

TileLayerData_Clone :: proc(layer: TileLayerData, allocator := context.allocator) -> TileLayerData

A copy of a painted layer (owning its name and tiles)

procedure. Source: tilemap.odin:131

TileLayerData_Equal :: proc(a, b: TileLayerData) -> bool

Whether two painted layers are the same

procedure. Source: tilemap.odin:140

TileMap_TileCollision :: proc(tileset: ^AsepriteTileset, tile: u32) -> (kind: TileCollisionKind, area: string)

A tile’s collision: its own properties, else its tileset’s (see the file’s overview).

Inputs

  • tileset: The tileset
  • tile: The tile value (its index; flips don’t matter)

Returns

  • What it becomes, and an area tile’s Area name (“” for the default)

procedure. Source: tilemap.odin:232

TileMap_TileAt :: proc(node: ^TileMapNode, layer: string, position: [2]f32) -> (tile: u32, at: ^TileMapLayer, ok: bool)

The tile under a point of the world, in a layer of a TileMap.

Inputs

  • node: The TileMap
  • layer: The layer’s name (“”: the top-most layer with a tile there)
  • position: A point in the world

Returns

  • The tile value (0: empty), its layer, and false when the point is off every grid

procedure. Source: tilemap.odin:271

TileMap_TileProperty :: proc(layer: ^TileMapLayer, tile: u32, name: string) -> (AsepriteValue, bool)

A property of a tile (its own, else its tileset’s).

Inputs

  • layer: The tile’s layer (TileMap_TileAt)
  • tile: The tile value
  • name: The property

Returns

  • The value, and whether there’s one

Example

// Given: level: ^e.TileMapNode, hero: ^e.Node
if tile, layer, ok := e.TileMap_TileAt(level, "Ground", e.Node_GetWorldPosition(hero)); ok {
if value, has := e.TileMap_TileProperty(layer, tile, "damage"); has do log.info(value)
}

procedure. Source: tilemap.odin:301

TileMap_CellAt :: proc(node: ^TileMapNode, layer: string, position: [2]f32) -> (column, row: int, ok: bool)

The tile cell under a point of the world, in a layer as it shows now.

Inputs

  • node: The TileMap
  • layer: The layer’s name
  • position: A point in the world

Returns

  • The column and row, and false when the point is off the layer’s grid (or there’s no such layer)

procedure. Source: tilemap.odin:492

TileMap_SetTile :: proc(node: ^TileMapNode, layer: string, column, row: int, tile: u32) -> bool

Changes a tile: drawn at once, collision rebuilt on the node’s next update (a broken block, a door opening). An animated layer gets it in every frame it plays, so it stays.

Inputs

  • node: The TileMap
  • layer: The layer’s name
  • column, row: The cell (in the layer’s grid, as TileMap_CellAt gives it)
  • tile: The tile value (an index in the layer’s tileset, with flip bits; 0 empties it)

Returns

  • false when there’s no such layer or cell

Example

// Given: level: ^e.TileMapNode, hit: [2]f32
if column, row, ok := e.TileMap_CellAt(level, "Ground", hit); ok do e.TileMap_SetTile(level, "Ground", column, row, 0)

procedure. Source: tilemap.odin:519

TileMap_Rebuild :: proc(node: ^TileMapNode, ase: ^Aseprite)

Builds a TileMap’s layers and collision again, from a file’s data (the node takes it and frees the old). Development runs do it when the file is saved; editors when it changes.

Inputs

  • node: The TileMap
  • ase: The file, read again

procedure. Source: tilemap.odin:688

Tileset_TerrainOf :: proc(tileset: ^AsepriteTileset, tile: u32) -> string

The terrain a tile belongs to: its terrain property in Aseprite (“” for none).

Inputs

  • tileset: The tileset
  • tile: A tile value (flips don’t matter)

procedure. Source: tilemap.odin:729

Tileset_Terrains :: proc(tileset: ^AsepriteTileset, allocator := context.allocator) -> []string

A tileset’s terrains, in the order their first tiles come.

Inputs

  • tileset: The tileset
  • allocator: For the list

Returns

  • The terrains’ names

procedure. Source: tilemap.odin:748

Terrain_TileFor :: proc(tileset: ^AsepriteTileset, terrain: string, mask: int) -> u32

The tile of a terrain drawn for a cell whose same-terrain neighbours are mask: the one whose own mask matches best (exactly, else in the most neighbours; corners only when the terrain’s tiles use them, so a 16-tile set of edges works too), else any tile of the terrain.

Inputs

  • tileset: The tileset
  • terrain: The terrain’s name
  • mask: The cell’s neighbours of the same terrain (TERRAIN_* bits)

Returns

  • The tile (an index; 0 when the tileset has no tile of that terrain)

procedure. Source: tilemap.odin:814

TileGrid_PaintTerrain :: proc(tileset: ^AsepriteTileset, tiles: []u32, columns, rows, column, row: int, terrain: string) -> bool

Paints a terrain into a grid of tiles (autotiling): the cell takes the terrain, then it and its eight neighbours of a terrain take the tile that fits their neighbours (Terrain_TileFor). Painting “” erases the cell and fixes its neighbours. Cells off the grid count as the same terrain, so a terrain reaches the map’s edges without a border.

Inputs

  • tileset: The tileset
  • tiles: The grid, row by row (changed in place)
  • columns, rows: Its size
  • column, row: The cell
  • terrain: The terrain (“” to erase)

Returns

  • false when the cell is off the grid

procedure. Source: tilemap.odin:853

TileMap_SetTerrain :: proc(node: ^TileMapNode, layer: string, column, row: int, terrain: string) -> bool

Paints a terrain into a TileMap’s layer from code (TileGrid_PaintTerrain): drawn at once, collision on the next update, every frame of an animated layer.

Inputs

  • node: The TileMap
  • layer: The layer’s name
  • column, row: The cell
  • terrain: The terrain’s name (“” erases)

Returns

  • false when there’s no such layer or cell

Example

// Given: level: ^e.TileMapNode, hit: [2]f32
if column, row, ok := e.TileMap_CellAt(level, "Ground", hit); ok do e.TileMap_SetTerrain(level, "Ground", column, row, "")

procedure. Source: tilemap.odin:891

ASEPRITE_MAX_SIZE :: 16384

The largest sprite, cel or tileset side the reader takes, in pixels or tiles

constant. Source: aseprite.odin:61

ASEPRITE_TILE_ID :: u32(0x1fffffff)

A tile’s index in its tileset: tile & ASEPRITE_TILE_ID (0 is the empty tile)

constant. Source: aseprite.odin:194

ASEPRITE_TILE_FLIP_X :: u32(0x80000000)

The tile is mirrored left to right

constant. Source: aseprite.odin:196

ASEPRITE_TILE_FLIP_Y :: u32(0x40000000)

The tile is mirrored top to bottom

constant. Source: aseprite.odin:198

ASEPRITE_TILE_FLIP_D :: u32(0x20000000)

The tile’s axes are swapped (a transpose; with the X or Y flip it makes the quarter turns)

constant. Source: aseprite.odin:200

ASEPRITE_LAYERS_SUFFIX :: "?layers="

The resource manager key suffix naming the layers a texture of an Aseprite file draws

constant. Source: aseprite_nodes.odin:31

DEFAULT_TILEMAP_SETTINGS :: TileMapSettings{collision = true}

A TileMap node’s settings by default

constant. Source: tilemap.odin:147

TERRAIN_N :: 1

The neighbour bits of a terrain tile’s mask property: the neighbours of the same terrain it’s drawn for (a corner counts only with both its edges: the “blob” rule). This one: the tile above

constant. Source: tilemap.odin:706

TERRAIN_NE :: 2

A terrain mask’s bit for the tile above and to the right

constant. Source: tilemap.odin:708

TERRAIN_E :: 4

A terrain mask’s bit for the tile to the right

constant. Source: tilemap.odin:710

TERRAIN_SE :: 8

A terrain mask’s bit for the tile below and to the right

constant. Source: tilemap.odin:712

TERRAIN_S :: 16

A terrain mask’s bit for the tile below

constant. Source: tilemap.odin:714

TERRAIN_SW :: 32

A terrain mask’s bit for the tile below and to the left

constant. Source: tilemap.odin:716

TERRAIN_W :: 64

A terrain mask’s bit for the tile to the left

constant. Source: tilemap.odin:718

TERRAIN_NW :: 128

A terrain mask’s bit for the tile above and to the left

constant. Source: tilemap.odin:720