Asset Format¶
This guide documents the file formats used for game assets in brkrs.
Level Files¶
Level definitions are consolidated in assets/levels/README.md:
File Naming¶
Format:
level_NNN.ronwhere NNN is a zero-padded numberExamples:
level_001.ron,level_002.ron,level_999.ron
Level Definition Structure¶
A level file is a RON value with the LevelDefinition structure the runtime expects.
Minimal example:
LevelDefinition(
number: 1, // Level number (must match filename)
gravity: Some((2.0, 0.0, 0.0)), // Optional: custom gravity vector (x, y, z)
description: Some("Level design notes and gameplay hints"), // Optional: level documentation
author: Some("[Jane Smith](mailto:jane@example.com)"), // Optional: contributor attribution
matrix: [
// 20 rows of 20 columns each
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
// ... 18 more rows ...
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
],
presentation: Some(( // Optional: per-level texture overrides
level_number: 1,
ground_profile: Some("ground/custom"),
background_profile: None,
sidewall_profile: None,
tint: None,
notes: Some("Custom ground texture"),
)),
)
Fields Reference¶
number: u32— Level index/identifier; used by the loader to findlevel_{:03}.ronnext-level files. Must match the filename (e.g.,number: 1inlevel_001.ron).gravity: Option<(f32, f32, f32)>— Optional gravity override for the level (X, Y, Z). If omitted, the runtime uses the global gravity configuration. During ball respawn, gravity is temporarily set to zero while the paddle grows back to normal size.matrix: Vec<Vec<u8>>— The tile grid, encoded as rows of byte values. The runtime normalizes input to 20×20 usingsrc/level_loader.rs::normalize_matrix_simple(padding/truncating rows or columns as needed).description: Option<String>— Optional level design documentation. Use for design notes, gameplay hints, technical implementation details, or any other information helpful to other developers. Supports multiline strings and special characters.author: Option<String>— Optional contributor attribution. Use plain text names or Markdown link format[Name](url)for email/website attribution. The runtime provides helper methods to extract display names from Markdown links.presentation: Option<LevelTextureSet>— Optional per-level texture overrides for ground, background, and sidewall materials. See “Assigning Per-Level Ground Textures” below.
Grid Coordinates¶
The game uses a 20×20 grid with the following coordinate system:
Origin: Top-left corner is
[0][0]X-axis: Columns (left to right, 0-19)
Z-axis: Rows (top to bottom, 0-19)
Y-axis: Fixed at Y=2.0 (gameplay plane)
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 (columns)
┌────────────────────────────────────────────────────────────┐
0 │ │
1 │ ┌─────────────────────┐ │
2 │ │ BRICKS (20s) │ │
3 │ └─────────────────────┘ │
4 │ │
│ │
12 │ ═ Paddle (2) │
│ │
18 │ ○ Ball (1) │
19 │ │
└────────────────────────────────────────────────────────────┘
(rows)
Gravity Override Examples¶
The gravity field is optional and allows per-level physics customization:
// No gravity override (uses default global gravity)
gravity: None,
// Custom gravity vector
gravity: Some((2.0, 0.0, 0.0)), // Pulls toward +X (right)
gravity: Some((-1.0, 0.0, 1.0)), // Diagonal pull
gravity: Some((0.0, -9.81, 0.0)), // Standard downward gravity
Matrix Cell Values (Tile Semantics)¶
The runtime uses numeric tile values to determine what to spawn at each grid cell:
Matrix Cell Values (Tile Semantics)¶
The runtime uses numeric tile values to determine what to spawn at each grid cell:
Value |
Entity |
Notes |
|---|---|---|
|
Empty |
No entity spawned |
|
Ball |
First occurrence only; additional 1s are ignored. At least one recommended. |
|
Paddle |
First occurrence only; additional 2s are ignored. At least one recommended. |
|
Standard Brick |
Canonical destructible brick type (recommended for new levels) |
|
Legacy Brick |
Standard destructible brick (legacy; prefer |
|
Indestructible Brick |
Collides like a brick but does NOT count toward level completion |
|
Custom Brick Types |
Appearance and behavior determined by texture manifest (if enabled) |
Notes for Designers¶
Prefer
20for standard destructible bricks. Value3is legacy and will be migrated automatically for repository assets.90is reserved for indestructible bricks — they cannot be destroyed but still collide and participate in gameplay.Only the first
2(paddle) and1(ball) in the matrix are used; add at most one of each. If they are absent, the runtime spawns reasonable defaults.The loader will convert input matrices to the expected 20×20 shape; but editing a properly sized matrix makes human editing and visual reasoning easier.
When adding custom brick types (4-89, 91-255), confirm textures are available (when
texture_manifestis enabled) or the default debug material will be used.When adding custom brick types (4-89, 91-255), confirm textures are available (when
texture_manifestis enabled) or the default debug material will be used.
Example: Complete Level¶
LevelDefinition(
number: 1,
gravity: Some((2.0, 0.0, 0.0)),
description: Some("Tutorial level: Learn the basics"),
author: Some("[Tutorial Team](https://github.com/brkrs/levels)"),
matrix: [
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,20,20,20,20,20,20,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,20,20,20,20,20,20,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,20,20,20,20,20,20,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,20,20,20,20,20,20,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
],
presentation: Some((
level_number: 1,
ground_profile: Some("ground/tutorial"),
background_profile: None,
sidewall_profile: None,
tint: None,
notes: Some("Tutorial level ground texture"),
)),
)
Editing and Testing Workflow¶
Editing and Testing Workflow¶
Edit files directly using your text editor - the RON format is human-readable and Git-friendly.
Test a specific level by setting environment variable
BK_LEVELto the level number when running the desktop build:BK_LEVEL=997 cargo run --release
Hot-reload: In-game, press L to cycle through levels quickly for visual verification.
Unit and integration tests exercise level loading and migration tooling. See
tests/for examples.
Assigning Per-Level Ground Textures¶
You can assign unique ground textures to each level automatically or manually:
Automatic Assignment (Recommended)¶
Use the helper script:
python scripts/assign_and_add_ground_profiles.py --mode=all # Reassigns all levels
python scripts/assign_and_add_ground_profiles.py --mode=missing # Only assigns to levels without a ground_profile
This script:
Randomly assigns a unique texture from
assets/textures/background/to each level’s ground plane (as aground_profilein thepresentationfield)Updates
assets/textures/manifest.ronto add any missing ground profiles for the assigned texturesCan be run multiple times to reshuffle textures or fill in missing assignments
Manual Assignment¶
Choose a texture from
assets/textures/background/(e.g.,nsTile1044.png).Add a profile to
assets/textures/manifest.ronif not already present:( id: "ground/nsTile1044", albedo_path: "background/nsTile1044.png", normal_path: None, roughness: 0.9, metallic: 0.0, uv_scale: (4.0, 3.0), uv_offset: (0.0, 0.0), fallback_chain: ["ground/default"], ),
Edit the level file (e.g.,
assets/levels/level_001.ron) and add or update thepresentationfield:LevelDefinition( number: 1, ... presentation: Some(( level_number: 1, ground_profile: Some("ground/nsTile1044"), background_profile: None, sidewall_profile: None, tint: None, notes: Some("Custom ground texture"), )), )
Save and reload the game (or use hot-reload if supported) to see the new ground texture in-game.
See also: assets/textures/README.md for more on texture profiles and manifest editing.
Visual / Texture Mapping¶
If the texture_manifest feature is enabled, a texture registry maps BrickTypeIds (tile values) to visual assets.
See assets/textures/README.md for texture profile names and how to add type variant mappings for new brick types.
Validation¶
Level Validation Rules¶
Matrix size: Must be exactly 20 rows × 20 columns (normalized automatically by the loader)
Level number: Must match the filename (e.g.,
number: 1inlevel_001.ron)Paddle spawn: At least one cell with value
2recommended (fallback spawn position used if absent)Ball spawn: At least one cell with value
1recommended (fallback spawn position used if absent)Cell values: All values must be 0-255 (u8 range)
Common Errors¶
“Matrix must be 20x20”: Check that all rows have exactly 20 elements and there are exactly 20 rows. The loader will normalize mismatched matrices, but editing with the correct size makes debugging easier.
“Invalid cell value”: Only values 0-255 are valid (u8 range). Check for typos or negative values.
“Missing paddle/ball”: The level will still load but will use fallback spawn positions.
For clarity, always include at least one 2 (paddle) and one 1 (ball) in the matrix.
“Failed to parse level”: Check RON syntax - common issues include trailing commas inside arrays, unmatched brackets, or missing commas between array elements.
Common Pitfalls¶
Common Pitfalls¶
Trailing commas: Avoid trailing commas inside numeric arrays as they can make RON parsing ambiguous. Follow the style of existing files in this folder.
Missing spawn points: Don’t rely on the runtime to always find a paddle/ball - give explicit spawn points to avoid surprising fallback behavior.
Custom brick types without textures: When adding custom brick types (>90), confirm textures are available (when
texture_manifestis enabled) or the default debug material will be used.Mismatched level number: Ensure the
numberfield matches the filename number to avoid confusion during level transitions.
Texture Assets¶
Texture asset documentation is consolidated in assets/textures/README.md:
The game uses a texture manifest system (manifest.ron) to define all visual materials for gameplay objects.
Textures can be customized globally or overridden per-level for unique visual themes.
Quick Start¶
Adding a New Texture¶
Place your texture file in
assets/textures/or a subdirectorySupported formats: PNG, KTX2
Recommended naming: descriptive lowercase with underscores (e.g.,
metal_rough.png)
Add a profile entry to
manifest.ron:
(
profiles: [
// ... existing profiles ...
// Your new profile
(
id: "brick/metal", // Unique identifier
albedo_path: "metal_rough.png", // Base color texture
normal_path: Some("metal_normal.png"), // Optional normal map
roughness: 0.8, // 0.0 (smooth) to 1.0 (rough)
metallic: 0.9, // 0.0 (non-metal) to 1.0 (metal)
uv_scale: (1.0, 1.0), // Texture tiling (x, y)
uv_offset: (0.0, 0.0), // Texture offset
fallback_chain: [], // Backup profiles if texture fails
),
],
// ... rest of manifest ...
)
Hot-reload: Save
manifest.ronand the game will reload automatically (no restart needed)
Applying Textures to Objects¶
Default Object Textures¶
Edit the canonical profiles in manifest.ron:
ball/default- Ball appearancepaddle/default- Paddle appearancebrick/default- Default brick appearancesidewall/default- Border wallsground/default- Ground planebackground/default- Background plane
Type-Specific Textures (Brick/Ball Variants)¶
Use type_variants to map gameplay types to visual profiles:
(
profiles: [
// ... profiles ...
],
type_variants: [
// Map brick type 3 to metal texture
(
object_class: Brick,
type_id: 3,
profile_id: "brick/metal",
emissive_color: None,
animation: None,
),
// Map ball type 1 to fire texture
(
object_class: Ball,
type_id: 1,
profile_id: "ball/fire",
emissive_color: Some(Srgba(red: 1.0, green: 0.3, blue: 0.0, alpha: 1.0)),
animation: None,
),
],
// ... rest of manifest ...
)
New type mappings for designers¶
Two new brick indices are introduced for designers:
20— canonical “simple” brick type going forward (legacy index3is still recognized during a compatibility window)90— indestructible brick (designer-visible; will never count toward level completion)
Add profiles for brick/type20 and brick/indestructible in manifest.ron and map them in type_variants to ensure the in-game editor and runtime render the correct visuals for these indices.
Example:
(
// ... profiles ...
type_variants: [
( object_class: Brick, type_id: 20, profile_id: "brick/type20", emissive_color: None, animation: None ),
( object_class: Brick, type_id: 90, profile_id: "brick/indestructible", emissive_color: None, animation: None ),
]
)
Per-Level Texture Overrides¶
Customize ground, background, and sidewall textures for specific levels:
Method 1: In manifest.ron¶
(
profiles: [
// ... profiles ...
],
type_variants: [
// ... variants ...
],
level_overrides: [
(
level_number: 2,
ground_profile: Some("ground/lava"),
background_profile: Some("background/sunset"),
sidewall_profile: Some("sidewall/marble"),
tint: Some(Srgba(red: 1.0, green: 0.8, blue: 0.6, alpha: 1.0)),
notes: Some("Lava-themed level with warm tint"),
),
],
)
Method 2: Inline in level file (assets/levels/level_002.ron)¶
LevelDefinition(
number: 2,
gravity: Some((-1.5, 0.0, 0.0)),
matrix: [
// ... matrix data ...
],
presentation: Some((
level_number: 2,
ground_profile: Some("ground/ice"),
background_profile: None, // Use default
sidewall_profile: None, // Use default
tint: None,
notes: Some("Ice level"),
)),
)
File Structure¶
assets/textures/
├── README.md # This guide
├── manifest.ron # Master texture configuration
├── fallback/ # Placeholder textures (auto-generated)
└── [your textures] # PNG/KTX2 texture files
Texture Manifest¶
The manifest.ron file is the central configuration for all textures in the game.
It defines visual asset profiles, maps gameplay types to textures, and configures per-level overrides.
Manifest Structure¶
// assets/textures/manifest.ron
(
profiles: [
// Visual asset profiles (materials with textures)
(
id: "brick/default",
albedo_path: "debug/four_squares_64.png",
normal_path: Some("debug/cube_normal.png"),
orm_path: None,
emissive_path: Some("test/test_emissive.png"),
depth_path: Some("debug/cube_depth.png"),
roughness: 0.7,
metallic: 0.0,
uv_scale: (1.0, 1.0),
uv_offset: (0.0, 0.0),
depth_scale: 0.2,
fallback_chain: [],
),
// ... more profiles ...
],
type_variants: [
// Map gameplay types to visual profiles
(
object_class: Brick,
type_id: 3,
profile_id: "brick/default",
emissive_color: None,
animation: None,
),
// ... more type mappings ...
],
level_overrides: [
// Per-level texture customizations
(
level_number: 2,
ground_profile: Some("ground/lava"),
background_profile: Some("background/sunset"),
sidewall_profile: Some("sidewall/marble"),
tint: Some(Srgba(red: 1.0, green: 0.8, blue: 0.6, alpha: 1.0)),
notes: Some("Lava-themed level"),
),
// ... more level overrides ...
],
)
Manifest Loading¶
Startup: Manifest is loaded once when the game starts
Hot-Reload: Changes to
manifest.ronare detected and applied automaticallyValidation: Parse errors are logged with line numbers for debugging
Fallback Behavior: If manifest is missing or invalid, game uses hardcoded default materials
Path Resolution¶
All texture paths in the manifest are relative to assets/textures/:
// These are equivalent:
albedo_path: "brick_stone.png" // → assets/textures/brick_stone.png
albedo_path: "materials/brick_stone.png" // → assets/textures/materials/brick_stone.png
Do NOT use absolute paths or ../ navigation - they will fail in WASM builds.
Fallback Textures¶
The fallback/ directory contains default textures used when:
Custom textures are missing or fail to load
A profile’s
fallback_chainis triggeredThe manifest is invalid or absent
Default Fallback Files¶
Generated automatically if missing:
File |
Purpose |
Dimensions |
Format |
|---|---|---|---|
|
Default brick texture |
64×64 |
PNG, sRGB |
|
Default paddle texture |
64×64 |
PNG, sRGB |
|
Default ball texture |
64×64 |
PNG, sRGB |
|
Floor texture |
512×512 |
PNG, sRGB |
|
Wall textures |
512×512 |
PNG, sRGB |
|
Background texture |
1024×1024 |
PNG, sRGB |
Fallback Chain Mechanism¶
Profiles can specify a fallback_chain to gracefully degrade when textures fail:
(
id: "brick/exotic",
albedo_path: "exotic_unavailable.png", // Might not exist
// ... other fields ...
fallback_chain: ["brick/metal", "brick/default"],
)
Resolution order:
Try loading
exotic_unavailable.pngIf that fails, try
brick/metalprofileIf that fails, try
brick/defaultprofileIf all fail, use hardcoded fallback material
Customizing Fallback Textures¶
You can replace the auto-generated fallbacks:
Create custom textures matching the filenames above
Place them in
assets/textures/fallback/Restart the game (fallbacks are loaded at startup, not hot-reloaded)
Recommended: Keep fallbacks simple and low-resolution for fast loading and WASM bundle size.
Texture Formats & Color Channels¶
Supported File Formats¶
PNG (Recommended for Development)¶
Pros: Lossless compression, wide tool support, easy iteration
Cons: Larger file sizes, slower loading than KTX2
Color Depth: 8-bit or 16-bit per channel supported
Alpha Channel: Fully supported (RGBA)
Use Case: Quick prototyping, asset creation, debugging
KTX2 (Recommended for Production)¶
Pros: GPU-native compression, 50-90% smaller than PNG, faster loading
Cons: Requires conversion tools, lossy compression options
Compression: Supports BC7 (high quality), BC3 (legacy), or Basis Universal
Alpha Channel: Fully supported
Use Case: Final builds, WASM deployments, performance-critical scenarios
Conversion: Use
toktxorPVRTexToolto convert PNG → KTX2
Color Channel Meanings by Texture Type¶
Albedo/Base Color Texture (albedo_path)¶
Format: RGB or RGBA
Color Space: sRGB (gamma-corrected)
Channels:
R, G, B: Diffuse surface color (what you see under white light)
Alpha: Transparency (1.0 = opaque, 0.0 = fully transparent)
For breakout game objects, typically use fully opaque (alpha = 1.0)
Alpha < 1.0 enables transparency but may affect rendering order
Best Practices:
Avoid pure white (255,255,255) or pure black (0,0,0) as they can look unrealistic
Use mid-range values (50-200) for most materials
Paint shadows/lighting in normal maps, not albedo
Normal Map Texture (normal_path)¶
Format: RGB (alpha channel ignored)
Color Space: Linear (do NOT use sRGB for normals)
Channels:
R (Red): X-axis normal component (left ↔ right surface angle)
G (Green): Y-axis normal component (down ↔ up surface angle)
B (Blue): Z-axis normal component (into ↔ out of surface)
Color Interpretation:
RGB (128, 128, 255) = flat surface pointing toward camera (normal)
RGB (255, 128, 128) = surface angled right
RGB (0, 128, 128) = surface angled left
RGB (128, 255, 128) = surface angled up
RGB (128, 0, 128) = surface angled down
Common Mistakes:
❌ Using a bump/height map instead of a normal map
❌ Saving as sRGB instead of linear color space
❌ Inverting Y-axis (use OpenGL format, not DirectX)
Conversion: GIMP → Filters → Generic → Normal Map (from height map)
ORM Texture (orm_path)¶
Format: RGB (Occlusion/Roughness/Metallic packed)
Color Space: Linear
Channels:
R (Red): Ambient Occlusion
255 (white) = fully lit, no occlusion
0 (black) = fully shadowed (crevices, cracks)
Controls subtle ambient shadows in recessed areas
G (Green): Roughness
255 (white) = completely rough/matte (1.0)
0 (black) = perfectly smooth/mirror (0.0)
Overrides profile’s
roughnessscalar value
B (Blue): Metallic
255 (white) = fully metallic (1.0)
0 (black) = non-metallic/dielectric (0.0)
Overrides profile’s
metallicscalar value
Alpha: Ignored
Optimization: Packing 3 grayscale maps into one RGB texture saves memory and texture slots
Emissive Texture (emissive_path)¶
Format: RGB or RGBA
Color Space: sRGB
Channels:
R, G, B: Emitted light color and intensity
Values > 128 (0.5 linear) emit noticeable light
Values > 200 (0.8 linear) emit strong light
Pure black (0,0,0) = no emission
Alpha: Can mask emission regions (0 = no emission, 255 = full emission)
Combination: Multiplied with
emissive_colorfrom TypeVariantDefinition if both setExamples:
RGB (255, 200, 0) = bright yellow-orange glow
RGB (0, 255, 255) = cyan neon glow
RGB (128, 0, 0) = subtle red glow
Depth/Parallax Map Texture (depth_path)¶
Format: Grayscale (R channel used, GB ignored) or single-channel
Color Space: Linear
Channel Interpretation:
White (255): Raised/protruding surface areas
Black (0): Recessed/indented surface areas
Mid-gray (128): Neutral height (no displacement)
Usage: Creates illusion of depth on flat surfaces via parallax occlusion mapping
Scaling: Actual depth effect controlled by
depth_scaleparameter (0.0-1.0 typical)Performance: Depth mapping is more expensive than normal mapping; use sparingly
Alpha/Transparency Handling¶
Opaque Materials (Default)¶
Set alpha = 1.0 (255) in albedo texture
Faster rendering (no transparency sorting needed)
Recommended for all breakout game objects (bricks, paddle, ball)
Transparent Materials¶
Set alpha < 1.0 in albedo texture
Enables alpha blending but may cause:
Rendering order issues (objects drawn back-to-front)
Performance overhead
Depth sorting artifacts
Use only when necessary (e.g., glass bricks, particle effects)
Alpha Masking (Binary Transparency)¶
Use alpha = 0.0 or 1.0 (no intermediate values)
Enables cutout/clip effects (e.g., chain-link fence pattern)
Better performance than alpha blending
Set alpha threshold in material if needed
Color Space Summary¶
Texture Type |
Color Space |
Why |
|---|---|---|
Albedo |
sRGB |
Matches how artists paint and how displays show color |
Normal Map |
Linear |
Mathematical vectors; sRGB gamma breaks calculations |
ORM |
Linear |
Physical properties; gamma correction distorts values |
Emissive |
sRGB |
Artist-friendly color specification |
Depth |
Linear |
Height values; gamma correction distorts displacement |
Important: Most image editors save as sRGB by default. For normal/ORM/depth maps, disable sRGB or use “Save as Linear” if available.
File Naming Conventions¶
Recommended naming scheme for texture sets:
material_name_albedo.png
material_name_normal.png
material_name_orm.png
material_name_emissive.png
material_name_depth.png
Example:
brick_stone_albedo.png
brick_stone_normal.png
brick_stone_orm.png
Manifest Schema Reference¶
VisualAssetProfile¶
Defines a complete material profile.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
String |
required |
Unique identifier (e.g., “brick/metal”) |
|
String |
required |
Path to base color texture |
|
|
|
Optional normal map for surface detail. Important: Must be a proper normal map (RGB values representing XYZ normals in tangent space), not a bump map. If you have a grayscale bump map, convert it to a normal map using image editing software (e.g., GIMP: Filters → Generic → Normal Map). Bevy does not automatically convert bump maps to normal maps. |
|
|
|
Optional ORM (Occlusion/Roughness/Metallic) packed texture. Red channel = occlusion, Green channel = roughness, Blue channel = metallic. When provided, overrides separate |
|
|
|
Optional emissive texture for self-illuminating surfaces. RGB values define the emitted light color and intensity. Combined with |
|
|
|
Optional depth/parallax mapping texture (height map). Grayscale values define surface height for parallax occlusion mapping, creating an illusion of depth on flat surfaces. White = raised areas, Black = recessed areas. Requires |
|
f32 |
|
Surface roughness (0.0 = mirror, 1.0 = matte). Overridden by |
|
f32 |
|
Metallic property (0.0 = non-metal/dielectric, 1.0 = fully metal). Controls how reflective and mirror-like a surface appears. Combined with |
|
(f32, f32) |
|
Texture tiling factors (x, y) |
|
(f32, f32) |
|
Texture offset (x, y) |
|
f32 |
|
Depth intensity multiplier for parallax mapping when |
|
|
|
Backup profile IDs if texture fails to load |
TypeVariantDefinition¶
Maps gameplay type IDs to visual profiles.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
Enum |
required |
|
|
u8 |
required |
Gameplay type ID (3+ for bricks) |
|
String |
required |
Reference to |
|
|
|
Self-illumination color |
|
|
|
Future: animation effects |
LevelTextureSet¶
Per-level material overrides for environmental objects.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
u32 |
required |
Level number to override |
|
|
|
Ground plane texture profile |
|
|
|
Background plane texture profile |
|
|
|
Border wall texture profile |
|
|
|
RGBA color multiplier for level mood |
|
|
|
Designer notes/description |
Common Workflows¶
Creating a Themed Level¶
Create custom texture profiles for ground, background, sidewalls
Add profiles to
manifest.ronReference them in level_overrides or inline in the level file
Optionally add a tint for color mood adjustment
Test in-game with hot-reload
Adding Brick Variety¶
Create texture files for each brick type (type 3, 4, 5, etc.)
Add VisualAssetProfile for each texture
Add TypeVariantDefinition entries mapping type IDs to profiles
Place brick types in level matrix (values 3+)
Test spawn behavior
Testing Textures¶
Hot-reload: Edit
manifest.ronwhile game is runningChanges apply automatically to current level
No restart required
Level preview: Press L to cycle through levels quickly
Visual verification:
Check all object types render correctly
Verify tiling/offset adjustments
Confirm fallbacks work when textures missing
Troubleshooting¶
Texture Not Appearing¶
Check file path: Paths in manifest are relative to
assets/textures/Check format: Only PNG and KTX2 supported
Check logs: Look for warnings about missing files
Verify profile ID: Ensure no typos in references
Fallback Material Showing¶
If you see default gray/colored materials instead of textures:
Check
manifest.ronfor parse errors (logs show line numbers)Verify profile ID matches between definition and usage
Ensure texture file exists at specified path
Check fallback_chain for backup options
Hot-Reload Not Working¶
Save
manifest.ronexplicitlyCheck for syntax errors in RON format
Restart game if asset server is stuck
Best Practices¶
Texture Creation¶
Resolution: 512x512 or 1024x1024 for most objects
Bricks/Balls: 512x512 sufficient
Ground/Background: 1024x1024 or 2048x2048 for quality
Normal/ORM maps: Can be half resolution of albedo (256x256 for 512x512 albedo)
Format:
PNG for development/iteration (lossless, easy editing)
KTX2 for production builds (50-90% size reduction, faster loading)
See “Texture Formats & Color Channels” section for detailed format guidance
Color Space:
Albedo/Emissive: Save as sRGB
Normal/ORM/Depth: Save as Linear (disable gamma correction)
Check your image editor’s export settings
Compression:
PNG: Keep under 1MB per file for quick loading
KTX2: Use BC7 compression for high quality, BC3 for legacy support
Alpha Channel:
Use fully opaque (alpha=1.0) for game objects unless transparency needed
Binary alpha masking (0.0 or 1.0 only) is faster than gradual transparency
UV Mapping:
Design textures to tile seamlessly for
uv_scale > 1.0Test tiling by setting uv_scale to (2.0, 2.0) and checking for seams
Power-of-2 resolutions (512, 1024, 2048) optimize GPU memory
Channel Usage:
Don’t paint lighting/shadows in albedo - use normal maps instead
ORM packing saves memory: combine occlusion, roughness, metallic into one RGB texture
Depth maps are expensive - use only when parallax effect is essential
Manifest Organization¶
Group related profiles together with comments
Use consistent naming conventions:
object_class/variantDocument complex fallback chains
Add notes to level overrides explaining theme
Performance¶
Reuse profiles across levels when possible
Use texture atlases for small repeated patterns
Prefer lower-resolution textures for background elements
Monitor WASM builds for asset size (use KTX2 compression)
Advanced Features¶
Fallback Chains¶
Create resilient material loading with fallback chains:
(
id: "brick/exotic",
albedo_path: "exotic_unavailable.png",
// ... other fields ...
fallback_chain: ["brick/metal", "brick/default"],
)
If exotic_unavailable.png fails, tries brick/metal, then brick/default.
Emissive Materials¶
Add glow effects to bricks or balls:
(
object_class: Brick,
type_id: 5,
profile_id: "brick/glowing",
emissive_color: Some(Srgba(red: 0.0, green: 1.0, blue: 0.8, alpha: 1.0)),
animation: None,
)
Tint Modifiers¶
Create color variations without new textures:
(
level_number: 3,
ground_profile: Some("ground/default"),
background_profile: None,
sidewall_profile: None,
tint: Some(Srgba(red: 0.8, green: 0.8, blue: 1.2, alpha: 1.0)), // Blueish tint
notes: Some("Moonlight theme"),
)
Contract API (For Tooling)¶
External tools can interact with the texture system via events:
Preview Asset (Temporary Override)¶
Send PreviewVisualAsset event with a profile to test textures without editing manifest:
PreviewVisualAsset {
profile: VisualAssetProfile {
id: "test/preview".to_string(),
albedo_path: "preview_texture.png".to_string(),
// ... other fields ...
},
persist: false, // Don't save to manifest
}
This enables external editors to preview textures in real-time.
Examples¶
Complete Manifest Example¶
(
profiles: [
// Canonical defaults
(
id: "ball/default",
albedo_path: "ball_default.png",
normal_path: None,
roughness: 0.3,
metallic: 0.0,
uv_scale: (1.0, 1.0),
uv_offset: (0.0, 0.0),
fallback_chain: [],
),
// Custom variants
(
id: "brick/wood",
albedo_path: "wood_planks.png",
normal_path: Some("wood_normal.png"),
roughness: 0.9,
metallic: 0.0,
uv_scale: (2.0, 2.0),
uv_offset: (0.0, 0.0),
fallback_chain: ["brick/default"],
),
],
type_variants: [
(
object_class: Brick,
type_id: 3,
profile_id: "brick/wood",
emissive_color: None,
animation: None,
),
],
level_overrides: [
(
level_number: 2,
ground_profile: Some("ground/grass"),
background_profile: Some("background/sky"),
sidewall_profile: Some("sidewall/wood"),
tint: Some(Srgba(red: 1.0, green: 1.0, blue: 0.9, alpha: 1.0)),
notes: Some("Outdoor garden theme"),
),
],
)
Getting Help¶
Check game logs for detailed error messages
Verify RON syntax with online validators
Review existing profiles in manifest for examples
Test changes incrementally with hot-reload
Audio Assets¶
Audio asset documentation is consolidated in assets/audio/README.md:
Overview¶
The game uses an audio manifest system (manifest.ron) to map gameplay events to audio files.
Sound effects are triggered automatically by the audio system in response to game events.
File Structure¶
assets/audio/
├── README.md # This guide
├── manifest.ron # Audio configuration mapping events to files
└── [your audio files] # OGG audio files
Supported Audio Format¶
OGG Vorbis is the only supported audio format:
Format:
.ogg(Vorbis codec)Why OGG:
Excellent compression (smaller than WAV, similar to MP3)
Open-source and royalty-free
Well-supported by Bevy audio system
Works in WASM builds
Recommended Settings:
Sample rate: 44.1 kHz or 48 kHz
Bit rate: 128-192 kbps for sound effects
Channels: Mono for most sound effects (stereo for music/ambience)
Conversion: Use tools like Audacity or ffmpeg to convert from WAV/MP3 to OGG
Adding Audio Files¶
Step 1: Prepare Your Audio File¶
Create or obtain your audio file in a supported format (WAV, MP3, FLAC, etc.)
Convert to OGG Vorbis:
# Using ffmpeg ffmpeg -i input.wav -c:a libvorbis -q:a 5 output.ogg # Using Audacity: File → Export → Export as OGG Vorbis
Use descriptive filenames:
brick_destroy.ogg,level_complete.ogg,paddle_hit_metal.oggPlace the file in
assets/audio/directory
Step 2: Update the Audio Manifest¶
Edit manifest.ron to map the sound effect to its filename:
AudioManifest(
sounds: {
BrickDestroy: "brick_destroy.ogg",
MultiHitImpact: "multi_hit_impact.ogg",
WallBounce: "wall_bounce.ogg",
PaddleHit: "paddle_hit.ogg",
PaddleWallHit: "paddle_wall_hit.ogg",
PaddleBrickHit: "paddle_brick_hit.ogg",
LevelStart: "level_start.ogg",
LevelComplete: "level_complete.ogg",
UiBeep: "cheat_mode_toggle.ogg",
// Add your new sound here:
NewSoundType: "new_sound.ogg",
}
)
Key Requirements:
The key (e.g.,
BrickDestroy) must match a validSoundTypeenum variant in the codeThe value is the filename of your audio file (relative to
assets/audio/)No duplicate keys allowed
Filenames are case-sensitive
Step 3: Test Your Audio¶
Run the game:
cargo runTrigger the event that should play your sound (e.g., destroy a brick for
BrickDestroy)Check logs: If the sound doesn’t play, check console for warnings about missing files
Supported Sound Types¶
The following sound types are currently supported (defined in src/systems/audio.rs):
Sound Type |
Event Trigger |
Notes |
|---|---|---|
|
Brick destroyed by ball |
Standard brick destruction |
|
Multi-hit brick damaged |
Brick hit but not destroyed |
|
Ball bounces off wall |
Border collision |
|
Ball bounces off paddle |
Paddle collision |
|
Paddle collides with wall |
Paddle movement limit |
|
Paddle collides with brick |
Rare edge case |
|
New level begins |
Level initialization |
|
All bricks cleared |
Level completion |
|
UI interaction blocked |
Error/feedback sound |
Adding New Sound Types: To add a new sound type, you must update the SoundType enum in src/systems/audio.rs and trigger the corresponding audio signal in the relevant game system.
Audio Manifest Reference¶
Structure¶
AudioManifest(
sounds: {
<SoundType>: "<filename.ogg>",
// ... more mappings
}
)
Fields¶
sounds: A map (dictionary) fromSoundTypeenum variants to audio filenamesKeys: Must be valid Rust identifiers matching
SoundTypeenum variantsValues: Filenames (strings) relative to
assets/audio/directory
Example: Complete Manifest¶
AudioManifest(
sounds: {
// Collision sounds
BrickDestroy: "impact_brick_shatter.ogg",
MultiHitImpact: "impact_brick_thud.ogg",
WallBounce: "impact_wall_bounce.ogg",
PaddleHit: "impact_paddle_hit.ogg",
// Paddle edge cases
PaddleWallHit: "paddle_wall_collision.ogg",
PaddleBrickHit: "paddle_brick_collision.ogg",
// Level events
LevelStart: "level_start_chime.ogg",
LevelComplete: "level_complete_fanfare.ogg",
// UI feedback
UiBeep: "ui_error_beep.ogg",
}
)
Best Practices¶
Audio File Creation¶
Keep files short: Sound effects should be 0.5-2 seconds for most impacts
Normalize volume: Aim for consistent loudness across all sound effects
Remove silence: Trim leading/trailing silence to ensure immediate playback
Avoid clipping: Keep peak levels below 0 dB to prevent distortion
Test in-game: Audio that sounds good in isolation may not fit gameplay context
File Organization¶
Use descriptive names:
brick_heavy_impact.oggis better thansound1.oggGroup related sounds: Consider prefixes like
impact_,ui_,level_Keep file sizes small: Compress to ~128 kbps for most effects (good quality, small size)
Optimize for WASM: Total audio assets affect web build loading time
Manifest Maintenance¶
Keep alphabetical: Sort sound type keys for easy reference
Comment variations: Add notes if using different sounds for similar events
Version control: Track manifest changes to understand audio design evolution
Troubleshooting¶
Sound Not Playing¶
Check filename: Ensure filename in manifest matches actual file (case-sensitive)
Verify format: Confirm file is valid OGG Vorbis (use
filecommand or media player)Check logs: Look for asset loading warnings in console output
Test file: Play the OGG file directly in a media player to confirm it works
Verify trigger: Ensure the game event that should trigger the sound is actually occurring
Sound Plays But Is Wrong¶
Check mapping: Verify correct sound is mapped to the event in manifest
Test isolation: Temporarily map all sounds to one file to isolate the issue
Volume check: Ensure audio file isn’t silent or too quiet
Manifest Parse Errors¶
Check RON syntax: Ensure proper RON format (commas between entries, colon between key/value)
Verify sound types: All keys must match exact
SoundTypeenum variant namesCheck for typos: Sound type names are case-sensitive (
BrickDestroynotbrickdestroy)Look for duplicates: Each sound type can only appear once in the map
Common Errors¶
“File not found”: Filename in manifest doesn’t match actual file. Check spelling and case.
“Unsupported format”: File is not OGG Vorbis. Convert using ffmpeg or Audacity.
“Unknown sound type”: Key in manifest doesn’t match any SoundType enum variant.
Check spelling and capitalization.
Audio Configuration¶
The game provides global audio configuration in config/audio.ron:
AudioConfig(
master_volume: 1.0, // Overall volume (0.0-1.0)
effects_volume: 0.8, // Sound effects volume multiplier
music_volume: 0.6, // Music volume multiplier (when implemented)
)
See config/audio.ron for current settings and documentation.