bevy_simple_screenshot

Crates.iobevy_simple_screenshot
lib.rsbevy_simple_screenshot
version0.1.2
created_at2025-12-21 09:46:57.503679+00
updated_at2025-12-21 10:11:47.154608+00
descriptionA plug-and-play screenshot library for Bevy 0.17+ with ring-buffered capture and automatic saving
homepage
repositoryhttps://github.com/powergun/bevy_simple_screenshot
max_upload_size
id1997776
size299,467
Wei Ning (powergun)

documentation

https://docs.rs/bevy_simple_screenshot

README

bevy_simple_screenshot

A plug-and-play screenshot library for Bevy 0.17+ with ring-buffered capture and automatic saving.

Quick Start

Add the dependency:

[dependencies]
bevy_simple_screenshot = "0.1"

Add the plugin and capture screenshots from anywhere in your game code:

use bevy::prelude::*;
use bevy_simple_screenshot::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(ScreenshotBufferPlugin::new())
        // Add your own game systems that use ScreenshotTrigger
        .add_systems(Update, your_game_system)
        .run();
}

// Example:
// Suppose you have a system that requires screenshot functionality.
// Here is the Update function of this system.
// You can integrate the screenshot functionality in other part of the system, too.
fn your_game_system(
    trigger: ScreenshotTrigger,
    // ... your other system parameters
) {
    // Call screenshot!() whenever you want to capture
    // For example, when a player dies:
    screenshot!(&trigger, "player_died");

    // Or with a category key for organization:
    screenshot!(&trigger, "combat", "boss_defeated");
}

The ScreenshotTrigger system parameter can be added to any of your game systems. Call screenshot!() whenever you want to capture - it's non-blocking and won't affect your game's performance (see benchmarks).

Screenshots are saved to .screenshots/ by default.

Entity-Focused Screenshots

Capture screenshots cropped to a specific entity (e.g., your game's hero during a special move). This is useful for:

  • Character animation debugging
  • Sprite sheet generation
  • Isolated entity captures for thumbnails or documentation
use bevy::prelude::*;
use bevy_simple_screenshot::prelude::*;

// ─────────────────────────────────────────────────────────────
// Your existing game code (components, etc.)
// ─────────────────────────────────────────────────────────────

#[derive(Component)]
struct Hero;

#[derive(Component)]
struct SpecialMoveActive;

// ─────────────────────────────────────────────────────────────
// Add EntityScreenshotTrigger to your system parameters
// ─────────────────────────────────────────────────────────────

fn capture_hero_special_move(
    trigger: EntityScreenshotTrigger,  // <-- Use this instead of ScreenshotTrigger
    hero_query: Query<Entity, (With<Hero>, Added<SpecialMoveActive>)>,
) {
    for hero_entity in &hero_query {
        // Capture with default settings (20px padding)
        screenshot_entity!(&trigger, hero_entity, "hero", "special_move");

        // Or with custom padding
        let settings = EntityScreenshotSettings::default().with_padding(50);
        screenshot_entity!(&trigger, hero_entity, "hero", "special_move", settings);
    }
}

How It Works

  1. The macro reads the entity's position and size (from Sprite or Aabb)
  2. Converts world coordinates to screen coordinates using the active 2D camera
  3. Captures the full window, then crops to the entity bounds + padding
  4. Handles HiDPI/Retina displays automatically (scales coordinates appropriately)

screenshot_entity! Variants

// Entity only (default key "default", no description, 20px padding)
screenshot_entity!(&trigger, entity);

// With key
screenshot_entity!(&trigger, entity, "hero");

// With key and description
screenshot_entity!(&trigger, entity, "hero", "idle_animation");

// With key, description, and custom settings
let settings = EntityScreenshotSettings::default()
    .with_padding(30)
    .with_fallback_size(64, 64);  // Used if entity has no Sprite/Aabb
screenshot_entity!(&trigger, entity, "hero", "idle_animation", settings);

Configuration

Create screenshots.toml in your project root:

output_dir = ".screenshots"
buffer_capacity = 10
format = "png"          # or "jpeg"
auto_save = true

[keys.combat]
buffer_capacity = 20
format = "jpeg"
jpeg_quality = "high"   # max, high, medium, low

# Optional: burn-in text overlay on screenshots
# NOTE: font_path is REQUIRED when burn-in is enabled - see "Important Notes" below
[burn_in]
enabled = true
font_path = "assets/fonts/MyFont.ttf"  # REQUIRED - must be a valid TTF from your project
show_frame = true       # show frame number (default when enabled)
show_key = true         # show screenshot key
show_description = true # show description text
position = "upper-right" # upper-left, upper-right, lower-left, lower-right
padding = 5             # pixels from edge
font_size = 18.0        # font size in pixels

Or configure programmatically:

ScreenshotBufferPlugin::with_config(
    ScreenshotConfig::default()
        .with_output_dir(".screenshots")
        .with_buffer_capacity(5)
        .with_format(ImageFormat::Jpeg)
        .with_burn_in(
            BurnInConfig::enabled()
                .with_font("assets/fonts/MyFont.ttf")  // required
                .with_show_frame(true)
                .with_show_key(true)
                .with_position(BurnInPosition::UpperRight)
        )
)

Important Notes

Font Requirement for Burn-In

When burn-in text overlay is enabled, you must provide a valid TTF font file from your game project. The library does not bundle any fonts to keep dependencies minimal and give you full control over the appearance.

// This will PANIC if the font file doesn't exist or is invalid
BurnInConfig::enabled()
    .with_font("assets/fonts/MyFont.ttf")  // <-- Required!

If you enable burn-in without specifying a font path, or if the font file cannot be loaded, the application will panic with a clear error message. Make sure to:

  1. Use a font that is already part of your game's assets
  2. Verify the path is correct relative to your project root
  3. Use a standard TTF font file

Description Text Sanitization

The description parameter in screenshot!() and screenshot_entity!() is used in the output filename. To ensure valid filenames across all platforms, the description is automatically sanitized:

  • Only alphanumeric characters, underscores (_), and hyphens (-) are preserved
  • All other characters (spaces, special characters, non-printable characters) are removed
  • Path traversal attempts (e.g., ../) are neutralized
// These descriptions will be sanitized:
screenshot!(&trigger, "combat", "boss defeated!");     // -> "boss_defeated"
screenshot!(&trigger, "debug", "frame 100 @ 60fps");   // -> "frame_100__60fps"
screenshot!(&trigger, "test", "../../etc/passwd");     // -> "etcpasswd"

This ensures screenshots are always saved with safe, predictable filenames regardless of the description content.

Documentation

License

MIT

Commit count: 0

cargo fmt