Skip to content

Sprites

Tecs provides a complete sprite system with animated sprites from Aseprite, per-frame collision boxes, animation events, and material maps for advanced lighting. Sprites are rendered using GPU instancing for maximum performance.

Quick Start

teal
local assets = require("tecs2d.assets")
local manager = world.resources[assets]

-- Load sprite sheet (async, returns handle, caches asset)
local handle = manager:loadSpriteSheet("player.png")

-- Create sprite from loaded sheet
world:spawn(
    tecs.builtins.Transform(100, 100),
    gfx.Sprite.fromSheet(handle.value, "walk")
)

Creating Sprites

Use loadSpriteSheet for async loading with automatic caching.

teal
local assets = require("tecs2d.assets")
local manager = world.resources[assets]

-- Load sprite sheet (async, returns handle)
local handle = manager:loadSpriteSheet("player.png")

-- Spawn a Sprite and pass in option configuration
world:spawn(
    tecs.builtins.Transform(200, 100),
    gfx.Sprite.fromSheet(handle.value, "attack", {
        speed = 1.5,         -- 1.5x playback speed
        centered = true,     -- Center on transform (default)
        pivotSlice = "feet", -- Use "feet" slice for pivot
        startTime = 0        -- Animation start time
    })
)

For static images without animation, use loadStaticSheet:

teal
local bgHandle = manager:loadStaticSheet("background.png")
world:spawn(
    tecs.builtins.Transform(0, 0),
    gfx.Sprite.fromSheet(bgHandle.value)
)

Convenience Shorthand (Sync)

fromAseprite combines loading and sprite creation in one call. Useful for prototyping, but blocks the main thread:

teal
-- Loads sheet synchronously and creates sprite
gfx.Sprite.fromAseprite("player.png", "walk")

-- With options
gfx.Sprite.fromAseprite("player.png", "attack", {
    speed = 1.5,
    pivotSlice = "feet"
})

From SpriteSheet (Sync)

For loading a sheet once and creating multiple sprites:

teal
-- Load sheet synchronously (blocks main thread)
local sheet = gfx.SpriteSheet.fromFile("enemies.png")

-- Create multiple sprites from the same sheet
local goblin = gfx.Sprite.fromSheet(sheet, "goblin_idle")
local orc = gfx.Sprite.fromSheet(sheet, "orc_idle")

From Texture (Single Frame)

For simple textures without animation:

teal
local texture = love.graphics.newImage("bullet.png")
gfx.Sprite.fromTexture(texture)

Sprite Options

OptionTypeDefaultDescription
speednumber1.0Animation speed multiplier
centeredbooleantrueCenter sprite on transform position
pivotSlicestringnilName of Aseprite slice to use as pivot
startTimenumber0Animation start time (for staggering)

Sprite Methods

teal
local sprite = world:get(entityId, gfx.Sprite)

-- Animation control
sprite:setTag("run")           -- Change animation tag
sprite:playOnce("attack")      -- Play a tag once, then freeze on the last frame
sprite:pause()                 -- Pause at current frame
sprite:resume()                -- Resume from pause
sprite:pauseAtEnd()            -- Jump to last frame and pause
sprite:pauseAtStart()          -- Jump to first frame and pause
sprite:gotoFrame(3)            -- Jump to specific frame (0-indexed)

-- Query state
local tag = sprite:getTag()              -- Current tag name
local frame = sprite:getFrame()          -- Current frame (0-indexed within tag)
local absFrame = sprite:getAbsoluteFrame() -- Absolute frame (1-indexed in sheet)
local w, h = sprite:getDimensions()      -- Sprite dimensions
local px, py = sprite:getPivot()         -- Current pivot point
local cx, cy = sprite:getCenter()        -- Center point (0.5, 0.5)

Combining with Other Components

Sprites work with styling components:

ComponentDescription
ColorRGBA tint applied to the sprite
BlendModeControls pixel blending (add, multiply, etc.)
UnlitSkip dynamic lighting (use for UI sprites)
PivotCustom origin point (overrides pivotSlice option)
MaterialPer-entity GPU shader effects (dissolve, glow, etc.)
teal
local sheet = manager:loadSpriteSheet("player.png").value

world:spawn(
    tecs.builtins.Transform(100, 100),
    gfx.Sprite.fromSheet(sheet, "idle"),
    gfx.Color(1, 0.8, 0.8, 1),           -- Tint
    gfx.Unlit,                           -- Skip lighting
    gfx.Pivot(0.5, 1.0)                  -- Custom pivot (overrides pivotSlice)
)

GPU Rendering Architecture

Sprites are rendered using a high-performance GPU pipeline that can handle millions of sprites efficiently.

Texture Buckets

Sprite textures are automatically grouped into size-bucketed texture arrays:

BucketMax SizeLayers per Array
Tiny16×16512
Small64×64256
Medium256×256128
Large512×51264
XL1024×102432
XXL2048×204816

This architecture enables:

  • Single draw call per bucket - All sprites using textures in the same bucket are batched together
  • Automatic allocation - Textures are assigned to buckets based on their dimensions
  • GPU culling - Visibility culling runs entirely on the GPU via compute shaders

DirectSprite for Large Textures

Textures larger than 2048×2048 cannot use the bucket system. Use the DirectSprite component for these:

teal
world:spawn(
    tecs.builtins.Transform(0, 0),
    gfx.Sprite.fromTexture(hugeBackgroundTexture),
    gfx.DirectSprite  -- Bypass bucket system, render directly
)

DirectSprite entities are rendered separately and don't benefit from instanced batching.

Documentation

TopicDescription
SheetsLoading sprite sheets, Aseprite export settings, material maps
AnimationTags, playback control, timing, directions
SlicesPivot points, per-frame pivots
CollisionsPhysics integration from slices
EventsChangeTag events
TilingRepeatedSprite for tiling