| Crates.io | bevy_fog_of_war |
| lib.rs | bevy_fog_of_war |
| version | 0.3.0 |
| created_at | 2025-01-20 11:13:00.60587+00 |
| updated_at | 2025-07-22 05:48:15.788949+00 |
| description | A 2D fog of war plugin for Bevy |
| homepage | https://crates.io/crates/bevy_fog_of_war |
| repository | https://github.com/foxzool/bevy_fog_of_war |
| max_upload_size | |
| id | 1523798 |
| size | 1,521,676 |
中文文档 | English
A fog of war implementation for the Bevy game engine. This crate provides a simple way to add fog of war effects to your 2D games, with support for multiple light sources, smooth transitions, and explored area tracking.
FogMapSettings resource.To use bevy_fog_of_war in your project, follow these steps:
(You can import most commonly used items via use bevy_fog_of_war::prelude::*;)
Add the plugin to your app:
Add the FogOfWarPlugin to your Bevy App:
use bevy::prelude::*;
use bevy_fog_of_war::FogOfWarPlugin;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(FogOfWarPlugin) // Add the fog of war plugin
// ... other setup ...
.run();
}
Add FogOfWarCamera to your camera:
The plugin needs to know which camera is used for the fog of war effect. Add the FogOfWarCamera component to your
main 2D camera entity.
use bevy::prelude::*;
use bevy_fog_of_war::prelude::FogOfWarCamera;
fn setup(mut commands: Commands) {
commands.spawn((Camera2d, FogOfWarCamera));
}
Add VisionSource to entities:
Entities that should reveal the map need a VisionSource component. You can create different shapes for the vision
area, such as circles, squares, or cones.
use bevy::prelude::*;
use bevy_fog_of_war::prelude::VisionSource;
fn setup_entities(mut commands: Commands) {
// Spawn an entity with a circular vision source
commands.spawn((Transform::from_xyz(0.0, 0.0, 0.0), VisionSource::circle(200.0)));
// Spawn another entity with a square vision source
commands.spawn((Transform::from_xyz(100.0, 50.0, 0.0), VisionSource::square(150.0)));
}
(Optional) Add Capturable to entities:
If you have entities that should only become visible when a VisionSource overlaps them (and remain visible once
discovered, depending on your fog settings), add the Capturable component to them.
use bevy::prelude::*;
use bevy_fog_of_war::prelude::Capturable;
fn setup_objects(mut commands: Commands) {
commands.spawn((
Sprite {
color: Color::srgb(0.2, 0.8, 0.8),
custom_size: Some(Vec2::new(60.0, 60.0)),
..default()
},
Capturable, // This entity will be revealed by vision sources
));
}
Customize FogMapSettings (Optional):
You can customize the fog of war behavior by inserting a FogMapSettings resource. Here's an example of how to
configure it:
use bevy::prelude::*;
use bevy_fog_of_war::prelude::*;
use bevy::render::render_resource::TextureFormat;
fn setup_fog_settings(mut commands: Commands) {
commands.insert_resource(FogMapSettings {
enabled: true, // Enable/disable fog of war effect
chunk_size: UVec2::new(256, 256), // Size of each chunk in world units
texture_resolution_per_chunk: UVec2::new(512, 512), // Texture resolution per chunk
fog_color_unexplored: Color::rgba(0.1, 0.1, 0.1, 0.9), // Color for unexplored areas
fog_color_explored: Color::rgba(0.3, 0.3, 0.3, 0.5), // Color for explored but not visible areas
vision_clear_color: Color::NONE, // Clear color for visible areas (usually transparent)
fog_texture_format: TextureFormat::R8Unorm, // Texture format for fog
snapshot_texture_format: TextureFormat::R8Unorm // Texture format for snapshots
});
}
Then add the system to your app:
.add_systems(Startup, setup_fog_settings)
Check the examples directory for more detailed usage scenarios, including dynamic vision sources and different vision shapes.
You can programmatically reset all fog of war data (explored areas, visibility states, and texture data) without despawning entities or cameras. This is useful when changing scenes or restarting levels:
use bevy::prelude::*;
use bevy_fog_of_war::prelude::*;
fn reset_on_keypress(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut reset_events: EventWriter<ResetFogOfWar>,
) {
if keyboard_input.just_pressed(KeyCode::KeyR) {
// Reset all fog of war data
reset_events.write(ResetFogOfWar);
}
}
// Add this system to your app
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(FogOfWarPlugin)
.add_systems(Update, reset_on_keypress)
.run();
}
The plugin provides events to notify you when reset operations complete:
use bevy::prelude::*;
use bevy_fog_of_war::prelude::*;
fn handle_fog_reset_events(
mut success_events: EventReader<FogResetSuccess>,
mut failure_events: EventReader<FogResetFailed>,
) {
for event in success_events.read() {
info!("✅ Fog reset completed successfully! Duration: {}ms, Chunks reset: {}",
event.duration_ms, event.chunks_reset);
}
for event in failure_events.read() {
error!("❌ Fog reset failed! Duration: {}ms, Error: {}",
event.duration_ms, event.error);
}
}
// Add the event handler to your app
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(FogOfWarPlugin)
.add_systems(Update, (reset_on_keypress, handle_fog_reset_events))
.run();
}
The reset functionality:
Check the playground.rs and simple_2d.rs examples for complete
demonstrations.
The plugin supports multiple serialization formats for optimal performance and compatibility. Choose the format that best suits your needs:
| Format | Size | Speed | Compatibility | Use Case |
|---|---|---|---|---|
| JSON | Largest | Slow | Universal | Debug, web APIs, human-readable |
| MessagePack | Small | Fast | Cross-language | Network, storage-efficient |
| bincode | Smallest* | Fastest | Rust-only | Local saves, performance-critical |
*When combined with compression
Add the desired formats to your Cargo.toml:
[dependencies]
# Default includes bincode for optimal performance with binary data
bevy_fog_of_war = "0.2.1"
# Add additional formats:
bevy_fog_of_war = { version = "0.2.1", features = ["format-messagepack"] }
# Or enable everything:
bevy_fog_of_war = { version = "0.2.1", features = ["all-formats"] }
# Compression support:
bevy_fog_of_war = { version = "0.2.1", features = ["all-formats", "all-compression"] }
Based on benchmarks with typical fog data:
Format | File Size | Save Time | Load Time
--------------------|-----------|-----------|----------
JSON | 100% | 100% | 100%
JSON + gzip | 65% | 120% | 110%
MessagePack | 45% | 30% | 40%
MessagePack + LZ4 | 35% | 35% | 45%
bincode | 30% | 15% | 20%
bincode + Zstandard | 25% | 20% | 25%
use bevy_fog_of_war::prelude::*;
// Request save with specific format
save_events.write(SaveFogOfWarRequest {
include_texture_data: true,
format: Some(SerializationFormat::Json), // or MessagePack, Bincode
});
// Load by reading file as bytes
let data = std::fs::read("fog_save.msgpack") ?;
load_events.write(LoadFogOfWarRequest {
data,
format: None, // Auto-detect format
});
// The plugin automatically selects optimal format based on available features
// Priority: bincode > messagepack > json
save_events.write(SaveFogOfWarRequest {
include_texture_data: true,
format: None, // Uses best available format
});
// Loading automatically detects format from file content
let data = std::fs::read("fog_save.bincode") ?;
load_events.write(LoadFogOfWarRequest {
data,
format: None, // Auto-detects bincode format
});
The plugin automatically tries formats in optimal order:
// Plugin tries formats in optimal order automatically
// Priority: bincode.zst → msgpack.lz4 → bincode → msgpack → json
let data = std::fs::read("fog_save.bincode") ?;
load_events.write(LoadFogOfWarRequest { data, format: None });
The plugin supports saving and loading fog of war data, allowing you to persist explored areas across game sessions or per character/save file.
use bevy::prelude::*;
use bevy_fog_of_war::prelude::*;
fn save_fog_data(
mut save_events: EventWriter<SaveFogOfWarRequest>,
) {
// Request to save fog data
save_events.write(SaveFogOfWarRequest {
include_texture_data: true, // Include texture data for partial visibility
format: None, // Use default format (prioritizes bincode -> messagepack -> json)
});
}
fn handle_save_complete(
mut events: EventReader<FogOfWarSaved>,
) {
for event in events.read() {
let filename = match event.format {
SerializationFormat::Json => "fog_save.json",
#[cfg(feature = "format-messagepack")]
SerializationFormat::MessagePack => "fog_save.msgpack",
#[cfg(feature = "format-bincode")]
SerializationFormat::Bincode => "fog_save.bincode",
};
match std::fs::write(filename, &event.data) {
Ok(_) => {
println!("✅ Saved {} chunks to '{}' - Format: {:?}",
event.chunk_count, filename, event.format);
}
Err(e) => {
eprintln!("❌ Failed to save fog data to '{}': {}", filename, e);
}
}
}
}
use bevy::prelude::*;
use bevy_fog_of_war::prelude::*;
fn load_fog_data(
mut load_events: EventWriter<LoadFogOfWarRequest>,
) {
// Try loading different format files in priority order
let format_priorities = [
#[cfg(all(feature = "format-bincode", feature = "compression-zstd"))]
"fog_save.bincode.zst",
#[cfg(all(feature = "format-messagepack", feature = "compression-lz4"))]
"fog_save.msgpack.lz4",
#[cfg(feature = "format-bincode")]
"fog_save.bincode",
#[cfg(feature = "format-messagepack")]
"fog_save.msgpack",
"fog_save.json",
];
let mut loaded = false;
for filename in format_priorities {
// Read file as bytes directly
match std::fs::read(filename) {
Ok(data) => {
println!("✅ Loading fog data from '{}'", filename);
load_events.write(LoadFogOfWarRequest {
data,
format: None, // Auto-detect format from data content
});
loaded = true;
break;
}
Err(_) => {
// File doesn't exist or failed to load, try next format
continue;
}
}
}
if !loaded {
eprintln!("⚠️ No save file found");
}
}
fn handle_load_complete(
mut events: EventReader<FogOfWarLoaded>,
) {
for event in events.read() {
println!("Loaded {} chunks", event.chunk_count);
if !event.warnings.is_empty() {
println!("Warnings: {:?}", event.warnings);
}
}
}
The persistence system is designed to work with server-side storage:
// Example server integration with binary data
async fn save_to_server(fog_data: &[u8], format: SerializationFormat) {
// Send binary fog data to your game server
let response = reqwest::Client::new()
.post("https://api.yourgame.com/fog-of-war/save")
.header("Content-Type", "application/octet-stream")
.header("X-Fog-Format", format!("{:?}", format))
.body(fog_data.to_vec())
.send()
.await
.unwrap();
}
async fn load_from_server() -> Vec<u8> {
// Fetch binary fog data from your game server
let response = reqwest::Client::new()
.get("https://api.yourgame.com/fog-of-war")
.send()
.await
.unwrap();
response.bytes().await.unwrap().to_vec()
}
See the playground.rs example for a complete demonstration of saving and loading fog data.
| Bevy Version | Plugin Version |
|---|---|
| 0.16 | 0.2, 0.3 |
| 0.15 | 0.1.0 |
This project is licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.