//! This example demonstrates how to load world data from files and then dynamically1//! apply that data to entities in your Bevy `World`. This includes spawning new2//! entities and applying updates to existing ones. World serialization in Bevy3//! serializes and deserializes `Components` and `Resources` so that you can easily4//! store, load, and manipulate data outside of a purely code-driven context.5//!6//! This example also shows how to do the following:7//! * Register your custom types for reflection, which allows them to be serialized,8//! deserialized, and manipulated dynamically.9//! * Skip serialization of fields you don't want stored in your world files (like10//! runtime values that should always be computed dynamically).11//! * Save a new world to disk to show how it can be updated compared to the original12//! world file (and how that updated world file might then be used later on).13//!14//! The example proceeds by creating components and resources, registering their types,15//! loading a world from a file, logging when changes are detected, and finally saving16//! a new world file to disk. This is useful for anyone wanting to see how to integrate17//! file-based ECS workflows into their Bevy projects.18//!19//! # Note on working with files20//!21//! The saving behavior uses the standard filesystem APIs, which are blocking, so it22//! utilizes a thread pool (`IoTaskPool`) to avoid stalling the main thread. This23//! won't work on WASM because WASM typically doesn't have direct filesystem access.24//!2526use bevy::{asset::LoadState, prelude::*, tasks::IoTaskPool};2728use core::time::Duration;29use std::{fs::File, io::Write};3031/// The entry point of our Bevy app.32///33/// Sets up default plugins, registers all necessary component/resource types34/// for serialization/reflection, and runs the various systems in the correct schedule.35fn main() {36App::new()37.add_plugins(DefaultPlugins)38.add_systems(39Startup,40(save_world_system, load_world_system, infotext_system),41)42.add_systems(Update, (log_system, panic_on_fail))43.run();44}4546/// # Components, Resources, and Reflection47///48/// Below are some simple examples of how to define your own Bevy `Component` types49/// and `Resource` types so that they can be properly reflected, serialized, and50/// deserialized. The `#[derive(Reflect)]` macro enables Bevy's reflection features,51/// and we add component-specific reflection by using `#[reflect(Component)]`.52/// We also illustrate how to skip serializing fields and how `FromWorld` can help53/// create runtime-initialized data.54///55/// A sample component that is fully serializable.56///57/// This component has public `x` and `y` fields that will be included in58/// the world files. Notice how it derives `Default`, `Reflect`, and declares59/// itself as a reflected component with `#[reflect(Component)]`.60#[derive(Component, Reflect, Default)]61#[reflect(Component)] // this tells the reflect derive to also reflect component behaviors62struct ComponentA {63/// An example `f32` field64pub x: f32,65/// Another example `f32` field66pub y: f32,67}6869/// A sample component that includes both serializable and non-serializable fields.70///71/// This is useful for skipping serialization of runtime data or fields you72/// don't want written to world files.73#[derive(Component, Reflect)]74#[reflect(Component)]75struct ComponentB {76/// A string field that will be serialized.77pub value: String,78/// A `Duration` field that should never be serialized to the world file, so we skip it.79#[reflect(skip_serializing)]80pub _time_since_startup: Duration,81}8283/// This implements `FromWorld` for `ComponentB`, letting us initialize runtime fields84/// by accessing the current ECS resources. In this case, we acquire the `Time` resource85/// and store the current elapsed time.86impl FromWorld for ComponentB {87fn from_world(world: &mut World) -> Self {88let time = world.resource::<Time>();89ComponentB {90_time_since_startup: time.elapsed(),91value: "Default Value".to_string(),92}93}94}9596/// A simple resource that also derives `Reflect`, allowing it to be stored in world files.97///98/// Just like a component, you can skip serializing fields or implement `FromWorld` if needed.99#[derive(Resource, Reflect, Default)]100#[reflect(Resource)]101struct ResourceA {102/// This resource tracks a `score` value.103pub score: u32,104}105106/// # World File Paths107///108/// `WORLD_FILE_PATH` points to the original world file that we'll be loading.109/// `NEW_WORLD_FILE_PATH` points to the new world file that we'll be creating110/// (and demonstrating how to serialize to disk).111///112/// The initial world file will be loaded below and not change when the world is saved.113const WORLD_FILE_PATH: &str = "serialized_worlds/load_scene_example.scn.ron";114115/// The new, updated world data will be saved here so that you can see the changes.116const NEW_WORLD_FILE_PATH: &str = "serialized_worlds/load_scene_example-new.scn.ron";117118/// Loads a world from an asset file and spawns it in the current world.119///120/// Spawning a `DynamicWorldRoot` creates a new parent entity, which then spawns new121/// instances of the world's entities as its children. If you modify the122/// `WORLD_FILE_PATH` file, or if you enable file watching, you can see123/// changes reflected immediately.124fn load_world_system(mut commands: Commands, asset_server: Res<AssetServer>) {125commands.spawn(DynamicWorldRoot(asset_server.load(WORLD_FILE_PATH)));126commands.spawn((127Camera3d::default(),128Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::new(0.0, 0.25, 0.0), Vec3::Y),129));130commands.spawn((131DirectionalLight::default(),132Transform::default().looking_to(Vec3::new(0.0, -1.0, -1.0), Vec3::Y),133));134}135136/// Logs changes made to `ComponentA` entities, and also checks whether `ResourceA`137/// has been recently added.138///139/// Any time a `ComponentA` is modified, that change will appear here. This system140/// demonstrates how you might detect and handle world updates at runtime.141fn log_system(142query: Query<(Entity, &ComponentA), Changed<ComponentA>>,143res: Option<Res<ResourceA>>,144) {145for (entity, component_a) in &query {146info!(" Entity({})", entity.index());147info!(148" ComponentA: {{ x: {} y: {} }}\n",149component_a.x, component_a.y150);151}152if let Some(res) = res153&& res.is_added()154{155info!(" New ResourceA: {{ score: {} }}\n", res.score);156}157}158159/// Demonstrates how to create a new world from scratch, populate it with data,160/// and then serialize it to a file. The new file is written to `NEW_WORLD_FILE_PATH`.161///162/// This system creates a fresh world, duplicates the type registry so that our163/// custom component types are recognized, spawns some sample entities and resources,164/// and then serializes the resulting dynamic world.165fn save_world_system(world: &mut World) {166let asset_server = world.resource::<AssetServer>().clone();167// The `TypeRegistry` resource contains information about all registered types (including components).168// This is used to construct worlds, so we'll want to ensure that we use the registry from the169// main world. To do this, we can simply clone the `AppTypeRegistry` resource.170let type_registry = world.resource::<AppTypeRegistry>().clone();171172// Any ECS World can be serialized.173// For demonstration purposes, we'll create a new one.174let mut scene_world = World::new();175176let mut component_b = ComponentB::from_world(world);177component_b.value = "hello".to_string();178scene_world.spawn((179component_b,180ComponentA { x: 1.0, y: 2.0 },181Transform::IDENTITY,182Name::new("joe"),183WorldAssetRoot(asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0")),184));185scene_world.spawn(ComponentA { x: 3.0, y: 4.0 });186scene_world.insert_resource(ResourceA { score: 1 });187188// With our sample world ready to go, we can now create a DynamicWorld from it.189// For simplicity, we will create our scene using DynamicWorld directly, but if190// you need more control, you can use DynamicWorldBuilder.191let dynamic_world = DynamicWorld::from_world_with(&scene_world, &type_registry.read());192193// Dynamic Worlds can be serialized like this:194let type_registry = world.resource::<AppTypeRegistry>();195let type_registry = type_registry.read();196let serialized_world = dynamic_world.serialize(&type_registry).unwrap();197198// Shows the serialized world in the console199info!("{}", serialized_world);200201// Writing the world to a new file. Using a task to avoid calling the filesystem APIs in a system202// as they are blocking.203//204// This can't work in Wasm as there is no filesystem access.205#[cfg(not(target_arch = "wasm32"))]206IoTaskPool::get()207.spawn(async move {208// Write the world RON data to file209File::create(format!("assets/{NEW_WORLD_FILE_PATH}"))210.and_then(|mut file| file.write(serialized_world.as_bytes()))211.expect("Error while writing world to file");212})213.detach();214}215216/// Spawns a simple 2D camera and some text indicating that the user should217/// check the console output for world loading/saving messages.218///219/// This system is only necessary for the info message in the UI.220fn infotext_system(mut commands: Commands) {221commands.spawn((222Text::new("Check the console output for more!"),223TextFont {224font_size: FontSize::Px(42.0),225..default()226},227Node {228align_self: AlignSelf::FlexEnd,229..default()230},231));232}233234/// To help with Bevy's automated testing, we want the example to close with an appropriate if the235/// world asset fails to load. This is most likely not something you want in your own app.236fn panic_on_fail(world_roots: Query<&DynamicWorldRoot>, asset_server: Res<AssetServer>) {237for world_root in &world_roots {238if let Some(LoadState::Failed(err)) = asset_server.get_load_state(&world_root.0) {239panic!("Failed to load world. {err}");240}241}242}243244245