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, } }
| Field | Description |
|---|---|
position | World-space 2D position |
size | Width and height in world units |
scale | Scale multiplier |
rotation | Rotation in radians |
depth | Z-ordering (higher values draw on top) |
color | RGBA tint color, multiplied with the texture |
texture_index | Primary texture slot in the sprite atlas |
texture_index2 | Secondary texture slot for blending |
blend_factor | Blend weight between the two textures (0.0 = first, 1.0 = second) |
uv_min / uv_max | UV coordinates within the atlas slot |
blend_mode | SpriteBlendMode::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 }
| Mode | Behavior |
|---|---|
LoopMode::Once | Plays once and stops on the last frame |
LoopMode::Loop | Loops back to the first frame (default) |
LoopMode::PingPong | Plays 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+GlobalTransformcomponents - 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.