AgentSkillsCN

rust-snake-assets

帮助管理并整合2D图像资源(精灵、精灵图集与动画背景),融入使用Bevy构建的Rust蛇形游戏。每当用户询问如何加载图像、整理资源、使用精灵图集,或为背景添加动画效果(包括类似GIF的特效)时,均可使用此功能。

SKILL.md
--- frontmatter
name: rust-snake-assets
description: 
  Helps manage and integrate 2D image assets (sprites, sprite sheets, and animated backgrounds)into a Rust Snake game built with Bevy. Use this whenever the user asks about loading images, organizing assets, using sprite sheets, or animating backgrounds (including GIF-like effects).

tags:

  • rust
  • bevy
  • game-dev
  • assets
  • sprite-sheets
  • animation when_to_use: > Use this skill whenever the user works on their Rust Snake game and asks about:
  • where to put image/audio assets
  • how to load textures in Bevy
  • how to slice and animate sprite sheets
  • how to emulate GIF animations for backgrounds
  • how to structure asset-related Rust code and modules.

Rust Snake Game Asset Skill

You are an assistant embedded in a Rust/Bevy Snake game repo.
Your job is to standardize and manage all visual assets: static sprites, sprite sheets, and animated backgrounds (GIF-style) for the game.

Follow the instructions below when the user asks anything related to game assets.


1. Project Asset Conventions

Assume the project structure:

text
project-root/
  Cargo.toml
  src/
    main.rs
    assets.rs
    background.rs
    snake.rs
  assets/
    images/
      snake_head.png
      snake_body_straight.png
      snake_body_turn.png
      snake_tail.png
      food_apple.png
      powerup_poison.png
      powerup_lightning.png
      obstacle_wall.png
      circuit_bg_sheet.png
    audio/
      eat.ogg
      game_over.wav
    fonts/
      ui_font.ttf

Rules:

  • All runtime-loaded assets live under assets/, never referenced with the assets/ prefix in code.
  • Image references in Rust always use paths like "images/snake_head.png", not "assets/images/...".
  • Prefer PNG with alpha for sprites and overlays; use JPG only for non-transparent backgrounds.

2. Basic Texture Loading in Bevy

When the user needs to load a static sprite, generate code like:

rust
use bevy::prelude::*;

#[derive(Resource)]
pub struct GameAssets {
    pub snake_head: Handle<Image>,
    pub snake_body_straight: Handle<Image>,
    pub snake_body_turn: Handle<Image>,
    pub snake_tail: Handle<Image>,
    pub food_apple: Handle<Image>,
    pub powerup_poison: Handle<Image>,
    pub powerup_lightning: Handle<Image>,
    pub obstacle_wall: Handle<Image>,
    pub bg_texture: Handle<Image>,
}

pub fn load_assets(mut commands: Commands, asset_server: Res<AssetServer>) {
    let assets = GameAssets {
        snake_head: asset_server.load("images/snake_head.png"),
        snake_body_straight: asset_server.load("images/snake_body_straight.png"),
        snake_body_turn: asset_server.load("images/snake_body_turn.png"),
        snake_tail: asset_server.load("images/snake_tail.png"),
        food_apple: asset_server.load("images/food_apple.png"),
        powerup_poison: asset_server.load("images/powerup_poison.png"),
        powerup_lightning: asset_server.load("images/powerup_lightning.png"),
        obstacle_wall: asset_server.load("images/obstacle_wall.png"),
        bg_texture: asset_server.load("images/circuit_bg_sheet.png"),
    };

    commands.insert_resource(assets);
}

Always suggest adding ImagePlugin::default_nearest() to keep pixel art crisp:

rust
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))

3. Handling Sprite Sheets (Snake + Powerups)

When the user has a single image containing multiple tiles of equal size:

  1. Ask them (if unknown) for:
    • total texture size in pixels
    • number of columns and rows
  2. Compute single-frame size as:
    • frame_width = sheet_width / columns
    • frame_height = sheet_height / rows

Generate code using TextureAtlas:

rust
use bevy::prelude::*;

pub fn spawn_snake_from_atlas(
    mut commands: Commands,
    game_assets: Res<GameAssets>,
    mut atlases: ResMut<Assets<TextureAtlas>>,
) {
    let texture_handle = game_assets.snake_head.clone(); // or dedicated sheet handle
    let atlas = TextureAtlas::from_grid(
        texture_handle,
        Vec2::new(32.0, 32.0), // replace with actual frame size
        4,                     // columns
        1,                     // rows
        None,
        None,
    );
    let atlas_handle = atlases.add(atlas);

    commands.spawn(SpriteSheetBundle {
        texture_atlas: atlas_handle,
        sprite: TextureAtlasSprite::new(0), // initial frame
        ..default()
    });
}

Never tell the user to use GIFs for in-game animation; always guide them to sprite sheets + frame index animation.


4. Animated Backgrounds (GIF-like Effect)

When the user has a 3×2 or similar background sprite sheet (e.g., circuit_bg_sheet.png):

  1. Treat it as a full-screen animated sprite, not a GIF.
  2. Use a component with a Timer and cycle the TextureAtlasSprite::index.

Template:

rust
use bevy::prelude::*;

const BG_COLUMNS: usize = 3;
const BG_ROWS: usize = 2;
const BG_FRAMES: usize = BG_COLUMNS * BG_ROWS;

#[derive(Component)]
pub struct AnimatedBackground {
    pub timer: Timer,
}

pub fn spawn_animated_background(
    mut commands: Commands,
    game_assets: Res<GameAssets>,
    mut atlases: ResMut<Assets<TextureAtlas>>,
) {
    let texture_handle = game_assets.bg_texture.clone();

    let atlas = TextureAtlas::from_grid(
        texture_handle,
        Vec2::new(1024.0, 576.0), // one frame size, to be adjusted
        BG_COLUMNS as u32,
        BG_ROWS as u32,
        None,
        None,
    );
    let atlas_handle = atlases.add(atlas);

    commands.spawn((
        SpriteSheetBundle {
            texture_atlas: atlas_handle,
            sprite: TextureAtlasSprite::new(0),
            transform: Transform::from_xyz(0.0, 0.0, -10.0),
            ..default()
        },
        AnimatedBackground {
            timer: Timer::from_seconds(0.12, TimerMode::Repeating),
        },
    ));
}

pub fn animate_background(
    time: Res<Time>,
    mut query: Query<(&mut AnimatedBackground, &mut TextureAtlasSprite)>,
) {
    for (mut anim, mut sprite) in &mut query {
        anim.timer.tick(time.delta());
        if anim.timer.finished() {
            sprite.index = (sprite.index + 1) % BG_FRAMES;
        }
    }
}

If the sheet includes a dark background area, treat it as the actual background image. Advise the user that transparency requires preprocessing in an image editor.


5. Python/PIL Helper for GIF Export (Optional)

If the user explicitly wants a .gif for web preview, generate a separate Python snippet using Pillow:

python
from PIL import Image

sheet = Image.open("image.jpg")
sheet_w, sheet_h = sheet.size

cols, rows = 3, 2
frame_w = sheet_w // cols
frame_h = sheet_h // rows

frames = []
for r in range(rows):
    for c in range(cols):
        left = c * frame_w
        top = r * frame_h
        right = left + frame_w
        bottom = top + frame_h
        frames.append(sheet.crop((left, top, right, bottom)))

frames.save(
    "background.gif",
    save_all=True,
    append_images=frames[1:],
    duration=120,
    loop=0,
)

Clarify that this GIF is not used by Bevy; it’s only for preview/sharing.


6. What to Ask the User

Before generating code, the agent should, if missing:

  • Confirm the actual file paths under assets/.
  • Confirm grid size (e.g., 32×32, 64×64) to keep sprites aligned.
  • Confirm sprite sheet layout (columns/rows) for correct atlas parameters.
  • Ask if they want:
    • crisp pixel art (use nearest neighbor)
    • or smooth scaling (use default filter).

Always adapt the code snippets to the project’s existing module structure and naming conventions.

code