From 7b79b3de8dedee4f9f9cdee376e47e6f758754ce Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Fri, 1 May 2020 13:12:47 -0700 Subject: [PATCH] organize examples and add ecs guide --- Cargo.toml | 87 ++++++- README.md | 2 +- assets/{ => branding}/banner-text.png | Bin assets/{ => branding}/banner.png | Bin assets/{ => branding}/bevy_logo_dark.png | Bin assets/{ => branding}/bevy_logo_dark.svg | 0 assets/{ => branding}/bevy_logo_dark_big.png | Bin assets/{ => branding}/bevy_logo_light.png | Bin assets/{ => branding}/bevy_logo_light.svg | 0 .../{ => branding}/bevy_logo_light_small.svg | 0 assets/{ => branding}/icon.png | Bin {examples/assets => assets/models}/Box0.bin | Bin {examples/assets => assets/models}/Monkey.bin | Bin .../assets => assets/models}/Monkey.gltf | 0 crates/bevy_app/src/app_builder.rs | 14 +- crates/bevy_app/src/schedule_runner.rs | 14 ++ crates/bevy_diagnostic/src/lib.rs | 2 +- crates/bevy_render/src/lib.rs | 4 +- crates/bevy_render/src/texture/texture.rs | 23 -- examples/{ => 3d}/load_model.rs | 6 +- examples/{ => 3d}/parenting.rs | 0 examples/{ => 3d}/scene.rs | 2 - examples/{ => 3d}/spawner.rs | 0 examples/{ => 3d}/texture.rs | 8 +- .../example_plugin/.gitignore | 0 .../example_plugin/Cargo.toml | 5 +- .../example_plugin/src/lib.rs | 0 .../{ => app}/dynamic_plugin_loading/main.rs | 0 examples/{ => app}/empty.rs | 0 examples/{ => app}/empty_defaults.rs | 0 examples/{ => app}/headless.rs | 15 +- examples/ecs/ecs_guide.rs | 217 ++++++++++++++++++ examples/{ => ecs}/event.rs | 4 +- examples/ecs/startup_system.rs | 22 ++ examples/{ => input}/input_keyboard.rs | 2 +- examples/{ => input}/input_mouse.rs | 2 +- examples/{ => serializing}/serializing.rs | 0 .../{ => shader}/shader_custom_material.rs | 0 examples/{ => shader}/shader_defs.rs | 0 examples/startup_system.rs | 47 ---- examples/systems.rs | 67 ------ examples/{ => ui}/ui.rs | 0 examples/{ => ui}/ui_bench.rs | 0 examples/{ => window}/multiple_windows.rs | 0 src/prelude.rs | 2 +- 45 files changed, 366 insertions(+), 179 deletions(-) rename assets/{ => branding}/banner-text.png (100%) rename assets/{ => branding}/banner.png (100%) rename assets/{ => branding}/bevy_logo_dark.png (100%) rename assets/{ => branding}/bevy_logo_dark.svg (100%) rename assets/{ => branding}/bevy_logo_dark_big.png (100%) rename assets/{ => branding}/bevy_logo_light.png (100%) rename assets/{ => branding}/bevy_logo_light.svg (100%) rename assets/{ => branding}/bevy_logo_light_small.svg (100%) rename assets/{ => branding}/icon.png (100%) rename {examples/assets => assets/models}/Box0.bin (100%) rename {examples/assets => assets/models}/Monkey.bin (100%) rename {examples/assets => assets/models}/Monkey.gltf (100%) rename examples/{ => 3d}/load_model.rs (88%) rename examples/{ => 3d}/parenting.rs (100%) rename examples/{ => 3d}/scene.rs (95%) rename examples/{ => 3d}/spawner.rs (100%) rename examples/{ => 3d}/texture.rs (92%) rename examples/{ => app}/dynamic_plugin_loading/example_plugin/.gitignore (100%) rename examples/{ => app}/dynamic_plugin_loading/example_plugin/Cargo.toml (71%) rename examples/{ => app}/dynamic_plugin_loading/example_plugin/src/lib.rs (100%) rename examples/{ => app}/dynamic_plugin_loading/main.rs (100%) rename examples/{ => app}/empty.rs (100%) rename examples/{ => app}/empty_defaults.rs (100%) rename examples/{ => app}/headless.rs (64%) create mode 100644 examples/ecs/ecs_guide.rs rename examples/{ => ecs}/event.rs (91%) create mode 100644 examples/ecs/startup_system.rs rename examples/{ => input}/input_keyboard.rs (98%) rename examples/{ => input}/input_mouse.rs (95%) rename examples/{ => serializing}/serializing.rs (100%) rename examples/{ => shader}/shader_custom_material.rs (100%) rename examples/{ => shader}/shader_defs.rs (100%) delete mode 100644 examples/startup_system.rs delete mode 100644 examples/systems.rs rename examples/{ => ui}/ui.rs (100%) rename examples/{ => ui}/ui_bench.rs (100%) rename examples/{ => window}/multiple_windows.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index e61d0c23fa..1601b07866 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ glam = "0.8.6" [workspace] members = [ "crates/*", + "examples/app/dynamic_plugin_loading/example_plugin" ] [dev-dependencies] @@ -58,4 +59,88 @@ type-uuid = "0.1" env_logger = "0.7" [profile.dev] -opt-level = 3 \ No newline at end of file +opt-level = 3 + +[[example]] +name = "hello_world" +path = "examples/hello_world.rs" + +[[example]] +name = "load_model" +path = "examples/3d/load_model.rs" + +[[example]] +name = "parenting" +path = "examples/3d/parenting.rs" + +[[example]] +name = "scene" +path = "examples/3d/scene.rs" + +[[example]] +name = "spawner" +path = "examples/3d/spawner.rs" + +[[example]] +name = "texture" +path = "examples/3d/texture.rs" + +[[example]] +name = "dynamic_plugin_loading" +path = "examples/app/dynamic_plugin_loading/main.rs" + +[[example]] +name = "empty_defaults" +path = "examples/app/empty_defaults.rs" + +[[example]] +name = "empty" +path = "examples/app/empty.rs" + +[[example]] +name = "headless" +path = "examples/app/headless.rs" + +[[example]] +name = "event" +path = "examples/ecs/event.rs" + +[[example]] +name = "startup_system" +path = "examples/ecs/startup_system.rs" + +[[example]] +name = "ecs_guide" +path = "examples/ecs/ecs_guide.rs" + +[[example]] +name = "input_mouse" +path = "examples/input/input_mouse.rs" + +[[example]] +name = "input_keyboard" +path = "examples/input/input_keyboard.rs" + +[[example]] +name = "serializing" +path = "examples/serializing/serializing.rs" + +[[example]] +name = "shader_custom_material" +path = "examples/shader/shader_custom_material.rs" + +[[example]] +name = "shader_defs" +path = "examples/shader/shader_defs.rs" + +[[example]] +name = "ui" +path = "examples/ui/ui.rs" + +[[example]] +name = "ui_bench" +path = "examples/ui/ui_bench.rs" + +[[example]] +name = "multiple_windows" +path = "examples/window/multiple_windows.rs" \ No newline at end of file diff --git a/README.md b/README.md index ea0060efd4..1169fca1a0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# [![Bevy](assets/bevy_logo_light_small.svg)](https://bevyengine.org) +# [![Bevy](assets/branding/bevy_logo_light_small.svg)](https://bevyengine.org) [![Crates.io](https://img.shields.io/crates/v/bevy.svg)](https://crates.io/crates/bevy) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bevyengine/bevy/LICENSE) [![Crates.io](https://img.shields.io/crates/d/bevy.svg)](https://crates.io/crates/bevy) diff --git a/assets/banner-text.png b/assets/branding/banner-text.png similarity index 100% rename from assets/banner-text.png rename to assets/branding/banner-text.png diff --git a/assets/banner.png b/assets/branding/banner.png similarity index 100% rename from assets/banner.png rename to assets/branding/banner.png diff --git a/assets/bevy_logo_dark.png b/assets/branding/bevy_logo_dark.png similarity index 100% rename from assets/bevy_logo_dark.png rename to assets/branding/bevy_logo_dark.png diff --git a/assets/bevy_logo_dark.svg b/assets/branding/bevy_logo_dark.svg similarity index 100% rename from assets/bevy_logo_dark.svg rename to assets/branding/bevy_logo_dark.svg diff --git a/assets/bevy_logo_dark_big.png b/assets/branding/bevy_logo_dark_big.png similarity index 100% rename from assets/bevy_logo_dark_big.png rename to assets/branding/bevy_logo_dark_big.png diff --git a/assets/bevy_logo_light.png b/assets/branding/bevy_logo_light.png similarity index 100% rename from assets/bevy_logo_light.png rename to assets/branding/bevy_logo_light.png diff --git a/assets/bevy_logo_light.svg b/assets/branding/bevy_logo_light.svg similarity index 100% rename from assets/bevy_logo_light.svg rename to assets/branding/bevy_logo_light.svg diff --git a/assets/bevy_logo_light_small.svg b/assets/branding/bevy_logo_light_small.svg similarity index 100% rename from assets/bevy_logo_light_small.svg rename to assets/branding/bevy_logo_light_small.svg diff --git a/assets/icon.png b/assets/branding/icon.png similarity index 100% rename from assets/icon.png rename to assets/branding/icon.png diff --git a/examples/assets/Box0.bin b/assets/models/Box0.bin similarity index 100% rename from examples/assets/Box0.bin rename to assets/models/Box0.bin diff --git a/examples/assets/Monkey.bin b/assets/models/Monkey.bin similarity index 100% rename from examples/assets/Monkey.bin rename to assets/models/Monkey.bin diff --git a/examples/assets/Monkey.gltf b/assets/models/Monkey.gltf similarity index 100% rename from examples/assets/Monkey.gltf rename to assets/models/Monkey.gltf diff --git a/crates/bevy_app/src/app_builder.rs b/crates/bevy_app/src/app_builder.rs index 5756f03ed0..925a0ad728 100644 --- a/crates/bevy_app/src/app_builder.rs +++ b/crates/bevy_app/src/app_builder.rs @@ -116,14 +116,14 @@ impl AppBuilder { self.add_system_to_stage(stage::UPDATE, system) } - pub fn add_system_init(&mut self, build: impl FnMut(&mut Resources) -> T) -> &mut Self + pub fn init_system(&mut self, build: impl FnMut(&mut Resources) -> T) -> &mut Self where T: Into, { - self.add_system_to_stage_init(stage::UPDATE, build) + self.init_system_to_stage(stage::UPDATE, build) } - pub fn add_system_to_stage_init( + pub fn init_system_to_stage( &mut self, stage: &str, mut build: impl FnMut(&mut Resources) -> T, @@ -151,17 +151,17 @@ impl AppBuilder { self } - pub fn add_startup_system_init( + pub fn init_startup_system( &mut self, build: impl FnMut(&mut Resources) -> T, ) -> &mut Self where T: Into, { - self.add_startup_system_to_stage_init(stage::STARTUP, build) + self.init_startup_system_to_stage(stage::STARTUP, build) } - pub fn add_startup_system_to_stage_init( + pub fn init_startup_system_to_stage( &mut self, stage: &str, mut build: impl FnMut(&mut Resources) -> T, @@ -211,7 +211,7 @@ impl AppBuilder { self } - pub fn add_resource_init(&mut self) -> &mut Self + pub fn init_resource(&mut self) -> &mut Self where R: FromResources + Send + Sync + 'static, { diff --git a/crates/bevy_app/src/schedule_runner.rs b/crates/bevy_app/src/schedule_runner.rs index b8540545e9..6bcf69681d 100644 --- a/crates/bevy_app/src/schedule_runner.rs +++ b/crates/bevy_app/src/schedule_runner.rs @@ -19,6 +19,20 @@ pub struct ScheduleRunnerPlugin { pub run_mode: RunMode, } +impl ScheduleRunnerPlugin { + pub fn run_once() -> Self { + ScheduleRunnerPlugin { + run_mode: RunMode::Once, + } + } + + pub fn run_loop(wait_duration: Duration) -> Self { + ScheduleRunnerPlugin { + run_mode: RunMode::Loop { wait: Some(wait_duration) }, + } + } +} + impl AppPlugin for ScheduleRunnerPlugin { fn build(&self, app: &mut AppBuilder) { let run_mode = self.run_mode; diff --git a/crates/bevy_diagnostic/src/lib.rs b/crates/bevy_diagnostic/src/lib.rs index 691b319c91..ea823dcd7a 100644 --- a/crates/bevy_diagnostic/src/lib.rs +++ b/crates/bevy_diagnostic/src/lib.rs @@ -28,7 +28,7 @@ impl Default for DiagnosticsPlugin { impl AppPlugin for DiagnosticsPlugin { fn build(&self, app: &mut AppBuilder) { - app.add_resource_init::(); + app.init_resource::(); if self.add_defaults { app.add_startup_system(setup_frame_time_diagnostic_system.system()) .add_system(frame_time_diagnostic_system.system()); diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 0f287010f7..7f30522365 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -70,10 +70,10 @@ impl AppPlugin for RenderPlugin { .add_resource(AssetBatchers::default()) // core systems .add_system(entity_render_resource_assignments_system()) - .add_system_to_stage_init(stage::POST_UPDATE, camera::camera_update_system) + .init_system_to_stage(stage::POST_UPDATE, camera::camera_update_system) .add_system_to_stage(stage::POST_UPDATE, mesh::mesh_specializer_system()) .add_system_to_stage(stage::POST_UPDATE, mesh::mesh_batcher_system()) // render resource provider systems - .add_system_to_stage_init(RENDER_RESOURCE_STAGE, mesh_resource_provider_system); + .init_system_to_stage(RENDER_RESOURCE_STAGE, mesh_resource_provider_system); } } diff --git a/crates/bevy_render/src/texture/texture.rs b/crates/bevy_render/src/texture/texture.rs index 888feac219..2997d9f379 100644 --- a/crates/bevy_render/src/texture/texture.rs +++ b/crates/bevy_render/src/texture/texture.rs @@ -36,29 +36,6 @@ impl Asset for Texture { } } -pub fn create_texels(size: usize) -> Vec { - use std::iter; - - (0..size * size) - .flat_map(|id| { - // get high five for recognizing this ;) - let cx = 3.0 * (id % size) as f32 / (size - 1) as f32 - 2.0; - let cy = 2.0 * (id / size) as f32 / (size - 1) as f32 - 1.0; - let (mut x, mut y, mut count) = (cx, cy, 0); - while count < 0xFF && x * x + y * y < 4.0 { - let old_x = x; - x = x * x - y * y + cx; - y = 2.0 * old_x * y + cy; - count += 1; - } - iter::once(0xFF - (count * 5) as u8) - .chain(iter::once(0xFF - (count * 15) as u8)) - .chain(iter::once(0xFF - (count * 50) as u8)) - .chain(iter::once(1)) - }) - .collect() -} - impl ShaderDefSuffixProvider for Option> { fn get_shader_def(&self) -> Option<&'static str> { match *self { diff --git a/examples/load_model.rs b/examples/3d/load_model.rs similarity index 88% rename from examples/load_model.rs rename to examples/3d/load_model.rs index 4f0709fd9c..36d6de2b9e 100644 --- a/examples/load_model.rs +++ b/examples/3d/load_model.rs @@ -4,15 +4,13 @@ fn main() { App::build() .add_default_plugins() .add_startup_system(setup) - .add_system_init(bevy::input::system::exit_on_esc_system) .run(); } fn setup(world: &mut World, resources: &mut Resources) { // load the mesh - let mesh = gltf::load_gltf("examples/assets/Monkey.gltf") - .unwrap() - .unwrap(); + let model_path = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/models/Monkey.gltf"); + let mesh = gltf::load_gltf(&model_path).unwrap().unwrap(); let mut mesh_storage = resources.get_mut::>().unwrap(); let mesh_handle = mesh_storage.add(mesh); diff --git a/examples/parenting.rs b/examples/3d/parenting.rs similarity index 100% rename from examples/parenting.rs rename to examples/3d/parenting.rs diff --git a/examples/scene.rs b/examples/3d/scene.rs similarity index 95% rename from examples/scene.rs rename to examples/3d/scene.rs index d08230b46f..32c97941ec 100644 --- a/examples/scene.rs +++ b/examples/3d/scene.rs @@ -1,11 +1,9 @@ use bevy::prelude::*; -use bevy_input::system::exit_on_esc_system; fn main() { App::build() .add_default_plugins() .add_startup_system(setup) - .add_system_init(exit_on_esc_system) .run(); } diff --git a/examples/spawner.rs b/examples/3d/spawner.rs similarity index 100% rename from examples/spawner.rs rename to examples/3d/spawner.rs diff --git a/examples/texture.rs b/examples/3d/texture.rs similarity index 92% rename from examples/texture.rs rename to examples/3d/texture.rs index 53d701149e..ac2ea189cc 100644 --- a/examples/texture.rs +++ b/examples/3d/texture.rs @@ -11,9 +11,11 @@ fn main() { fn setup(world: &mut World, resources: &mut Resources) { // load a texture let mut texture_storage = resources.get_mut::>().unwrap(); - let texture = Texture::load(TextureType::Png( - concat!(env!("CARGO_MANIFEST_DIR"), "/assets/bevy_logo_dark_big.png").to_string(), - )); + let texture_path = concat!( + env!("CARGO_MANIFEST_DIR"), + "/assets/branding/bevy_logo_dark_big.png" + ); + let texture = Texture::load(TextureType::Png(texture_path.to_string())); let aspect = texture.height as f32 / texture.width as f32; let texture_handle = texture_storage.add(texture); diff --git a/examples/dynamic_plugin_loading/example_plugin/.gitignore b/examples/app/dynamic_plugin_loading/example_plugin/.gitignore similarity index 100% rename from examples/dynamic_plugin_loading/example_plugin/.gitignore rename to examples/app/dynamic_plugin_loading/example_plugin/.gitignore diff --git a/examples/dynamic_plugin_loading/example_plugin/Cargo.toml b/examples/app/dynamic_plugin_loading/example_plugin/Cargo.toml similarity index 71% rename from examples/dynamic_plugin_loading/example_plugin/Cargo.toml rename to examples/app/dynamic_plugin_loading/example_plugin/Cargo.toml index b4c4c61004..00f7f94bf0 100644 --- a/examples/dynamic_plugin_loading/example_plugin/Cargo.toml +++ b/examples/app/dynamic_plugin_loading/example_plugin/Cargo.toml @@ -8,7 +8,4 @@ edition = "2018" crate-type = ["cdylib"] [dependencies] -bevy = { path = "../../../../bevy" } - -[profile.release] -debug = true \ No newline at end of file +bevy = { path = "../../../../../bevy" } \ No newline at end of file diff --git a/examples/dynamic_plugin_loading/example_plugin/src/lib.rs b/examples/app/dynamic_plugin_loading/example_plugin/src/lib.rs similarity index 100% rename from examples/dynamic_plugin_loading/example_plugin/src/lib.rs rename to examples/app/dynamic_plugin_loading/example_plugin/src/lib.rs diff --git a/examples/dynamic_plugin_loading/main.rs b/examples/app/dynamic_plugin_loading/main.rs similarity index 100% rename from examples/dynamic_plugin_loading/main.rs rename to examples/app/dynamic_plugin_loading/main.rs diff --git a/examples/empty.rs b/examples/app/empty.rs similarity index 100% rename from examples/empty.rs rename to examples/app/empty.rs diff --git a/examples/empty_defaults.rs b/examples/app/empty_defaults.rs similarity index 100% rename from examples/empty_defaults.rs rename to examples/app/empty_defaults.rs diff --git a/examples/headless.rs b/examples/app/headless.rs similarity index 64% rename from examples/headless.rs rename to examples/app/headless.rs index 3af3bfa04d..cd8cbfae61 100644 --- a/examples/headless.rs +++ b/examples/app/headless.rs @@ -1,7 +1,4 @@ -use bevy::{ - app::schedule_runner::{RunMode, ScheduleRunnerPlugin}, - prelude::*, -}; +use bevy::prelude::*; use std::time::Duration; // This example disables the default plugins by not registering them during setup. @@ -14,19 +11,13 @@ use std::time::Duration; fn main() { // this app runs once App::build() - .add_plugin(ScheduleRunnerPlugin { - run_mode: RunMode::Once, - }) + .add_plugin(ScheduleRunnerPlugin::run_once()) .add_system(hello_world_system.system()) .run(); // this app loops forever at 60 fps App::build() - .add_plugin(ScheduleRunnerPlugin { - run_mode: RunMode::Loop { - wait: Some(Duration::from_secs_f64(1.0 / 60.0)), - }, - }) + .add_plugin(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(1.0 / 60.0))) .add_system(hello_world_system.system()) .run(); } diff --git a/examples/ecs/ecs_guide.rs b/examples/ecs/ecs_guide.rs new file mode 100644 index 0000000000..473625a764 --- /dev/null +++ b/examples/ecs/ecs_guide.rs @@ -0,0 +1,217 @@ +use bevy::prelude::*; + +/// This is a guided introduction to Bevy's "Entity Component System" (ECS) +/// All Bevy app logic is built using the ECS pattern, so definitely pay attention! +/// +/// Why ECS? +/// * Data oriented: Functionality is driven by data +/// * Clean Architecture: Loose coupling of functionality / prevents deeply nested inheritance +/// * High Performance: Massively parallel and cache friendly +/// +/// ECS Definitions: +/// +/// Component: just a normal Rust data type. generally scoped to a single piece of functionality +/// Examples: position, velocity, health, color, name +/// +/// Entity: a collection of components with a unique id +/// Examples: Entity1 { Name("Alice"), Position(0, 0) }, Entity2 { Name("Bill"), Position(10, 5) } + +/// Resource: a shared global piece of data +/// Examples: asset_storage, events, system state +/// +/// System: runs logic on entities, components, and resources +/// Examples: move_system, damage_system +/// +/// Now that you know a little bit about ECS, lets look at some Bevy code! + +// Our Bevy app's entry point +fn main() { + // Bevy apps are created using the builder pattern. Here add our + App::build() + // This plugin runs our app's "system schedule" exactly once. Most apps will run on a loop, + // but we don't want to spam your console with a bunch of example text :) + .add_plugin(ScheduleRunnerPlugin::run_once()) + // Resources can be added to our app like this + .add_resource(A { value: 1 }) + // Resources that implement the Default or FromResources trait can be added like this: + .init_resource::() + .init_resource::() + // Systems can be added to our app like this + // the system() call converts normal rust functions into ECS systems + .add_system(empty_system.system()) + // Startup systems run exactly once BEFORE all other systems. These are generally used for + // app initialization code (adding entities and resources) + .add_startup_system(startup_system) + // Systems that need resources to be constructed can be added like this + .init_system(complex_system) + // Here we just the rest of the example systems + .add_system(resource_system.system()) + .add_system(for_each_entity_system.system()) + .add_system(resources_and_components_system.system()) + .add_system(command_buffer_system.system()) + .add_system(thread_local_system) + .add_system(closure_system()) + .add_system(stateful_system.system()) + .run(); +} + +// RESOURCES: "global" state accessible by systems + +struct A { + value: usize, +} + +#[derive(Default)] +struct B { + value: usize, +} + +struct C; + +// COMPONENTS: pieces of functionality we add to entities + +struct X { + value: usize, +} +struct Y { + value: usize, +} + +// SYSTEMS: logic that runs on entities, components, and resources + +// This is the simplest system. It will run once each time our app updates: +fn empty_system() { + println!("hello!"); +} + +// Systems can also read and modify resources: +fn resource_system(a: Resource, mut b: ResourceMut) { + b.value += 1; + println!("resource_system: {} {}", a.value, b.value); +} + +// This system runs once for each entity with the X and Y component +// NOTE: x is a read-only reference (Ref) whereas y can be modified (RefMut) +fn for_each_entity_system(x: Ref, mut y: RefMut) { + y.value += 1; + println!("for_each_entity_system: {} {}", x.value, y.value); +} + +// This system is the same as the above example, but it also accesses resource A +// NOTE: resources must always come before components in system functions +fn resources_and_components_system(a: Resource, x: Ref, mut y: RefMut) { + y.value += 1; + println!("resources_and_components:"); + println!(" components: {} {}", x.value, y.value); + println!(" resource: {} ", a.value); +} + +// This is a "startup" system that runs once when the app starts up. The only thing that distinguishes a +// startup" system from a "normal" system is how it is registered: +// app.add_startup_system(startup_system) +// app.add_system(normal_system) +// With startup systems we can create resources and add entities to our world, which can then be used by +// our other systems: +fn startup_system(world: &mut World, resources: &mut Resources) { + // We already added A and B when we built our App above, so we don't re-add them here + resources.insert(C); + + // Add some entities to our world + world.insert( + (), + vec![ + (X { value: 0 }, Y { value: 1 }), + (X { value: 2 }, Y { value: 3 }), + ], + ); + + // Add some entities to our world + world.insert( + (), + vec![ + (X { value: 0 }, Y { value: 1 }), + (X { value: 2 }, Y { value: 3 }), + ], + ); +} + +// This system uses a command buffer to create a new entity on each iteration +// Normal systems cannot safely access the World instance because they run in parallel +// Command buffers give us the ability to queue up changes to our World without directly accessing it +// NOTE: Command buffers must always come before resources and components in system functions +fn command_buffer_system(command_buffer: &mut CommandBuffer, a: Resource) { + // Creates a new entity with a value read from resource A + command_buffer.insert((), vec![(X { value: a.value },)]); +} + +// If you really need full/immediate read/write access to the world or resources, you can use a "thread local system". +// These run on the main app thread (hence the name "thread local") +// WARNING: These will block all parallel execution of other systems until they finish, so they should generally be avoided +// NOTE: You may notice that this looks exactly like the "setup" system above. Thats because they are both thread local! +fn thread_local_system(world: &mut World, _resources: &mut Resources) { + world.insert((), vec![(X { value: 1 },)]); +} + +// These are like normal systems, but they also "capture" variables, which they can use as local state. +// This system captures the "counter" variable and uses it to maintain a count across executions +// NOTE: This function returns a Box type. If you are new to rust don't worry! All you +// need to know for now is that the Box contains our system AND the state it captured. +// You may recognize the .system() call from when we added our system functions to our App in the main() +// function above. Now you know that we are actually converting our functions into the Box type! +fn closure_system() -> Box { + let mut counter = 0; + (move |x: Ref, mut y: RefMut| { + y.value += 1; + println!("closure_system: {} {}", x.value, y.value); + println!(" ran {} times: ", counter); + counter += 1; + }) + .system() +} + +// Closure systems should be avoided in general because they hide state from the ECS. This makes scenarios +// like "saving", "networking/multiplayer", and "replays" much harder. +// Instead you should use the "state" pattern whenever possible: + +#[derive(Default)] +struct State { + counter: usize, +} + +fn stateful_system(mut state: RefMut, x: Ref, mut y: RefMut) { + y.value += 1; + println!("stateful_system: {} {}", x.value, y.value); + println!(" ran {} times: ", state.counter); + state.counter += 1; +} + +// If you need more flexibility, you can define complex systems using "system builders". +// SystemBuilder enables scenarios like "multiple queries" and "query filters" +fn complex_system(_resources: &mut Resources) -> Box { + let mut counter = 0; + SystemBuilder::new("complex_system") + .read_resource::() + .write_resource::() + // this query is equivalent to the system we saw above: system(x: Ref, y: RefMut) + .with_query(<(Read, Write)>::query()) + // this query only runs on entities with an X component that has changed since the last update + .with_query(>::query().filter(changed::())) + .build( + move |_command_buffer, world, (a, ref mut b), (x_y_query, x_changed_query)| { + println!("complex_system:"); + println!(" resources: {} {}", a.value, b.value); + for (x, mut y) in x_y_query.iter_mut(world) { + y.value += 1; + println!( + " processed entity {} times: {} {}", + counter, x.value, y.value + ); + counter += 1; + } + + for x in x_changed_query.iter(world) { + println!(" x changed: {}", x.value); + } + }, + ) +} diff --git a/examples/event.rs b/examples/ecs/event.rs similarity index 91% rename from examples/event.rs rename to examples/ecs/event.rs index 619590903c..5254599f4c 100644 --- a/examples/event.rs +++ b/examples/ecs/event.rs @@ -5,7 +5,7 @@ fn main() { .add_default_plugins() .add_event::() .add_resource(EventTriggerState::default()) - .add_resource_init::() + .init_resource::() .add_system(event_trigger_system.system()) .add_system(event_listener_system.system()) .run(); @@ -29,7 +29,7 @@ fn event_trigger_system( state.elapsed += time.delta_seconds; if state.elapsed > 1.0 { my_events.send(MyEvent { - message: "Hello World".to_string(), + message: "MyEvent just happened!".to_string(), }); state.elapsed = 0.0; diff --git a/examples/ecs/startup_system.rs b/examples/ecs/startup_system.rs new file mode 100644 index 0000000000..a6ff8726e8 --- /dev/null +++ b/examples/ecs/startup_system.rs @@ -0,0 +1,22 @@ +use bevy::{ + app::schedule_runner::ScheduleRunnerPlugin, + prelude::*, +}; + +fn main() { + App::build() + .add_plugin(ScheduleRunnerPlugin::run_once()) // only run the app once so the printed system order is clearer + .add_startup_system(startup_system.system()) + .add_system(normal_system.system()) + .run(); +} + +/// Startup systems are run exactly once when the app starts up. +/// They run right before "normal" systems run. +fn startup_system() { + println!("startup system ran first"); +} + +fn normal_system() { + println!("normal system ran second"); +} diff --git a/examples/input_keyboard.rs b/examples/input/input_keyboard.rs similarity index 98% rename from examples/input_keyboard.rs rename to examples/input/input_keyboard.rs index 983f7efd2e..7a6a066491 100644 --- a/examples/input_keyboard.rs +++ b/examples/input/input_keyboard.rs @@ -6,7 +6,7 @@ use bevy::{ fn main() { App::build() .add_default_plugins() - .add_resource_init::() + .init_resource::() .add_system(collect_input_system.system()) .add_system(move_system.system()) .add_startup_system(setup) diff --git a/examples/input_mouse.rs b/examples/input/input_mouse.rs similarity index 95% rename from examples/input_mouse.rs rename to examples/input/input_mouse.rs index 4c3211cd14..be6bac495d 100644 --- a/examples/input_mouse.rs +++ b/examples/input/input_mouse.rs @@ -6,7 +6,7 @@ use bevy::{ fn main() { App::build() .add_default_plugins() - .add_resource_init::() + .init_resource::() .add_system(mouse_input_system.system()) .run(); } diff --git a/examples/serializing.rs b/examples/serializing/serializing.rs similarity index 100% rename from examples/serializing.rs rename to examples/serializing/serializing.rs diff --git a/examples/shader_custom_material.rs b/examples/shader/shader_custom_material.rs similarity index 100% rename from examples/shader_custom_material.rs rename to examples/shader/shader_custom_material.rs diff --git a/examples/shader_defs.rs b/examples/shader/shader_defs.rs similarity index 100% rename from examples/shader_defs.rs rename to examples/shader/shader_defs.rs diff --git a/examples/startup_system.rs b/examples/startup_system.rs deleted file mode 100644 index c3b473b5b3..0000000000 --- a/examples/startup_system.rs +++ /dev/null @@ -1,47 +0,0 @@ -use bevy::prelude::*; - -fn main() { - App::build() - .add_default_plugins() - .add_startup_system(startup_system.system()) - .run(); -} - -/// Set up a simple scene using a "startup system". -/// Startup systems are run exactly once when the app starts up. -/// They run right before "normal" systems run. -fn startup_system( - command_buffer: &mut CommandBuffer, - mut meshes: ResourceMut>, - mut materials: ResourceMut>, -) { - let cube_handle = meshes.add(Mesh::from(shape::Cube)); - let cube_material_handle = materials.add(StandardMaterial { - albedo: Color::rgb(0.5, 0.4, 0.3), - ..Default::default() - }); - - command_buffer - .build() - // cube - .add_entity(MeshEntity { - mesh: cube_handle, - material: cube_material_handle, - translation: Translation::new(0.0, 0.0, 0.0), - ..Default::default() - }) - // light - .add_entity(LightEntity { - translation: Translation::new(4.0, -4.0, 5.0), - ..Default::default() - }) - // camera - .add_entity(CameraEntity { - local_to_world: LocalToWorld(Mat4::look_at_rh( - Vec3::new(3.0, 8.0, 5.0), - Vec3::new(0.0, 0.0, 0.0), - Vec3::new(0.0, 0.0, 1.0), - )), - ..Default::default() - }); -} diff --git a/examples/systems.rs b/examples/systems.rs deleted file mode 100644 index 82a1a4e298..0000000000 --- a/examples/systems.rs +++ /dev/null @@ -1,67 +0,0 @@ -use bevy::prelude::*; - -/// Illustrates the different ways you can declare systems -fn main() { - App::build() - .add_default_plugins() - .add_event::() - .add_startup_system(setup_system) - .add_system_init(built_system) - .add_system(simple_system.system()) - .add_system(closure_system()) - .run(); -} - -struct MyEvent(usize); - -// resources -struct A(usize); - -// components -struct X(usize); -struct Y(usize); - -// add our resources and entities -fn setup_system(world: &mut World, resources: &mut Resources) { - resources.insert(A(0)); - world.insert((), vec![(X(0), Y(1)), (X(2), Y(3))]); -} - -// runs once for each entity with the X and Y component -fn simple_system(x: Ref, mut y: RefMut) { - y.0 += 1; - println!("processed entity: {} {}", x.0, y.0); -} - -// does the same thing as the first system, but also captures the "counter" variable and uses it as internal state -fn closure_system() -> Box { - let mut counter = 0; - (move |x: Ref, mut y: RefMut| { - y.0 += 1; - println!("processed entity: {} {}", x.0, y.0); - println!("ran {} times", counter); - counter += 1; - }) - .system() -} - -// if you need more flexibility, you can define complex systems using the system builder -fn built_system(resources: &mut Resources) -> Box { - let mut my_event_reader = resources.get_event_reader::(); - SystemBuilder::new("example") - .read_resource::>() - .write_resource::() - .with_query(<(Read, Write)>::query()) - .build( - move |_command_buffer, world, (my_events, ref mut a), query| { - for event in my_event_reader.iter(&my_events) { - a.0 += event.0; - println!("modified resource A with event: {}", event.0); - } - for (x, mut y) in query.iter_mut(world) { - y.0 += 1; - println!("processed entity: {} {}", x.0, y.0); - } - }, - ) -} diff --git a/examples/ui.rs b/examples/ui/ui.rs similarity index 100% rename from examples/ui.rs rename to examples/ui/ui.rs diff --git a/examples/ui_bench.rs b/examples/ui/ui_bench.rs similarity index 100% rename from examples/ui_bench.rs rename to examples/ui/ui_bench.rs diff --git a/examples/multiple_windows.rs b/examples/window/multiple_windows.rs similarity index 100% rename from examples/multiple_windows.rs rename to examples/window/multiple_windows.rs diff --git a/src/prelude.rs b/src/prelude.rs index fb82b239f8..661f47a0cf 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -39,7 +39,7 @@ pub use crate::window::{Window, WindowDescriptor, WindowPlugin, Windows}; pub use crate::{ app::{ stage, App, AppBuilder, AppPlugin, EntityArchetype, EventReader, Events, GetEventReader, - System, + System, schedule_runner::ScheduleRunnerPlugin }, math::{self, Mat3, Mat4, Quat, Vec2, Vec3, Vec4}, AddDefaultPlugins,