organize examples and add ecs guide

This commit is contained in:
Carter Anderson 2020-05-01 13:12:47 -07:00
parent 368a1b8815
commit 7b79b3de8d
45 changed files with 366 additions and 179 deletions

View file

@ -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
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"

View file

@ -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)

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View file

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View file

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View file

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View file

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -116,14 +116,14 @@ impl AppBuilder {
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
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,
stage: &str,
mut build: impl FnMut(&mut Resources) -> T,
@ -151,17 +151,17 @@ impl AppBuilder {
self
}
pub fn add_startup_system_init<T>(
pub fn init_startup_system<T>(
&mut self,
build: impl FnMut(&mut Resources) -> T,
) -> &mut Self
where
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,
stage: &str,
mut build: impl FnMut(&mut Resources) -> T,
@ -211,7 +211,7 @@ impl AppBuilder {
self
}
pub fn add_resource_init<R>(&mut self) -> &mut Self
pub fn init_resource<R>(&mut self) -> &mut Self
where
R: FromResources + Send + Sync + 'static,
{

View file

@ -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;

View file

@ -28,7 +28,7 @@ impl Default for DiagnosticsPlugin {
impl AppPlugin for DiagnosticsPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_resource_init::<Diagnostics>();
app.init_resource::<Diagnostics>();
if self.add_defaults {
app.add_startup_system(setup_frame_time_diagnostic_system.system())
.add_system(frame_time_diagnostic_system.system());

View file

@ -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);
}
}

View file

@ -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>> {
fn get_shader_def(&self) -> Option<&'static str> {
match *self {

View file

@ -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::<AssetStorage<Mesh>>().unwrap();
let mesh_handle = mesh_storage.add(mesh);

View file

@ -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();
}

View file

@ -11,9 +11,11 @@ fn main() {
fn setup(world: &mut World, resources: &mut Resources) {
// load a texture
let mut texture_storage = resources.get_mut::<AssetStorage<Texture>>().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);

View file

@ -8,7 +8,4 @@ edition = "2018"
crate-type = ["cdylib"]
[dependencies]
bevy = { path = "../../../../bevy" }
[profile.release]
debug = true
bevy = { path = "../../../../../bevy" }

View file

@ -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();
}

217
examples/ecs/ecs_guide.rs Normal file
View 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);
}
},
)
}

View file

@ -5,7 +5,7 @@ fn main() {
.add_default_plugins()
.add_event::<MyEvent>()
.add_resource(EventTriggerState::default())
.add_resource_init::<EventListenerState>()
.init_resource::<EventListenerState>()
.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;

View 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");
}

View file

@ -6,7 +6,7 @@ use bevy::{
fn main() {
App::build()
.add_default_plugins()
.add_resource_init::<State>()
.init_resource::<State>()
.add_system(collect_input_system.system())
.add_system(move_system.system())
.add_startup_system(setup)

View file

@ -6,7 +6,7 @@ use bevy::{
fn main() {
App::build()
.add_default_plugins()
.add_resource_init::<State>()
.init_resource::<State>()
.add_system(mouse_input_system.system())
.run();
}

View file

@ -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()
});
}

View file

@ -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);
}
},
)
}

View file

@ -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,