Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/scene/scene.rs
9328 views
1
//! This example demonstrates how to load scene data from files and then dynamically
2
//! apply that data to entities in your Bevy `World`. This includes spawning new
3
//! entities and applying updates to existing ones. Scenes in Bevy encapsulate
4
//! serialized and deserialized `Components` or `Resources` so that you can easily
5
//! store, load, and manipulate data outside of a purely code-driven context.
6
//!
7
//! This example also shows how to do the following:
8
//! * Register your custom types for reflection, which allows them to be serialized,
9
//! deserialized, and manipulated dynamically.
10
//! * Skip serialization of fields you don't want stored in your scene files (like
11
//! runtime values that should always be computed dynamically).
12
//! * Save a new scene to disk to show how it can be updated compared to the original
13
//! scene file (and how that updated scene file might then be used later on).
14
//!
15
//! The example proceeds by creating components and resources, registering their types,
16
//! loading a scene from a file, logging when changes are detected, and finally saving
17
//! a new scene file to disk. This is useful for anyone wanting to see how to integrate
18
//! file-based scene workflows into their Bevy projects.
19
//!
20
//! # Note on working with files
21
//!
22
//! The saving behavior uses the standard filesystem APIs, which are blocking, so it
23
//! utilizes a thread pool (`IoTaskPool`) to avoid stalling the main thread. This
24
//! won't work on WASM because WASM typically doesn't have direct filesystem access.
25
//!
26
27
use bevy::{asset::LoadState, prelude::*, tasks::IoTaskPool};
28
29
use core::time::Duration;
30
use std::{fs::File, io::Write};
31
32
/// The entry point of our Bevy app.
33
///
34
/// Sets up default plugins, registers all necessary component/resource types
35
/// for serialization/reflection, and runs the various systems in the correct schedule.
36
fn main() {
37
App::new()
38
.add_plugins(DefaultPlugins)
39
.add_systems(
40
Startup,
41
(save_scene_system, load_scene_system, infotext_system),
42
)
43
.add_systems(Update, (log_system, panic_on_fail))
44
.run();
45
}
46
47
/// # Components, Resources, and Reflection
48
///
49
/// Below are some simple examples of how to define your own Bevy `Component` types
50
/// and `Resource` types so that they can be properly reflected, serialized, and
51
/// deserialized. The `#[derive(Reflect)]` macro enables Bevy's reflection features,
52
/// and we add component-specific reflection by using `#[reflect(Component)]`.
53
/// We also illustrate how to skip serializing fields and how `FromWorld` can help
54
/// create runtime-initialized data.
55
///
56
/// A sample component that is fully serializable.
57
///
58
/// This component has public `x` and `y` fields that will be included in
59
/// the scene files. Notice how it derives `Default`, `Reflect`, and declares
60
/// itself as a reflected component with `#[reflect(Component)]`.
61
#[derive(Component, Reflect, Default)]
62
#[reflect(Component)] // this tells the reflect derive to also reflect component behaviors
63
struct ComponentA {
64
/// An example `f32` field
65
pub x: f32,
66
/// Another example `f32` field
67
pub y: f32,
68
}
69
70
/// A sample component that includes both serializable and non-serializable fields.
71
///
72
/// This is useful for skipping serialization of runtime data or fields you
73
/// don't want written to scene files.
74
#[derive(Component, Reflect)]
75
#[reflect(Component)]
76
struct ComponentB {
77
/// A string field that will be serialized.
78
pub value: String,
79
/// A `Duration` field that should never be serialized to the scene file, so we skip it.
80
#[reflect(skip_serializing)]
81
pub _time_since_startup: Duration,
82
}
83
84
/// This implements `FromWorld` for `ComponentB`, letting us initialize runtime fields
85
/// by accessing the current ECS resources. In this case, we acquire the `Time` resource
86
/// and store the current elapsed time.
87
impl FromWorld for ComponentB {
88
fn from_world(world: &mut World) -> Self {
89
let time = world.resource::<Time>();
90
ComponentB {
91
_time_since_startup: time.elapsed(),
92
value: "Default Value".to_string(),
93
}
94
}
95
}
96
97
/// A simple resource that also derives `Reflect`, allowing it to be stored in scenes.
98
///
99
/// Just like a component, you can skip serializing fields or implement `FromWorld` if needed.
100
#[derive(Resource, Reflect, Default)]
101
#[reflect(Resource)]
102
struct ResourceA {
103
/// This resource tracks a `score` value.
104
pub score: u32,
105
}
106
107
/// # Scene File Paths
108
///
109
/// `SCENE_FILE_PATH` points to the original scene file that we'll be loading.
110
/// `NEW_SCENE_FILE_PATH` points to the new scene file that we'll be creating
111
/// (and demonstrating how to serialize to disk).
112
///
113
/// The initial scene file will be loaded below and not change when the scene is saved.
114
const SCENE_FILE_PATH: &str = "scenes/load_scene_example.scn.ron";
115
116
/// The new, updated scene data will be saved here so that you can see the changes.
117
const NEW_SCENE_FILE_PATH: &str = "scenes/load_scene_example-new.scn.ron";
118
119
/// Loads a scene from an asset file and spawns it in the current world.
120
///
121
/// Spawning a `DynamicSceneRoot` creates a new parent entity, which then spawns new
122
/// instances of the scene's entities as its children. If you modify the
123
/// `SCENE_FILE_PATH` scene file, or if you enable file watching, you can see
124
/// changes reflected immediately.
125
fn load_scene_system(mut commands: Commands, asset_server: Res<AssetServer>) {
126
commands.spawn(DynamicSceneRoot(asset_server.load(SCENE_FILE_PATH)));
127
}
128
129
/// Logs changes made to `ComponentA` entities, and also checks whether `ResourceA`
130
/// has been recently added.
131
///
132
/// Any time a `ComponentA` is modified, that change will appear here. This system
133
/// demonstrates how you might detect and handle scene updates at runtime.
134
fn log_system(
135
query: Query<(Entity, &ComponentA), Changed<ComponentA>>,
136
res: Option<Res<ResourceA>>,
137
) {
138
for (entity, component_a) in &query {
139
info!(" Entity({})", entity.index());
140
info!(
141
" ComponentA: {{ x: {} y: {} }}\n",
142
component_a.x, component_a.y
143
);
144
}
145
if let Some(res) = res
146
&& res.is_added()
147
{
148
info!(" New ResourceA: {{ score: {} }}\n", res.score);
149
}
150
}
151
152
/// Demonstrates how to create a new scene from scratch, populate it with data,
153
/// and then serialize it to a file. The new file is written to `NEW_SCENE_FILE_PATH`.
154
///
155
/// This system creates a fresh world, duplicates the type registry so that our
156
/// custom component types are recognized, spawns some sample entities and resources,
157
/// and then serializes the resulting dynamic scene.
158
fn save_scene_system(world: &mut World) {
159
// Scenes can be created from any ECS World.
160
// You can either create a new one for the scene or use the current World.
161
// For demonstration purposes, we'll create a new one.
162
let mut scene_world = World::new();
163
164
// The `TypeRegistry` resource contains information about all registered types (including components).
165
// This is used to construct scenes, so we'll want to ensure that our previous type registrations
166
// exist in this new scene world as well.
167
// To do this, we can simply clone the `AppTypeRegistry` resource.
168
let type_registry = world.resource::<AppTypeRegistry>().clone();
169
scene_world.insert_resource(type_registry);
170
171
let mut component_b = ComponentB::from_world(world);
172
component_b.value = "hello".to_string();
173
scene_world.spawn((
174
component_b,
175
ComponentA { x: 1.0, y: 2.0 },
176
Transform::IDENTITY,
177
Name::new("joe"),
178
));
179
scene_world.spawn(ComponentA { x: 3.0, y: 4.0 });
180
scene_world.insert_resource(ResourceA { score: 1 });
181
182
// With our sample world ready to go, we can now create our scene using DynamicScene or DynamicSceneBuilder.
183
// For simplicity, we will create our scene using DynamicScene:
184
let scene = DynamicScene::from_world(&scene_world);
185
186
// Scenes can be serialized like this:
187
let type_registry = world.resource::<AppTypeRegistry>();
188
let type_registry = type_registry.read();
189
let serialized_scene = scene.serialize(&type_registry).unwrap();
190
191
// Showing the scene in the console
192
info!("{}", serialized_scene);
193
194
// Writing the scene to a new file. Using a task to avoid calling the filesystem APIs in a system
195
// as they are blocking.
196
//
197
// This can't work in Wasm as there is no filesystem access.
198
#[cfg(not(target_arch = "wasm32"))]
199
IoTaskPool::get()
200
.spawn(async move {
201
// Write the scene RON data to file
202
File::create(format!("assets/{NEW_SCENE_FILE_PATH}"))
203
.and_then(|mut file| file.write(serialized_scene.as_bytes()))
204
.expect("Error while writing scene to file");
205
})
206
.detach();
207
}
208
209
/// Spawns a simple 2D camera and some text indicating that the user should
210
/// check the console output for scene loading/saving messages.
211
///
212
/// This system is only necessary for the info message in the UI.
213
fn infotext_system(mut commands: Commands) {
214
commands.spawn(Camera2d);
215
commands.spawn((
216
Text::new("Nothing to see in this window! Check the console output!"),
217
TextFont {
218
font_size: FontSize::Px(42.0),
219
..default()
220
},
221
Node {
222
align_self: AlignSelf::FlexEnd,
223
..default()
224
},
225
));
226
}
227
228
/// To help with Bevy's automated testing, we want the example to close with an appropriate if the
229
/// scene fails to load. This is most likely not something you want in your own app.
230
fn panic_on_fail(scenes: Query<&DynamicSceneRoot>, asset_server: Res<AssetServer>) {
231
for scene in &scenes {
232
if let Some(LoadState::Failed(err)) = asset_server.get_load_state(&scene.0) {
233
panic!("Failed to load scene. {err}");
234
}
235
}
236
}
237
238