organize examples and add ecs guide
85
Cargo.toml
|
@ -48,6 +48,7 @@ glam = "0.8.6"
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"crates/*",
|
"crates/*",
|
||||||
|
"examples/app/dynamic_plugin_loading/example_plugin"
|
||||||
]
|
]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -59,3 +60,87 @@ env_logger = "0.7"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 3
|
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"
|
|
@ -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)
|
[![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)
|
[![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)
|
[![Crates.io](https://img.shields.io/crates/d/bevy.svg)](https://crates.io/crates/bevy)
|
||||||
|
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
@ -116,14 +116,14 @@ impl AppBuilder {
|
||||||
self.add_system_to_stage(stage::UPDATE, system)
|
self.add_system_to_stage(stage::UPDATE, system)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_system_init<T>(&mut self, build: impl FnMut(&mut Resources) -> T) -> &mut Self
|
pub fn init_system<T>(&mut self, build: impl FnMut(&mut Resources) -> T) -> &mut Self
|
||||||
where
|
where
|
||||||
T: Into<System>,
|
T: Into<System>,
|
||||||
{
|
{
|
||||||
self.add_system_to_stage_init(stage::UPDATE, build)
|
self.init_system_to_stage(stage::UPDATE, build)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_system_to_stage_init<T>(
|
pub fn init_system_to_stage<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
stage: &str,
|
stage: &str,
|
||||||
mut build: impl FnMut(&mut Resources) -> T,
|
mut build: impl FnMut(&mut Resources) -> T,
|
||||||
|
@ -151,17 +151,17 @@ impl AppBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_startup_system_init<T>(
|
pub fn init_startup_system<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
build: impl FnMut(&mut Resources) -> T,
|
build: impl FnMut(&mut Resources) -> T,
|
||||||
) -> &mut Self
|
) -> &mut Self
|
||||||
where
|
where
|
||||||
T: Into<System>,
|
T: Into<System>,
|
||||||
{
|
{
|
||||||
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<T>(
|
pub fn init_startup_system_to_stage<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
stage: &str,
|
stage: &str,
|
||||||
mut build: impl FnMut(&mut Resources) -> T,
|
mut build: impl FnMut(&mut Resources) -> T,
|
||||||
|
@ -211,7 +211,7 @@ impl AppBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_resource_init<R>(&mut self) -> &mut Self
|
pub fn init_resource<R>(&mut self) -> &mut Self
|
||||||
where
|
where
|
||||||
R: FromResources + Send + Sync + 'static,
|
R: FromResources + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,6 +19,20 @@ pub struct ScheduleRunnerPlugin {
|
||||||
pub run_mode: RunMode,
|
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 {
|
impl AppPlugin for ScheduleRunnerPlugin {
|
||||||
fn build(&self, app: &mut AppBuilder) {
|
fn build(&self, app: &mut AppBuilder) {
|
||||||
let run_mode = self.run_mode;
|
let run_mode = self.run_mode;
|
||||||
|
|
|
@ -28,7 +28,7 @@ impl Default for DiagnosticsPlugin {
|
||||||
|
|
||||||
impl AppPlugin for DiagnosticsPlugin {
|
impl AppPlugin for DiagnosticsPlugin {
|
||||||
fn build(&self, app: &mut AppBuilder) {
|
fn build(&self, app: &mut AppBuilder) {
|
||||||
app.add_resource_init::<Diagnostics>();
|
app.init_resource::<Diagnostics>();
|
||||||
if self.add_defaults {
|
if self.add_defaults {
|
||||||
app.add_startup_system(setup_frame_time_diagnostic_system.system())
|
app.add_startup_system(setup_frame_time_diagnostic_system.system())
|
||||||
.add_system(frame_time_diagnostic_system.system());
|
.add_system(frame_time_diagnostic_system.system());
|
||||||
|
|
|
@ -70,10 +70,10 @@ impl AppPlugin for RenderPlugin {
|
||||||
.add_resource(AssetBatchers::default())
|
.add_resource(AssetBatchers::default())
|
||||||
// core systems
|
// core systems
|
||||||
.add_system(entity_render_resource_assignments_system())
|
.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_specializer_system())
|
||||||
.add_system_to_stage(stage::POST_UPDATE, mesh::mesh_batcher_system())
|
.add_system_to_stage(stage::POST_UPDATE, mesh::mesh_batcher_system())
|
||||||
// render resource provider systems
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,29 +36,6 @@ impl Asset<TextureType> for Texture {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_texels(size: usize) -> Vec<u8> {
|
|
||||||
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<Handle<Texture>> {
|
impl ShaderDefSuffixProvider for Option<Handle<Texture>> {
|
||||||
fn get_shader_def(&self) -> Option<&'static str> {
|
fn get_shader_def(&self) -> Option<&'static str> {
|
||||||
match *self {
|
match *self {
|
||||||
|
|
|
@ -4,15 +4,13 @@ fn main() {
|
||||||
App::build()
|
App::build()
|
||||||
.add_default_plugins()
|
.add_default_plugins()
|
||||||
.add_startup_system(setup)
|
.add_startup_system(setup)
|
||||||
.add_system_init(bevy::input::system::exit_on_esc_system)
|
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(world: &mut World, resources: &mut Resources) {
|
fn setup(world: &mut World, resources: &mut Resources) {
|
||||||
// load the mesh
|
// load the mesh
|
||||||
let mesh = gltf::load_gltf("examples/assets/Monkey.gltf")
|
let model_path = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/models/Monkey.gltf");
|
||||||
.unwrap()
|
let mesh = gltf::load_gltf(&model_path).unwrap().unwrap();
|
||||||
.unwrap();
|
|
||||||
let mut mesh_storage = resources.get_mut::<AssetStorage<Mesh>>().unwrap();
|
let mut mesh_storage = resources.get_mut::<AssetStorage<Mesh>>().unwrap();
|
||||||
let mesh_handle = mesh_storage.add(mesh);
|
let mesh_handle = mesh_storage.add(mesh);
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_input::system::exit_on_esc_system;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::build()
|
App::build()
|
||||||
.add_default_plugins()
|
.add_default_plugins()
|
||||||
.add_startup_system(setup)
|
.add_startup_system(setup)
|
||||||
.add_system_init(exit_on_esc_system)
|
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,11 @@ fn main() {
|
||||||
fn setup(world: &mut World, resources: &mut Resources) {
|
fn setup(world: &mut World, resources: &mut Resources) {
|
||||||
// load a texture
|
// load a texture
|
||||||
let mut texture_storage = resources.get_mut::<AssetStorage<Texture>>().unwrap();
|
let mut texture_storage = resources.get_mut::<AssetStorage<Texture>>().unwrap();
|
||||||
let texture = Texture::load(TextureType::Png(
|
let texture_path = concat!(
|
||||||
concat!(env!("CARGO_MANIFEST_DIR"), "/assets/bevy_logo_dark_big.png").to_string(),
|
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 aspect = texture.height as f32 / texture.width as f32;
|
||||||
let texture_handle = texture_storage.add(texture);
|
let texture_handle = texture_storage.add(texture);
|
||||||
|
|
|
@ -8,7 +8,4 @@ edition = "2018"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = { path = "../../../../bevy" }
|
bevy = { path = "../../../../../bevy" }
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
debug = true
|
|
|
@ -1,7 +1,4 @@
|
||||||
use bevy::{
|
use bevy::prelude::*;
|
||||||
app::schedule_runner::{RunMode, ScheduleRunnerPlugin},
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
// This example disables the default plugins by not registering them during setup.
|
// This example disables the default plugins by not registering them during setup.
|
||||||
|
@ -14,19 +11,13 @@ use std::time::Duration;
|
||||||
fn main() {
|
fn main() {
|
||||||
// this app runs once
|
// this app runs once
|
||||||
App::build()
|
App::build()
|
||||||
.add_plugin(ScheduleRunnerPlugin {
|
.add_plugin(ScheduleRunnerPlugin::run_once())
|
||||||
run_mode: RunMode::Once,
|
|
||||||
})
|
|
||||||
.add_system(hello_world_system.system())
|
.add_system(hello_world_system.system())
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
// this app loops forever at 60 fps
|
// this app loops forever at 60 fps
|
||||||
App::build()
|
App::build()
|
||||||
.add_plugin(ScheduleRunnerPlugin {
|
.add_plugin(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(1.0 / 60.0)))
|
||||||
run_mode: RunMode::Loop {
|
|
||||||
wait: Some(Duration::from_secs_f64(1.0 / 60.0)),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.add_system(hello_world_system.system())
|
.add_system(hello_world_system.system())
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
217
examples/ecs/ecs_guide.rs
Normal file
|
@ -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::<B>()
|
||||||
|
.init_resource::<State>()
|
||||||
|
// 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<A>, mut b: ResourceMut<B>) {
|
||||||
|
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<X>, mut y: RefMut<Y>) {
|
||||||
|
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<A>, x: Ref<X>, mut y: RefMut<Y>) {
|
||||||
|
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<A>) {
|
||||||
|
// 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<dyn Schedulable> 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<dyn Schedulable> type!
|
||||||
|
fn closure_system() -> Box<dyn Schedulable> {
|
||||||
|
let mut counter = 0;
|
||||||
|
(move |x: Ref<X>, mut y: RefMut<Y>| {
|
||||||
|
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<State>, x: Ref<X>, mut y: RefMut<Y>) {
|
||||||
|
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<dyn Schedulable> {
|
||||||
|
let mut counter = 0;
|
||||||
|
SystemBuilder::new("complex_system")
|
||||||
|
.read_resource::<A>()
|
||||||
|
.write_resource::<B>()
|
||||||
|
// this query is equivalent to the system we saw above: system(x: Ref<X>, y: RefMut<Y>)
|
||||||
|
.with_query(<(Read<X>, Write<Y>)>::query())
|
||||||
|
// this query only runs on entities with an X component that has changed since the last update
|
||||||
|
.with_query(<Read<X>>::query().filter(changed::<X>()))
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ fn main() {
|
||||||
.add_default_plugins()
|
.add_default_plugins()
|
||||||
.add_event::<MyEvent>()
|
.add_event::<MyEvent>()
|
||||||
.add_resource(EventTriggerState::default())
|
.add_resource(EventTriggerState::default())
|
||||||
.add_resource_init::<EventListenerState>()
|
.init_resource::<EventListenerState>()
|
||||||
.add_system(event_trigger_system.system())
|
.add_system(event_trigger_system.system())
|
||||||
.add_system(event_listener_system.system())
|
.add_system(event_listener_system.system())
|
||||||
.run();
|
.run();
|
||||||
|
@ -29,7 +29,7 @@ fn event_trigger_system(
|
||||||
state.elapsed += time.delta_seconds;
|
state.elapsed += time.delta_seconds;
|
||||||
if state.elapsed > 1.0 {
|
if state.elapsed > 1.0 {
|
||||||
my_events.send(MyEvent {
|
my_events.send(MyEvent {
|
||||||
message: "Hello World".to_string(),
|
message: "MyEvent just happened!".to_string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
state.elapsed = 0.0;
|
state.elapsed = 0.0;
|
22
examples/ecs/startup_system.rs
Normal file
|
@ -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");
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ use bevy::{
|
||||||
fn main() {
|
fn main() {
|
||||||
App::build()
|
App::build()
|
||||||
.add_default_plugins()
|
.add_default_plugins()
|
||||||
.add_resource_init::<State>()
|
.init_resource::<State>()
|
||||||
.add_system(collect_input_system.system())
|
.add_system(collect_input_system.system())
|
||||||
.add_system(move_system.system())
|
.add_system(move_system.system())
|
||||||
.add_startup_system(setup)
|
.add_startup_system(setup)
|
|
@ -6,7 +6,7 @@ use bevy::{
|
||||||
fn main() {
|
fn main() {
|
||||||
App::build()
|
App::build()
|
||||||
.add_default_plugins()
|
.add_default_plugins()
|
||||||
.add_resource_init::<State>()
|
.init_resource::<State>()
|
||||||
.add_system(mouse_input_system.system())
|
.add_system(mouse_input_system.system())
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
|
@ -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<AssetStorage<Mesh>>,
|
|
||||||
mut materials: ResourceMut<AssetStorage<StandardMaterial>>,
|
|
||||||
) {
|
|
||||||
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()
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
/// Illustrates the different ways you can declare systems
|
|
||||||
fn main() {
|
|
||||||
App::build()
|
|
||||||
.add_default_plugins()
|
|
||||||
.add_event::<MyEvent>()
|
|
||||||
.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<X>, mut y: RefMut<Y>) {
|
|
||||||
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<dyn Schedulable> {
|
|
||||||
let mut counter = 0;
|
|
||||||
(move |x: Ref<X>, mut y: RefMut<Y>| {
|
|
||||||
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<dyn Schedulable> {
|
|
||||||
let mut my_event_reader = resources.get_event_reader::<MyEvent>();
|
|
||||||
SystemBuilder::new("example")
|
|
||||||
.read_resource::<Events<MyEvent>>()
|
|
||||||
.write_resource::<A>()
|
|
||||||
.with_query(<(Read<X>, Write<Y>)>::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);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -39,7 +39,7 @@ pub use crate::window::{Window, WindowDescriptor, WindowPlugin, Windows};
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
app::{
|
app::{
|
||||||
stage, App, AppBuilder, AppPlugin, EntityArchetype, EventReader, Events, GetEventReader,
|
stage, App, AppBuilder, AppPlugin, EntityArchetype, EventReader, Events, GetEventReader,
|
||||||
System,
|
System, schedule_runner::ScheduleRunnerPlugin
|
||||||
},
|
},
|
||||||
math::{self, Mat3, Mat4, Quat, Vec2, Vec3, Vec4},
|
math::{self, Mat3, Mat4, Quat, Vec2, Vec3, Vec4},
|
||||||
AddDefaultPlugins,
|
AddDefaultPlugins,
|
||||||
|
|