diff --git a/examples/scene/scene.rs b/examples/scene/scene.rs index 118b02bf10..58d733a80c 100644 --- a/examples/scene/scene.rs +++ b/examples/scene/scene.rs @@ -1,8 +1,37 @@ -//! This example illustrates loading scenes from files. +//! This example demonstrates how to load scene data from files and then dynamically +//! apply that data to entities in your Bevy `World`. This includes spawning new +//! entities and applying updates to existing ones. Scenes in Bevy encapsulate +//! serialized and deserialized `Components` or `Resources` so that you can easily +//! store, load, and manipulate data outside of a purely code-driven context. +//! +//! This example also shows how to do the following: +//! * Register your custom types for reflection, which allows them to be serialized, +//! deserialized, and manipulated dynamically. +//! * Skip serialization of fields you don't want stored in your scene files (like +//! runtime values that should always be computed dynamically). +//! * Save a new scene to disk to show how it can be updated compared to the original +//! scene file (and how that updated scene file might then be used later on). +//! +//! The example proceeds by creating components and resources, registering their types, +//! loading a scene from a file, logging when changes are detected, and finally saving +//! a new scene file to disk. This is useful for anyone wanting to see how to integrate +//! file-based scene workflows into their Bevy projects. +//! +//! # Note on working with files +//! +//! The saving behavior uses the standard filesystem APIs, which are blocking, so it +//! utilizes a thread pool (`IoTaskPool`) to avoid stalling the main thread. This +//! won't work on WASM because WASM typically doesn't have direct filesystem access. +//! + use bevy::{prelude::*, tasks::IoTaskPool}; use core::time::Duration; use std::{fs::File, io::Write}; +/// The entry point of our Bevy app. +/// +/// Sets up default plugins, registers all necessary component/resource types +/// for serialization/reflection, and runs the various systems in the correct schedule. fn main() { App::new() .add_plugins(DefaultPlugins) @@ -17,31 +46,46 @@ fn main() { .run(); } -// Registered components must implement the `Reflect` and `FromWorld` traits. -// The `Reflect` trait enables serialization, deserialization, and dynamic property access. -// `Reflect` enable a bunch of cool behaviors, so its worth checking out the dedicated `reflect.rs` -// example. The `FromWorld` trait determines how your component is constructed when it loads. -// For simple use cases you can just implement the `Default` trait (which automatically implements -// `FromWorld`). The simplest registered component just needs these three derives: +/// # Components, Resources, and Reflection +/// +/// Below are some simple examples of how to define your own Bevy `Component` types +/// and `Resource` types so that they can be properly reflected, serialized, and +/// deserialized. The `#[derive(Reflect)]` macro enables Bevy's reflection features, +/// and we add component-specific reflection by using `#[reflect(Component)]`. +/// We also illustrate how to skip serializing fields and how `FromWorld` can help +/// create runtime-initialized data. +/// +/// A sample component that is fully serializable. +/// +/// This component has public `x` and `y` fields that will be included in +/// the scene files. Notice how it derives `Default`, `Reflect`, and declares +/// itself as a reflected component with `#[reflect(Component)]`. #[derive(Component, Reflect, Default)] #[reflect(Component)] // this tells the reflect derive to also reflect component behaviors struct ComponentA { + /// An example `f32` field pub x: f32, + /// Another example `f32` field pub y: f32, } -// Some components have fields that cannot (or should not) be written to scene files. These can be -// ignored with the #[reflect(skip_serializing)] attribute. This is also generally where the `FromWorld` -// trait comes into play. `FromWorld` gives you access to your App's current ECS `Resources` -// when you construct your component. +/// A sample component that includes both serializable and non-serializable fields. +/// +/// This is useful for skipping serialization of runtime data or fields you +/// don't want written to scene files. #[derive(Component, Reflect)] #[reflect(Component)] struct ComponentB { + /// A string field that will be serialized. pub value: String, + /// A `Duration` field that should never be serialized to the scene file, so we skip it. #[reflect(skip_serializing)] pub _time_since_startup: Duration, } +/// This implements `FromWorld` for `ComponentB`, letting us initialize runtime fields +/// by accessing the current ECS resources. In this case, we acquire the `Time` resource +/// and store the current elapsed time. impl FromWorld for ComponentB { fn from_world(world: &mut World) -> Self { let time = world.resource::