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:
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 theassets/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:
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:
.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:
- •Ask them (if unknown) for:
- •total texture size in pixels
- •number of columns and rows
- •Compute single-frame size as:
- •
frame_width = sheet_width / columns - •
frame_height = sheet_height / rows
- •
Generate code using TextureAtlas:
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):
- •Treat it as a full-screen animated sprite, not a GIF.
- •Use a component with a
Timerand cycle theTextureAtlasSprite::index.
Template:
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:
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.