Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Sprites

Live Demo: Sprites

Sprites are 2D textured quads rendered in 3D space. They support texture blending, animation, and GPU-instanced rendering with automatic z-sorting.

Sprite Component

The Sprite component defines the visual properties of a sprite entity:

#![allow(unused)]
fn main() {
pub struct Sprite {
    pub position: Vec2,
    pub size: Vec2,
    pub scale: Vec2,
    pub rotation: f32,
    pub depth: f32,
    pub color: [f32; 4],
    pub texture_index: u32,
    pub texture_index2: u32,
    pub blend_factor: f32,
    pub uv_min: Vec2,
    pub uv_max: Vec2,
    pub blend_mode: SpriteBlendMode,
}
}
FieldDescription
positionWorld-space 2D position
sizeWidth and height in world units
scaleScale multiplier
rotationRotation in radians
depthZ-ordering (higher values draw on top)
colorRGBA tint color, multiplied with the texture
texture_indexPrimary texture slot in the sprite atlas
texture_index2Secondary texture slot for blending
blend_factorBlend weight between the two textures (0.0 = first, 1.0 = second)
uv_min / uv_maxUV coordinates within the atlas slot
blend_modeSpriteBlendMode::Alpha, Additive, or Screen

Creating Sprites

Sprites live in the Sprite2d sub-ECS, not Core. Use the spawn_sprite helper or manually add sprite components:

#![allow(unused)]
fn main() {
let entity = spawn_sprite(world, Vec2::new(0.0, 0.0), Vec2::new(32.0, 32.0));

if let Some(sprite) = world.sprite2d.get_sprite_mut(entity) {
    sprite.texture_index = 0;
    sprite.color = [1.0, 0.5, 0.5, 1.0];
}
}

Core and Sprite2d flags cannot be mixed in one spawn_entities call. The spawn_sprite helper handles this for you.

Texture Blending

Blend between two textures for smooth transitions:

#![allow(unused)]
fn main() {
let sprite = Sprite::new()
    .with_multitexture(0, 1, 0.5);
}

Loading Sprite Textures

Sprite textures are stored in the sprite texture atlas, not the main texture cache. Upload textures to atlas slots using WorldCommand::UploadSpriteTexture:

#![allow(unused)]
fn main() {
world.queue_command(WorldCommand::UploadSpriteTexture {
    slot: 0,
    rgba_data: image_bytes,
    width: 256,
    height: 256,
});
}

Each slot is 512x512 pixels. There are 128 total slots. Textures smaller than the slot size are placed in the top-left corner of the slot.

Sprite Animation

The SpriteAnimator component drives frame-based animation by updating the sprite's UV coordinates each frame.

Grid-Based Animation

For sprite sheets arranged in a grid:

#![allow(unused)]
fn main() {
let animator = SpriteAnimator::from_grid(
    8,      // columns
    4,      // rows
    32,     // total frames
    0.1,    // seconds per frame
);

world.sprite2d.add_components(entity, SPRITE_ANIMATOR);
world.sprite2d.set_sprite_animator(entity, animator);
}

This computes UV coordinates for each frame automatically based on the grid layout.

Loop Modes

#![allow(unused)]
fn main() {
let animator = SpriteAnimator::from_grid(4, 1, 4, 0.15)
    .with_loop_mode(LoopMode::Loop);      // default
}
ModeBehavior
LoopMode::OncePlays once and stops on the last frame
LoopMode::LoopLoops back to the first frame (default)
LoopMode::PingPongPlays forward, then backward, repeating

Playback Control

#![allow(unused)]
fn main() {
animator.play();
animator.pause();
animator.reset();
}

Speed

#![allow(unused)]
fn main() {
let animator = SpriteAnimator::from_grid(4, 1, 4, 0.1)
    .with_speed(2.0); // 2x playback speed
}

Manual Frames

For non-uniform frame layouts, define frames individually:

#![allow(unused)]
fn main() {
let animator = SpriteAnimator {
    frames: vec![
        SpriteFrame {
            uv_min: Vec2::new(0.0, 0.0),
            uv_max: Vec2::new(0.25, 0.5),
            duration: 0.1,
            texture_index: None,
        },
        SpriteFrame {
            uv_min: Vec2::new(0.25, 0.0),
            uv_max: Vec2::new(0.5, 0.5),
            duration: 0.2,
            texture_index: Some(1), // switch to atlas slot 1
        },
    ],
    ..Default::default()
};
}

The texture_index field on SpriteFrame optionally switches the sprite's atlas slot on that frame.

Animation System

The sprite_animation_system must run each frame to advance animations:

#![allow(unused)]
fn main() {
fn run_systems(&mut self, world: &mut World) {
    sprite_animation_system(world);
}
}

Rendering

The SpritePass renders all sprite entities using GPU instancing. It:

  • Collects all entities with Sprite + GlobalTransform components
  • Performs frustum culling based on camera position
  • Sorts sprites by depth for correct alpha blending
  • Renders camera-facing quads with instanced draw calls

Sprites are rendered as part of the geometry pass pipeline and write to both scene_color and depth.