From 525ccd50838e52905cb15cbaaf80f25ad21dca8b Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 20:00:44 -0400 Subject: [PATCH 01/39] Clean up on Examples README To make it easier to add the new example. Table auto-formatted with Markdownlint VSCode extension --- examples/README.md | 274 ++++++++++++++++++++++----------------------- 1 file changed, 137 insertions(+), 137 deletions(-) diff --git a/examples/README.md b/examples/README.md index 096afc63e4193..fa66f675e7833 100644 --- a/examples/README.md +++ b/examples/README.md @@ -71,176 +71,176 @@ git checkout v0.4.0 ## Hello, World! -Example | Main | Description ---- | --- | --- -`hello_world` | [`hello_world.rs`](./hello_world.rs) | Runs a minimal example that outputs "hello world" +| Example | Main | Description | +| ------------- | ------------------------------------ | ------------------------------------------------- | +| `hello_world` | [`hello_world.rs`](./hello_world.rs) | Runs a minimal example that outputs "hello world" | # Cross-Platform Examples ## 2D Rendering -Example | Main | Description ---- | --- | --- -`contributors` | [`2d/contributors.rs`](./2d/contributors.rs) | Displays each contributor as a bouncy bevy-ball! -`many_sprites` | [`2d/many_sprites.rs`](./2d/many_sprites.rs) | Displays many sprites in a grid arragement! Used for performance testing. -`mesh` | [`2d/mesh.rs`](./2d/mesh.rs) | Renders a custom mesh -`sprite` | [`2d/sprite.rs`](./2d/sprite.rs) | Renders a sprite -`sprite_sheet` | [`2d/sprite_sheet.rs`](./2d/sprite_sheet.rs) | Renders an animated sprite -`text2d` | [`2d/text2d.rs`](./2d/text2d.rs) | Generates text in 2d -`sprite_flipping` | [`2d/sprite_flipping.rs`](./2d/sprite_flipping.rs) | Renders a sprite flipped along an axis -`texture_atlas` | [`2d/texture_atlas.rs`](./2d/texture_atlas.rs) | Generates a texture atlas (sprite sheet) from individual sprites +| Example | Main | Description | +| ----------------- | -------------------------------------------------- | -------------------------------------------------------------------------- | +| `contributors` | [`2d/contributors.rs`](./2d/contributors.rs) | Displays each contributor as a bouncy bevy-ball! | +| `many_sprites` | [`2d/many_sprites.rs`](./2d/many_sprites.rs) | Displays many sprites in a grid arrangement! Used for performance testing. | +| `mesh` | [`2d/mesh.rs`](./2d/mesh.rs) | Renders a custom mesh | +| `sprite` | [`2d/sprite.rs`](./2d/sprite.rs) | Renders a sprite | +| `sprite_sheet` | [`2d/sprite_sheet.rs`](./2d/sprite_sheet.rs) | Renders an animated sprite | +| `text2d` | [`2d/text2d.rs`](./2d/text2d.rs) | Generates text in 2d | +| `sprite_flipping` | [`2d/sprite_flipping.rs`](./2d/sprite_flipping.rs) | Renders a sprite flipped along an axis | +| `texture_atlas` | [`2d/texture_atlas.rs`](./2d/texture_atlas.rs) | Generates a texture atlas (sprite sheet) from individual sprites | ## 3D Rendering -Example | File | Description ---- | --- | --- -`3d_scene` | [`3d/3d_scene.rs`](./3d/3d_scene.rs) | Simple 3D scene with basic shapes and lighting -`load_gltf` | [`3d/load_gltf.rs`](./3d/load_gltf.rs) | Loads and renders a gltf file as a scene -`msaa` | [`3d/msaa.rs`](./3d/msaa.rs) | Configures MSAA (Multi-Sample Anti-Aliasing) for smoother edges -`orthographic` | [`3d/orthographic.rs`](./3d/orthographic.rs) | Shows how to create a 3D orthographic view (for isometric-look games or CAD applications) -`parenting` | [`3d/parenting.rs`](./3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations -`pbr` | [`3d/pbr.rs`](./3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties -`render_to_texture` | [`3d/render_to_texture.rs`](./3d/render_to_texture.rs) | Shows how to render to texture -`spawner` | [`3d/spawner.rs`](./3d/spawner.rs) | Renders a large number of cubes with changing position and material -`texture` | [`3d/texture.rs`](./3d/texture.rs) | Shows configuration of texture materials -`update_gltf_scene` | [`3d/update_gltf_scene.rs`](./3d/update_gltf_scene.rs) | Update a scene from a gltf file, either by spawning the scene as a child of another entity, or by accessing the entities of the scene -`wireframe` | [`3d/wireframe.rs`](./3d/wireframe.rs) | Showcases wireframe rendering -`z_sort_debug` | [`3d/z_sort_debug.rs`](./3d/z_sort_debug.rs) | Visualizes camera Z-ordering +| Example | File | Description | +| ------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | +| `3d_scene` | [`3d/3d_scene.rs`](./3d/3d_scene.rs) | Simple 3D scene with basic shapes and lighting | +| `load_gltf` | [`3d/load_gltf.rs`](./3d/load_gltf.rs) | Loads and renders a gltf file as a scene | +| `msaa` | [`3d/msaa.rs`](./3d/msaa.rs) | Configures MSAA (Multi-Sample Anti-Aliasing) for smoother edges | +| `orthographic` | [`3d/orthographic.rs`](./3d/orthographic.rs) | Shows how to create a 3D orthographic view (for isometric-look games or CAD applications) | +| `parenting` | [`3d/parenting.rs`](./3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations | +| `pbr` | [`3d/pbr.rs`](./3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties | +| `render_to_texture` | [`3d/render_to_texture.rs`](./3d/render_to_texture.rs) | Shows how to render to texture | +| `spawner` | [`3d/spawner.rs`](./3d/spawner.rs) | Renders a large number of cubes with changing position and material | +| `texture` | [`3d/texture.rs`](./3d/texture.rs) | Shows configuration of texture materials | +| `update_gltf_scene` | [`3d/update_gltf_scene.rs`](./3d/update_gltf_scene.rs) | Update a scene from a gltf file, either by spawning the scene as a child of another entity, or by accessing the entities of the scene | +| `wireframe` | [`3d/wireframe.rs`](./3d/wireframe.rs) | Showcases wireframe rendering | +| `z_sort_debug` | [`3d/z_sort_debug.rs`](./3d/z_sort_debug.rs) | Visualizes camera Z-ordering | ## Application -Example | File | Description ---- | --- | --- -`custom_loop` | [`app/custom_loop.rs`](./app/custom_loop.rs) | Demonstrates how to create a custom runner (to update an app manually). -`drag_and_drop` | [`app/drag_and_drop.rs`](./app/drag_and_drop.rs) | An example that shows how to handle drag and drop in an app. -`empty` | [`app/empty.rs`](./app/empty.rs) | An empty application (does nothing) -`empty_defaults` | [`app/empty_defaults.rs`](./app/empty_defaults.rs) | An empty application with default plugins -`headless` | [`app/headless.rs`](./app/headless.rs) | An application that runs without default plugins -`logs` | [`app/logs.rs`](./app/logs.rs) | Illustrate how to use generate log output -`plugin` | [`app/plugin.rs`](./app/plugin.rs) | Demonstrates the creation and registration of a custom plugin -`plugin_group` | [`app/plugin_group.rs`](./app/plugin_group.rs) | Demonstrates the creation and registration of a custom plugin group -`return_after_run` | [`app/return_after_run.rs`](./app/return_after_run.rs) | Show how to return to main after the Bevy app has exited -`thread_pool_resources` | [`app/thread_pool_resources.rs`](./app/thread_pool_resources.rs) | Creates and customizes the internal thread pool +| Example | File | Description | +| ----------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------- | +| `custom_loop` | [`app/custom_loop.rs`](./app/custom_loop.rs) | Demonstrates how to create a custom runner (to update an app manually). | +| `drag_and_drop` | [`app/drag_and_drop.rs`](./app/drag_and_drop.rs) | An example that shows how to handle drag and drop in an app. | +| `empty` | [`app/empty.rs`](./app/empty.rs) | An empty application (does nothing) | +| `empty_defaults` | [`app/empty_defaults.rs`](./app/empty_defaults.rs) | An empty application with default plugins | +| `headless` | [`app/headless.rs`](./app/headless.rs) | An application that runs without default plugins | +| `logs` | [`app/logs.rs`](./app/logs.rs) | Illustrate how to use generate log output | +| `plugin` | [`app/plugin.rs`](./app/plugin.rs) | Demonstrates the creation and registration of a custom plugin | +| `plugin_group` | [`app/plugin_group.rs`](./app/plugin_group.rs) | Demonstrates the creation and registration of a custom plugin group | +| `return_after_run` | [`app/return_after_run.rs`](./app/return_after_run.rs) | Show how to return to main after the Bevy app has exited | +| `thread_pool_resources` | [`app/thread_pool_resources.rs`](./app/thread_pool_resources.rs) | Creates and customizes the internal thread pool | ## Assets -Example | File | Description ---- | --- | --- -`asset_loading` | [`asset/asset_loading.rs`](./asset/asset_loading.rs) | Demonstrates various methods to load assets -`custom_asset` | [`asset/custom_asset.rs`](./asset/custom_asset.rs) | Implements a custom asset loader -`custom_asset_io` | [`asset/custom_asset_io.rs`](./asset/custom_asset_io.rs) | Implements a custom asset io loader -`hot_asset_reloading` | [`asset/hot_asset_reloading.rs`](./asset/hot_asset_reloading.rs) | Demonstrates automatic reloading of assets when modified on disk +| Example | File | Description | +| --------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------- | +| `asset_loading` | [`asset/asset_loading.rs`](./asset/asset_loading.rs) | Demonstrates various methods to load assets | +| `custom_asset` | [`asset/custom_asset.rs`](./asset/custom_asset.rs) | Implements a custom asset loader | +| `custom_asset_io` | [`asset/custom_asset_io.rs`](./asset/custom_asset_io.rs) | Implements a custom asset I/O loader | +| `hot_asset_reloading` | [`asset/hot_asset_reloading.rs`](./asset/hot_asset_reloading.rs) | Demonstrates automatic reloading of assets when modified on disk | ## Audio -Example | File | Description ---- | --- | --- -`audio` | [`audio/audio.rs`](./audio/audio.rs) | Shows how to load and play an audio file +| Example | File | Description | +| ------- | ------------------------------------ | ---------------------------------------- | +| `audio` | [`audio/audio.rs`](./audio/audio.rs) | Shows how to load and play an audio file | ## Diagnostics -Example | File | Description ---- | --- | --- -`custom_diagnostic` | [`diagnostics/custom_diagnostic.rs`](./diagnostics/custom_diagnostic.rs) | Shows how to create a custom diagnostic -`log_diagnostics` | [`diagnostics/log_diagnostics.rs`](./diagnostics/log_diagnostics.rs) | Add a plugin that logs diagnostics to the console +| Example | File | Description | +| ------------------- | ------------------------------------------------------------------------ | ------------------------------------------------- | +| `custom_diagnostic` | [`diagnostics/custom_diagnostic.rs`](./diagnostics/custom_diagnostic.rs) | Shows how to create a custom diagnostic | +| `log_diagnostics` | [`diagnostics/log_diagnostics.rs`](./diagnostics/log_diagnostics.rs) | Add a plugin that logs diagnostics to the console | ## ECS (Entity Component System) -Example | File | Description ---- | --- | --- -`ecs_guide` | [`ecs/ecs_guide.rs`](./ecs/ecs_guide.rs) | Full guide to Bevy's ECS -`change_detection` | [`ecs/change_detection.rs`](./ecs/change_detection.rs) | Change detection on components -`event` | [`ecs/event.rs`](./ecs/event.rs) | Illustrates event creation, activation, and reception -`fixed_timestep` | [`ecs/fixed_timestep.rs`](./ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick -`hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities -`parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` -`query_bundle` | [`ecs/query_bundle.rs`](./ecs/query_bundle.rs) | Shows how to query entities that contain components in a `Bundle` -`removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame. -`startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) -`state` | [`ecs/state.rs`](./ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state -`system_chaining` | [`ecs/system_chaining.rs`](./ecs/system_chaining.rs) | Chain two systems together, specifying a return type in a system (such as `Result`) -`system_param` | [`ecs/system_param.rs`](./ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam` -`system_sets` | [`ecs/system_sets.rs`](./ecs/system_sets.rs) | Shows `SystemSet` use along with run criterion -`timers` | [`ecs/timers.rs`](./ecs/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state +| Example | File | Description | +| ------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | +| `ecs_guide` | [`ecs/ecs_guide.rs`](./ecs/ecs_guide.rs) | Full guide to Bevy's ECS | +| `change_detection` | [`ecs/change_detection.rs`](./ecs/change_detection.rs) | Change detection on components | +| `event` | [`ecs/event.rs`](./ecs/event.rs) | Illustrates event creation, activation, and reception | +| `fixed_timestep` | [`ecs/fixed_timestep.rs`](./ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick | +| `hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities | +| `parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` | +| `query_bundle` | [`ecs/query_bundle.rs`](./ecs/query_bundle.rs) | Shows how to query entities that contain components in a `Bundle` | +| `removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame. | +| `startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) | +| `state` | [`ecs/state.rs`](./ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state | +| `system_chaining` | [`ecs/system_chaining.rs`](./ecs/system_chaining.rs) | Chain two systems together, specifying a return type in a system (such as `Result`) | +| `system_param` | [`ecs/system_param.rs`](./ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam` | +| `system_sets` | [`ecs/system_sets.rs`](./ecs/system_sets.rs) | Shows `SystemSet` use along with run criterion | +| `timers` | [`ecs/timers.rs`](./ecs/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state | ## Games -Example | File | Description ---- | --- | --- -`alien_cake_addict` | [`game/alien_cake_addict.rs`](./game/alien_cake_addict.rs) | Eat the cakes. Eat them all. An example 3D game -`breakout` | [`game/breakout.rs`](./game/breakout.rs) | An implementation of the classic game "Breakout" +| Example | File | Description | +| ------------------- | ---------------------------------------------------------- | ------------------------------------------------ | +| `alien_cake_addict` | [`game/alien_cake_addict.rs`](./game/alien_cake_addict.rs) | Eat the cakes. Eat them all. An example 3D game | +| `breakout` | [`game/breakout.rs`](./game/breakout.rs) | An implementation of the classic game "Breakout" | ## Input -Example | File | Description ---- | --- | --- -`char_input_events` | [`input/char_input_events.rs`](./input/char_input_events.rs) | Prints out all chars as they are inputted. -`gamepad_input` | [`input/gamepad_input.rs`](./input/gamepad_input.rs) | Shows handling of gamepad input, connections, and disconnections -`gamepad_input_events` | [`input/gamepad_input_events.rs`](./input/gamepad_input_events.rs) | Iterates and prints gamepad input and connection events -`keyboard_input` | [`input/keyboard_input.rs`](./input/keyboard_input.rs) | Demonstrates handling a key press/release -`keyboard_input_events` | [`input/keyboard_input_events.rs`](./input/keyboard_input_events.rs) | Prints out all keyboard events -`keyboard_modifiers` | [`input/keyboard_modifiers.rs`](./input/keyboard_modifiers.rs) | Demonstrates using key modifiers (ctrl, shift) -`mouse_input` | [`input/mouse_input.rs`](./input/mouse_input.rs) | Demonstrates handling a mouse button press/release -`mouse_input_events` | [`input/mouse_input_events.rs`](./input/mouse_input_events.rs) | Prints out all mouse events (buttons, movement, etc.) -`touch_input` | [`input/touch_input.rs`](./input/touch_input.rs) | Displays touch presses, releases, and cancels -`touch_input_events` | [`input/touch_input_events.rs`](./input/touch_input_events.rs) | Prints out all touch inputs +| Example | File | Description | +| ----------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------- | +| `char_input_events` | [`input/char_input_events.rs`](./input/char_input_events.rs) | Prints out all chars as they are inputted. | +| `gamepad_input` | [`input/gamepad_input.rs`](./input/gamepad_input.rs) | Shows handling of gamepad input, connections, and disconnections | +| `gamepad_input_events` | [`input/gamepad_input_events.rs`](./input/gamepad_input_events.rs) | Iterates and prints gamepad input and connection events | +| `keyboard_input` | [`input/keyboard_input.rs`](./input/keyboard_input.rs) | Demonstrates handling a key press/release | +| `keyboard_input_events` | [`input/keyboard_input_events.rs`](./input/keyboard_input_events.rs) | Prints out all keyboard events | +| `keyboard_modifiers` | [`input/keyboard_modifiers.rs`](./input/keyboard_modifiers.rs) | Demonstrates using key modifiers (ctrl, shift) | +| `mouse_input` | [`input/mouse_input.rs`](./input/mouse_input.rs) | Demonstrates handling a mouse button press/release | +| `mouse_input_events` | [`input/mouse_input_events.rs`](./input/mouse_input_events.rs) | Prints out all mouse events (buttons, movement, etc.) | +| `touch_input` | [`input/touch_input.rs`](./input/touch_input.rs) | Displays touch presses, releases, and cancels | +| `touch_input_events` | [`input/touch_input_events.rs`](./input/touch_input_events.rs) | Prints out all touch inputs | ## Reflection -Example | File | Description ---- | --- | --- -`reflection` | [`reflection/reflection.rs`](reflection/reflection.rs) | Demonstrates how reflection in Bevy provides a way to dynamically interact with Rust types -`generic_reflection` | [`reflection/generic_reflection.rs`](reflection/generic_reflection.rs) | Registers concrete instances of generic types that may be used with reflection -`reflection_types` | [`reflection/reflection_types.rs`](reflection/reflection_types.rs) | Illustrates the various reflection types available -`trait_reflection` | [`reflection/trait_reflection.rs`](reflection/trait_reflection.rs) | Allows reflection with trait objects +| Example | File | Description | +| -------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| `reflection` | [`reflection/reflection.rs`](reflection/reflection.rs) | Demonstrates how reflection in Bevy provides a way to dynamically interact with Rust types | +| `generic_reflection` | [`reflection/generic_reflection.rs`](reflection/generic_reflection.rs) | Registers concrete instances of generic types that may be used with reflection | +| `reflection_types` | [`reflection/reflection_types.rs`](reflection/reflection_types.rs) | Illustrates the various reflection types available | +| `trait_reflection` | [`reflection/trait_reflection.rs`](reflection/trait_reflection.rs) | Allows reflection with trait objects | ## Scene -Example | File | Description ---- | --- | --- -`scene` | [`scene/scene.rs`](./scene/scene.rs) | Demonstrates loading from and saving scenes to files +| Example | File | Description | +| ------- | ------------------------------------ | ---------------------------------------------------- | +| `scene` | [`scene/scene.rs`](./scene/scene.rs) | Demonstrates loading from and saving scenes to files | ## Shaders -Example | File | Description ---- | --- | --- -`animate_shader` | [`shader/animate_shader.rs`](./shader/animate_shader.rs) | Shows how to animate a shader by accessing a time uniform variable -`array_texture` | [`shader/array_texture.rs`](./shader/array_texture.rs) | Illustrates how to create a texture for use with a texture2DArray shader uniform variable -`hot_shader_reloading` | [`shader/hot_shader_reloading.rs`](./shader/hot_shader_reloading.rs) | Illustrates how to load shaders such that they can be edited while the example is still running -`mesh_custom_attribute` | [`shader/mesh_custom_attribute.rs`](./shader/mesh_custom_attribute.rs) | Illustrates how to add a custom attribute to a mesh and use it in a custom shader -`shader_custom_material` | [`shader/shader_custom_material.rs`](./shader/shader_custom_material.rs) | Illustrates creating a custom material and a shader that uses it -`shader_defs` | [`shader/shader_defs.rs`](./shader/shader_defs.rs) | Demonstrates creating a custom material that uses "shaders defs" (a tool to selectively toggle parts of a shader) +| Example | File | Description | +| ------------------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | +| `animate_shader` | [`shader/animate_shader.rs`](./shader/animate_shader.rs) | Shows how to animate a shader by accessing a time uniform variable | +| `array_texture` | [`shader/array_texture.rs`](./shader/array_texture.rs) | Illustrates how to create a texture for use with a texture2DArray shader uniform variable | +| `hot_shader_reloading` | [`shader/hot_shader_reloading.rs`](./shader/hot_shader_reloading.rs) | Illustrates how to load shaders such that they can be edited while the example is still running | +| `mesh_custom_attribute` | [`shader/mesh_custom_attribute.rs`](./shader/mesh_custom_attribute.rs) | Illustrates how to add a custom attribute to a mesh and use it in a custom shader | +| `shader_custom_material` | [`shader/shader_custom_material.rs`](./shader/shader_custom_material.rs) | Illustrates creating a custom material and a shader that uses it | +| `shader_defs` | [`shader/shader_defs.rs`](./shader/shader_defs.rs) | Demonstrates creating a custom material that uses "shaders defs" (a tool to selectively toggle parts of a shader) | ## Tests -Example | File | Description ---- | --- | --- -`how_to_test_systems` | [`../tests/how_to_test_systems.rs`](../tests/how_to_test_systems.rs) | How to test systems with commands, queries or resources +| Example | File | Description | +| --------------------- | -------------------------------------------------------------------- | ------------------------------------------------------- | +| `how_to_test_systems` | [`../tests/how_to_test_systems.rs`](../tests/how_to_test_systems.rs) | How to test systems with commands, queries or resources | ## Tools -Example | File | Description ---- | --- | --- -`bevymark` | [`tools/bevymark.rs`](./tools/bevymark.rs) | A heavy workload to benchmark your system with Bevy +| Example | File | Description | +| ---------- | ------------------------------------------ | --------------------------------------------------- | +| `bevymark` | [`tools/bevymark.rs`](./tools/bevymark.rs) | A heavy workload to benchmark your system with Bevy | ## UI (User Interface) -Example | File | Description ---- | --- | --- -`button` | [`ui/button.rs`](./ui/button.rs) | Illustrates creating and updating a button -`font_atlas_debug` | [`ui/font_atlas_debug.rs`](./ui/font_atlas_debug.rs) | Illustrates how FontAtlases are populated (used to optimize text rendering internally) -`text` | [`ui/text.rs`](./ui/text.rs) | Illustrates creating and updating text -`text_debug` | [`ui/text_debug.rs`](./ui/text_debug.rs) | An example for debugging text layout -`ui` | [`ui/ui.rs`](./ui/ui.rs) | Illustrates various features of Bevy UI +| Example | File | Description | +| ------------------ | ---------------------------------------------------- | -------------------------------------------------------------------------------------- | +| `button` | [`ui/button.rs`](./ui/button.rs) | Illustrates creating and updating a button | +| `font_atlas_debug` | [`ui/font_atlas_debug.rs`](./ui/font_atlas_debug.rs) | Illustrates how FontAtlases are populated (used to optimize text rendering internally) | +| `text` | [`ui/text.rs`](./ui/text.rs) | Illustrates creating and updating text | +| `text_debug` | [`ui/text_debug.rs`](./ui/text_debug.rs) | An example for debugging text layout | +| `ui` | [`ui/ui.rs`](./ui/ui.rs) | Illustrates various features of Bevy UI | ## Window -Example | File | Description ---- | --- | --- -`clear_color` | [`window/clear_color.rs`](./window/clear_color.rs) | Creates a solid color window -`multiple_windows` | [`window/multiple_windows.rs`](./window/multiple_windows.rs) | Creates two windows and cameras viewing the same mesh -`scale_factor_override` | [`window/scale_factor_override.rs`](./window/scale_factor_override.rs) | Illustrates how to customize the default window settings -`window_settings` | [`window/window_settings.rs`](./window/window_settings.rs) | Demonstrates customizing default window settings +| Example | File | Description | +| ----------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------- | +| `clear_color` | [`window/clear_color.rs`](./window/clear_color.rs) | Creates a solid color window | +| `multiple_windows` | [`window/multiple_windows.rs`](./window/multiple_windows.rs) | Creates two windows and cameras viewing the same mesh | +| `scale_factor_override` | [`window/scale_factor_override.rs`](./window/scale_factor_override.rs) | Illustrates how to customize the default window settings | +| `window_settings` | [`window/window_settings.rs`](./window/window_settings.rs) | Demonstrates customizing default window settings | # Platform-Specific Examples @@ -284,7 +284,7 @@ Bevy by default targets Android API level 29 in its examples which is the Users of older phones may want to use an older API when testing. -To use a different API, the following fields must be updated in Cargo.toml: +To use a different API, the following fields must be updated in `Cargo.toml`: ```toml [package.metadata.android] @@ -292,9 +292,9 @@ target_sdk_version = >>API<< min_sdk_version = >>API or less<< ``` -Example | File | Description ---- | --- | --- -`android` | [`android/android.rs`](./android/android.rs) | The `3d/3d_scene.rs` example for Android +| Example | File | Description | +| --------- | -------------------------------------------- | ---------------------------------------- | +| `android` | [`android/android.rs`](./android/android.rs) | The `3d/3d_scene.rs` example for Android | ## iOS @@ -322,13 +322,13 @@ specify the simulator device UUID via: DEVICE_ID=${YOUR_DEVICE_ID} make run ``` -If you'd like to see xcode do stuff, you can run +If you'd like to see Xcode do stuff, you can run ```sh open bevy_ios_example.xcodeproj/ ``` -which will open xcode. You then must push the zoom zoom play button and wait +which will open Xcode. You then must push the zoom zoom play button and wait for the magic. The Xcode build GUI will by default build the rust library for both @@ -340,9 +340,9 @@ variable in the "`cargo_ios` target" to be either `x86_64-apple-ios` or Note: if you update this variable in Xcode, it will also change the default used for the `Makefile`. -Example | File | Description ---- | --- | --- -`ios` | [`ios/src/lib.rs`](./ios/src/lib.rs) | The `3d/3d_scene.rs` example for iOS +| Example | File | Description | +| ------- | ------------------------------------ | ------------------------------------ | +| `ios` | [`ios/src/lib.rs`](./ios/src/lib.rs) | The `3d/3d_scene.rs` example for iOS | ## WASM @@ -355,7 +355,7 @@ cargo install wasm-bindgen-cli ### Build & Run -Following is an example for `headless_wasm`. For other examples in wasm/ directory, +Following is an example for `headless_wasm`. For other examples in the `wasm/` directory, change the `headless_wasm` in the following commands **and edit** `examples/wasm/index.html` to point to the correct `.js` file. @@ -364,15 +364,15 @@ cargo build --example headless_wasm --target wasm32-unknown-unknown --no-default wasm-bindgen --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/debug/examples/headless_wasm.wasm ``` -Then serve `examples/wasm` dir to browser. i.e. +Then serve `examples/wasm` the directory to your browser. i.e. ```sh basic-http-server examples/wasm ``` -Example | File | Description ---- | --- | --- -`hello_wasm` | [`wasm/hello_wasm.rs`](./wasm/hello_wasm.rs) | Runs a minimal example that logs "hello world" to the browser's console -`assets_wasm` | [`wasm/assets_wasm.rs`](./wasm/assets_wasm.rs) | Demonstrates how to load assets from wasm -`headless_wasm` | [`wasm/headless_wasm.rs`](./wasm/headless_wasm.rs) | Sets up a schedule runner and continually logs a counter to the browser's console -`winit_wasm` | [`wasm/winit_wasm.rs`](./wasm/winit_wasm.rs) | Logs user input to the browser's console. Requires the `bevy_winit` features +| Example | File | Description | +| --------------- | -------------------------------------------------- | --------------------------------------------------------------------------------- | +| `hello_wasm` | [`wasm/hello_wasm.rs`](./wasm/hello_wasm.rs) | Runs a minimal example that logs "hello world" to the browser's console | +| `assets_wasm` | [`wasm/assets_wasm.rs`](./wasm/assets_wasm.rs) | Demonstrates how to load assets from wasm | +| `headless_wasm` | [`wasm/headless_wasm.rs`](./wasm/headless_wasm.rs) | Sets up a schedule runner and continually logs a counter to the browser's console | +| `winit_wasm` | [`wasm/winit_wasm.rs`](./wasm/winit_wasm.rs) | Logs user input to the browser's console. Requires the `bevy_winit` features | From fcf73a45332dc9bd250c2d3fbddf6facbf4d407b Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 20:03:10 -0400 Subject: [PATCH 02/39] Add per_entity_events stub example --- Cargo.toml | 4 ++++ examples/README.md | 1 + examples/ecs/per_entity_events.rs | 5 +++++ 3 files changed, 10 insertions(+) create mode 100644 examples/ecs/per_entity_events.rs diff --git a/Cargo.toml b/Cargo.toml index 95141021a8077..0fd3d7a01c31b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -259,6 +259,10 @@ path = "examples/ecs/change_detection.rs" name = "event" path = "examples/ecs/event.rs" +[[example]] +name = "per_entity_events" +path = "examples/ecs/per_entity_events" + [[example]] name = "fixed_timestep" path = "examples/ecs/fixed_timestep.rs" diff --git a/examples/README.md b/examples/README.md index fa66f675e7833..79e25455180cf 100644 --- a/examples/README.md +++ b/examples/README.md @@ -151,6 +151,7 @@ git checkout v0.4.0 | `ecs_guide` | [`ecs/ecs_guide.rs`](./ecs/ecs_guide.rs) | Full guide to Bevy's ECS | | `change_detection` | [`ecs/change_detection.rs`](./ecs/change_detection.rs) | Change detection on components | | `event` | [`ecs/event.rs`](./ecs/event.rs) | Illustrates event creation, activation, and reception | +| `per_entity_events` | [`ecs/per_entity_events.rs`](./ecs/per_entity_events.rs) | Demonstrates how to store events on individual entities in a channel-like fashion. | | `fixed_timestep` | [`ecs/fixed_timestep.rs`](./ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick | | `hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities | | `parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` | diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs new file mode 100644 index 0000000000000..222c52667b11d --- /dev/null +++ b/examples/ecs/per_entity_events.rs @@ -0,0 +1,5 @@ +use bevy::prelude::*; + +fn main() { + App::build().add_plugins(DefaultPlugins).run() +} From 76c83a2938a1352bdcc2a77dec4447063efcd008 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 20:49:28 -0400 Subject: [PATCH 03/39] Basic example description --- examples/ecs/per_entity_events.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 222c52667b11d..a376af728f0ec 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -1,5 +1,29 @@ use bevy::prelude::*; +/// In this example, we show how to store events of a given type +/// as a component on individual entities rather than in a single resource. +/// +/// This pattern allows you to dispatch events directly to the entity that needs to handle them, +/// letting you avoid storing the `Entity` in the event, and prevents your from needing to either +/// repeatedly scan the entire event list for relevant events or look-up the appropriate entity using +/// slow query.get(my_entity) calls that have poor cache-locality. +/// +/// By storing the events on particular entities, +/// you can treat each entity as a seperate event-channel, +/// letting you create new events intended for only certain consumers +/// without forcing you to create a new event type to disambiguate. +/// fn main() { - App::build().add_plugins(DefaultPlugins).run() + App::build() + .add_plugins(DefaultPlugins) + // Adding events using .add_event:: will cause all resources and components of type T + // to be automatically cleaned in a double-buffer fashion by inserting an appropriate system + // + // You can avoid this behavior and manually clean up your events by simply adding events + // as vanilla components or resources + .add_event::() + .add_startup_system(setup.system()) + .add_system(input_dispatch.system()) + .add_system(act.system()) + .run() } From b00edd4f1c4483a3437938e6a6603551c63003ff Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 21:06:03 -0400 Subject: [PATCH 04/39] Added missing file extension --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0fd3d7a01c31b..715027509e0ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -261,7 +261,7 @@ path = "examples/ecs/event.rs" [[example]] name = "per_entity_events" -path = "examples/ecs/per_entity_events" +path = "examples/ecs/per_entity_events.rs" [[example]] name = "fixed_timestep" From 9119a695d0f32032fc29a5128cd2feb588381cd9 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 22:07:12 -0400 Subject: [PATCH 05/39] Input dispatch example skeleton --- examples/ecs/per_entity_events.rs | 131 +++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 3 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index a376af728f0ec..98d4299a13f0a 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -1,3 +1,4 @@ +use bevy::app::Events; use bevy::prelude::*; /// In this example, we show how to store events of a given type @@ -13,6 +14,9 @@ use bevy::prelude::*; /// letting you create new events intended for only certain consumers /// without forcing you to create a new event type to disambiguate. /// +/// This specific example shows a simple input -> action dispatch use case, +/// where this pattern helps to avoid messy rechecking and allows simple merging of multiple event input streams. +/// fn main() { App::build() .add_plugins(DefaultPlugins) @@ -21,9 +25,130 @@ fn main() { // // You can avoid this behavior and manually clean up your events by simply adding events // as vanilla components or resources - .add_event::() + .add_event::() + .add_event::() + .init_resource::() .add_startup_system(setup.system()) - .add_system(input_dispatch.system()) - .add_system(act.system()) + .add_system(select_entity.system()) + .add_system( + input_dispatch + .system() + .label("input_dispatch") + .before("action_handling"), + ) + .add_system(cycle_color.system().label("action_handling")) + .add_system(add_number.system().label("action_handling")) + .add_system(update_text_color.system().after("action_handling")) .run() } + +// Tracks which entity is selected +#[derive(Default)] +struct Selected(Option); +// Marks entities as selectable +struct Selectable; +#[derive(Bundle)] +struct InteractableBundle { + selectable: Selectable, + #[bundle] + text_bundle: Text2dBundle, + cycle_color_events: Events, + swell_events: Events, +} + +impl InteractableBundle { + fn new(x: f32, y: f32) -> Self { + // TODO: write convenience function + } +} + +enum Rainbow { + Red, + Orange, + Yellow, + Green, + Blue, + Violet, +} + +impl Iterator for Rainbow { + type Item = Self; + + fn next(&mut self) -> Option { + use Rainbow::*; + Some(match *self { + Red => Orange, + Orange => Yellow, + Yellow => Green, + Green => Blue, + Blue => Violet, + Violet => Red, + }) + } +} + +impl From for Color { + fn from(rainbow: Rainbow) -> Color { + use Rainbow::*; + match rainbow { + Red => Color::RED, + Orange => Color::ORANGE, + Yellow => Color::YELLOW, + Green => Color::GREEN, + Blue => Color::BLUE, + Violet => Color::VIOLET, + } + } +} + +// Events can be simple unit structs +struct CycleColorAction; +// Or store data to be responded to +struct AddNumberAction { + number: u32, +} + +fn setup(mut commands: Commands) { + // TODO: spawn three InteractableBundles across the screen +} + +/// Cycles through entities appropriately based on input +fn select_entity(mut query: Query<(Entity, &mut Text), With>, selected: Res) { +} + +// FIXME: make this work with EventWriters +/// Dispatches actions to entities based on the input +/// Note that we can store several events at once! +/// Try pressing both "Enter" and "Space" at once to cycle colors twice, +/// Or both "1" and "3" to add 4 all at once to the selected display +fn input_dispatch( + mut query: Query<( + &EventWriter, + &EventWriter, + )>, + selected: Res, +) { +} + +// FIXME: make this work with EventReaders +fn cycle_color(mut query: Query<(&mut Rainbow, &EventReader)>) { + for (mut rainbow, cycle_color_action_queue) in query.iter_mut() { + for action in cycle_color_action_queue.iter() { + *rainbow = rainbow.next(); + } + } +} + +fn update_text_color(mut query: Query<(&mut Text, &Rainbow), Changed>) { + for (mut text, rainbow) in query.iter_mut() { + // TODO: change the color + } +} + +fn add_number(mut query: Query<(&mut Text, &EventReader)>) { + for (mut text, add_number_action_queue) in query.iter_mut() { + for action in add_number_action_queue.iter() { + // TODO: add the number + } + } +} From cdd98a0340a827b4b8b7a5f6ecccea3bd0477668 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 22:14:34 -0400 Subject: [PATCH 06/39] Cleaned up InteractableBundle --- examples/ecs/per_entity_events.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 98d4299a13f0a..9e052e8925b75 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -49,11 +49,12 @@ struct Selected(Option); struct Selectable; #[derive(Bundle)] struct InteractableBundle { - selectable: Selectable, #[bundle] text_bundle: Text2dBundle, + selectable: Selectable, + rainbow: Rainbow, cycle_color_events: Events, - swell_events: Events, + add_number_events: Events, } impl InteractableBundle { From 4786931336acfc24d3ed717df9d73da73e660a07 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 22:14:56 -0400 Subject: [PATCH 07/39] Added lifetimes to satisfy borrow checker --- examples/ecs/per_entity_events.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 9e052e8925b75..c0e3e5bbee1df 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -124,18 +124,18 @@ fn select_entity(mut query: Query<(Entity, &mut Text), With>, select /// Or both "1" and "3" to add 4 all at once to the selected display fn input_dispatch( mut query: Query<( - &EventWriter, - &EventWriter, + &'static EventWriter, + &'static EventWriter, )>, selected: Res, ) { } // FIXME: make this work with EventReaders -fn cycle_color(mut query: Query<(&mut Rainbow, &EventReader)>) { +fn cycle_color(mut query: Query<(&mut Rainbow, &'static EventReader)>) { for (mut rainbow, cycle_color_action_queue) in query.iter_mut() { for action in cycle_color_action_queue.iter() { - *rainbow = rainbow.next(); + *rainbow = rainbow.next().unwrap(); } } } @@ -146,7 +146,7 @@ fn update_text_color(mut query: Query<(&mut Text, &Rainbow), Changed>) } } -fn add_number(mut query: Query<(&mut Text, &EventReader)>) { +fn add_number(mut query: Query<(&mut Text, &'static EventReader)>) { for (mut text, add_number_action_queue) in query.iter_mut() { for action in add_number_action_queue.iter() { // TODO: add the number From b1bd2e54663afc6c828023d6304975f1f14edb7b Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 22:17:18 -0400 Subject: [PATCH 08/39] Added better impl plan notes --- examples/ecs/per_entity_events.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index c0e3e5bbee1df..ff423f2b45969 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -117,7 +117,7 @@ fn setup(mut commands: Commands) { fn select_entity(mut query: Query<(Entity, &mut Text), With>, selected: Res) { } -// FIXME: make this work with EventWriters +// FIXME: make this work using `EventWriter` syntax and specialized behavior /// Dispatches actions to entities based on the input /// Note that we can store several events at once! /// Try pressing both "Enter" and "Space" at once to cycle colors twice, @@ -131,7 +131,7 @@ fn input_dispatch( ) { } -// FIXME: make this work with EventReaders +// FIXME: make this work using `EventReader` syntax and specialized behavior fn cycle_color(mut query: Query<(&mut Rainbow, &'static EventReader)>) { for (mut rainbow, cycle_color_action_queue) in query.iter_mut() { for action in cycle_color_action_queue.iter() { From d36a304307202b684859ba3a75aee56ce0da4245 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 22:37:05 -0400 Subject: [PATCH 09/39] Spawn InteractableBundle entities --- examples/ecs/per_entity_events.rs | 32 +++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index ff423f2b45969..990a7729f13fc 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -58,8 +58,29 @@ struct InteractableBundle { } impl InteractableBundle { - fn new(x: f32, y: f32) -> Self { - // TODO: write convenience function + fn new(x: f32, y: f32, font_handle: &Handle) -> Self { + InteractableBundle { + text_bundle: Text2dBundle { + text: Text::with_section( + "0", + TextStyle { + font: font_handle.clone(), + font_size: 60.0, + color: Color::WHITE, + }, + TextAlignment { + vertical: VerticalAlign::Center, + horizontal: HorizontalAlign::Center, + }, + ), + transform: Transform::from_xyz(x, y, 0.0), + ..Default::default() + }, + selectable: Selectable, + rainbow: Rainbow::Red, + cycle_color_events: Events::::default(), + add_number_events: Events::::default(), + } } } @@ -109,8 +130,11 @@ struct AddNumberAction { number: u32, } -fn setup(mut commands: Commands) { - // TODO: spawn three InteractableBundles across the screen +fn setup(mut commands: Commands, asset_server: Res) { + let font_handle = asset_server.load("fonts/FiraSans-Bold.ttf"); + commands.spawn_bundle(InteractableBundle::new(-200.0, 0.0, &font_handle)); + commands.spawn_bundle(InteractableBundle::new(0.0, 0.0, &font_handle)); + commands.spawn_bundle(InteractableBundle::new(200.0, 0.0, &font_handle)); } /// Cycles through entities appropriately based on input From 36faf02adc9ab2a19b75e3753c269e03f118728a Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 22:45:43 -0400 Subject: [PATCH 10/39] Color updating system --- examples/ecs/per_entity_events.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 990a7729f13fc..dde200fc1d332 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -109,8 +109,8 @@ impl Iterator for Rainbow { } } -impl From for Color { - fn from(rainbow: Rainbow) -> Color { +impl From<&Rainbow> for Color { + fn from(rainbow: &Rainbow) -> Color { use Rainbow::*; match rainbow { Red => Color::RED, @@ -166,7 +166,7 @@ fn cycle_color(mut query: Query<(&mut Rainbow, &'static EventReader>) { for (mut text, rainbow) in query.iter_mut() { - // TODO: change the color + text.sections[0].style.color = rainbow.into(); } } From 4ca43734dac0432b36cdafe1bc9a4578d3bc1cf0 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 23:25:44 -0400 Subject: [PATCH 11/39] Should demo how to use raw Events --- examples/ecs/per_entity_events.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index dde200fc1d332..cdf8b6c339e79 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -158,7 +158,7 @@ fn input_dispatch( // FIXME: make this work using `EventReader` syntax and specialized behavior fn cycle_color(mut query: Query<(&mut Rainbow, &'static EventReader)>) { for (mut rainbow, cycle_color_action_queue) in query.iter_mut() { - for action in cycle_color_action_queue.iter() { + for _ in cycle_color_action_queue.iter() { *rainbow = rainbow.next().unwrap(); } } @@ -170,7 +170,9 @@ fn update_text_color(mut query: Query<(&mut Text, &Rainbow), Changed>) } } -fn add_number(mut query: Query<(&mut Text, &'static EventReader)>) { +// Just as when using Events as a resource, you can work with `Events` directly instead +// EventReader and EventWriter are just convenient wrappers that better communicate intent +fn add_number(mut query: Query<(&mut Text, &Events)>) { for (mut text, add_number_action_queue) in query.iter_mut() { for action in add_number_action_queue.iter() { // TODO: add the number From f39accdd32f393d36a512aa8113498bf0f956c04 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 5 May 2021 14:29:26 -0400 Subject: [PATCH 12/39] Manual event reader syntax --- examples/ecs/per_entity_events.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index cdf8b6c339e79..d427e004554aa 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -173,8 +173,10 @@ fn update_text_color(mut query: Query<(&mut Text, &Rainbow), Changed>) // Just as when using Events as a resource, you can work with `Events` directly instead // EventReader and EventWriter are just convenient wrappers that better communicate intent fn add_number(mut query: Query<(&mut Text, &Events)>) { - for (mut text, add_number_action_queue) in query.iter_mut() { - for action in add_number_action_queue.iter() { + // To add events manually, use events.send(MyEvent::new()) + for (mut text, action_queue) in query.iter_mut() { + let mut reader = action_queue.get_reader(); + for action in reader.iter(&action_queue) { // TODO: add the number } } From b552aaa1021d4315ce0319f8b2232fe400ed9955 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 5 May 2021 14:39:56 -0400 Subject: [PATCH 13/39] Clear componentized Events too --- crates/bevy_ecs/src/event.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 889d9d22e8b4c..cd37b3361e4f3 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -1,7 +1,7 @@ use crate as bevy_ecs; use crate::{ component::Component, - system::{Local, Res, ResMut, SystemParam}, + system::{Local, Query, Res, ResMut, SystemParam}, }; use bevy_utils::tracing::trace; use std::{ @@ -323,9 +323,16 @@ impl Events { } } - /// A system that calls [Events::update] once per frame. - pub fn update_system(mut events: ResMut) { - events.update(); + /// A system that calls [Events::update] once per frame on all events of the specified type. + /// + /// This clears all events from the previous frame, + /// and advances the current frame's buffer so it is ready to be cleared. + pub fn update_system( + mut resource_events: ResMut, + mut component_events: Query<&mut Self>, + ) { + resource_events.update(); + component_events.for_each_mut(|mut e| e.update()); } /// Removes all events. From 3ae1b2d3eda5ea8abe7b67eb1bdcf39b70e46cb8 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 5 May 2021 19:03:19 -0400 Subject: [PATCH 14/39] Example mostly works --- examples/ecs/per_entity_events.rs | 152 ++++++++++++++++++++++++++---- 1 file changed, 132 insertions(+), 20 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index d427e004554aa..4bfe427c350ba 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -27,7 +27,6 @@ fn main() { // as vanilla components or resources .add_event::() .add_event::() - .init_resource::() .add_startup_system(setup.system()) .add_system(select_entity.system()) .add_system( @@ -38,19 +37,21 @@ fn main() { ) .add_system(cycle_color.system().label("action_handling")) .add_system(add_number.system().label("action_handling")) + .add_system(scale_selected.system().after("action_handling")) .add_system(update_text_color.system().after("action_handling")) .run() } // Tracks which entity is selected -#[derive(Default)] -struct Selected(Option); +struct Selected { + entity: Entity, +} // Marks entities as selectable struct Selectable; #[derive(Bundle)] struct InteractableBundle { #[bundle] - text_bundle: Text2dBundle, + text_bundle: TextBundle, selectable: Selectable, rainbow: Rainbow, cycle_color_events: Events, @@ -58,9 +59,10 @@ struct InteractableBundle { } impl InteractableBundle { + // FIXME: fix position fn new(x: f32, y: f32, font_handle: &Handle) -> Self { InteractableBundle { - text_bundle: Text2dBundle { + text_bundle: TextBundle { text: Text::with_section( "0", TextStyle { @@ -127,38 +129,145 @@ impl From<&Rainbow> for Color { struct CycleColorAction; // Or store data to be responded to struct AddNumberAction { - number: u32, + number: u8, } fn setup(mut commands: Commands, asset_server: Res) { + // Don't forget to include a camera! + commands.spawn_bundle(UiCameraBundle::default()); + let font_handle = asset_server.load("fonts/FiraSans-Bold.ttf"); - commands.spawn_bundle(InteractableBundle::new(-200.0, 0.0, &font_handle)); - commands.spawn_bundle(InteractableBundle::new(0.0, 0.0, &font_handle)); - commands.spawn_bundle(InteractableBundle::new(200.0, 0.0, &font_handle)); + // Spawns the first entity, and grabs the Entity id that is being allocated + let first_entity = commands + .spawn_bundle(InteractableBundle::new(-200.0, 400.0, &font_handle)) + .id(); + commands.insert_resource(Selected { + entity: first_entity, + }); + + commands.spawn_bundle(InteractableBundle::new(0.0, 400.0, &font_handle)); + commands.spawn_bundle(InteractableBundle::new(200.0, 400.0, &font_handle)); +} + +enum CycleBehavior { + Forward, + Back, } /// Cycles through entities appropriately based on input -fn select_entity(mut query: Query<(Entity, &mut Text), With>, selected: Res) { +fn select_entity( + mut query: Query>, + mut selected: ResMut, + keyboard_input: Res>, +) { + let cycle_behavior: CycleBehavior = if keyboard_input.just_pressed(KeyCode::Tab) { + if keyboard_input.pressed(KeyCode::LShift) || keyboard_input.pressed(KeyCode::RShift) { + CycleBehavior::Back + } else { + CycleBehavior::Forward + } + } else if keyboard_input.just_pressed(KeyCode::Right) { + CycleBehavior::Forward + } else if keyboard_input.just_pressed(KeyCode::Left) { + CycleBehavior::Back + } else { + return; + }; + + let mut entities = Vec::::new(); + // FIXME: Move to `.for_each` when https://github.com/bevyengine/bevy/issues/753 is resolved + query.for_each_mut(|entity| entities.push(entity.clone())); + + let current_position = entities.iter().position(|&e| e == selected.entity).unwrap() as isize; + + let new_position = match cycle_behavior { + // We have to convert to isize for this step to avoid underflows when current_postion == 0 + CycleBehavior::Forward => (current_position + 1).rem_euclid(entities.len() as isize), + CycleBehavior::Back => (current_position - 1).rem_euclid(entities.len() as isize), + }; + + selected.entity = entities[new_position as usize]; +} + +fn scale_selected( + mut query: Query<(Entity, &mut Text), With>, + selected: Res, +) { + // Only do work when the selection is changed + if !selected.is_changed() { + return; + } + + for (entity, mut text) in query.iter_mut() { + if entity == selected.entity { + text.sections[0].style.font_size = 90.0; + } else { + text.sections[0].style.font_size = 60.0; + } + } } // FIXME: make this work using `EventWriter` syntax and specialized behavior +// FIXME: all input events are duplicated, due to just_pressed behavior /// Dispatches actions to entities based on the input /// Note that we can store several events at once! -/// Try pressing both "Enter" and "Space" at once to cycle colors twice, -/// Or both "1" and "3" to add 4 all at once to the selected display +/// Try pressing both "Enter" and "Space" at the same time to cycle colors twice, +/// Or both "1" and "3" to add 4 all at the same time to the selected display fn input_dispatch( - mut query: Query<( - &'static EventWriter, - &'static EventWriter, - )>, + mut query: Query< + (&mut Events, &mut Events), + With, + >, selected: Res, + keyboard_input: Res>, ) { + let (mut cycle_actions, mut add_actions) = query.get_mut(selected.entity).unwrap(); + + // Inputs for cycling colors + // Normally, you'd probably want to use || on the inputs here, + // but we're demonstrating the ability to process multiple events at once + if keyboard_input.just_pressed(KeyCode::Return) { + cycle_actions.send(CycleColorAction); + } + if keyboard_input.just_pressed(KeyCode::Space) { + cycle_actions.send(CycleColorAction); + } + + // Inputs for sending numbers to be added + if keyboard_input.just_pressed(KeyCode::Key1) { + add_actions.send(AddNumberAction { number: 1 }); + } + if keyboard_input.just_pressed(KeyCode::Key2) { + add_actions.send(AddNumberAction { number: 2 }); + } + if keyboard_input.just_pressed(KeyCode::Key3) { + add_actions.send(AddNumberAction { number: 3 }); + } + if keyboard_input.just_pressed(KeyCode::Key4) { + add_actions.send(AddNumberAction { number: 4 }); + } + if keyboard_input.just_pressed(KeyCode::Key5) { + add_actions.send(AddNumberAction { number: 5 }); + } + if keyboard_input.just_pressed(KeyCode::Key6) { + add_actions.send(AddNumberAction { number: 6 }); + } + if keyboard_input.just_pressed(KeyCode::Key7) { + add_actions.send(AddNumberAction { number: 7 }); + } + if keyboard_input.just_pressed(KeyCode::Key8) { + add_actions.send(AddNumberAction { number: 8 }); + } + if keyboard_input.just_pressed(KeyCode::Key9) { + add_actions.send(AddNumberAction { number: 9 }); + } } // FIXME: make this work using `EventReader` syntax and specialized behavior -fn cycle_color(mut query: Query<(&mut Rainbow, &'static EventReader)>) { - for (mut rainbow, cycle_color_action_queue) in query.iter_mut() { - for _ in cycle_color_action_queue.iter() { +fn cycle_color(mut query: Query<(&mut Rainbow, &mut Events)>) { + for (mut rainbow, action_queue) in query.iter_mut() { + let mut reader = action_queue.get_reader(); + for _ in reader.iter(&action_queue) { *rainbow = rainbow.next().unwrap(); } } @@ -177,7 +286,10 @@ fn add_number(mut query: Query<(&mut Text, &Events)>) { for (mut text, action_queue) in query.iter_mut() { let mut reader = action_queue.get_reader(); for action in reader.iter(&action_queue) { - // TODO: add the number + let current_number: u8 = text.sections[0].value.clone().parse().unwrap(); + // Wrap addition, rather than overflowing + let new_number = (current_number + action.number) % std::u8::MAX; + text.sections[0].value = new_number.to_string(); } } } From eae4f978b3e7ad981780ecf1dc718f91c42a7369 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 5 May 2021 19:33:50 -0400 Subject: [PATCH 15/39] Prevent addition overflow --- examples/ecs/per_entity_events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 4bfe427c350ba..511f7cbe74e75 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -288,7 +288,7 @@ fn add_number(mut query: Query<(&mut Text, &Events)>) { for action in reader.iter(&action_queue) { let current_number: u8 = text.sections[0].value.clone().parse().unwrap(); // Wrap addition, rather than overflowing - let new_number = (current_number + action.number) % std::u8::MAX; + let new_number = ((current_number + action.number) as u16) % std::u8::MAX as u16; text.sections[0].value = new_number.to_string(); } } From 922a29880f5ff472656c8a7de40701cd33676fb3 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 5 May 2021 19:41:03 -0400 Subject: [PATCH 16/39] Code quality cleanup --- examples/ecs/per_entity_events.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 511f7cbe74e75..12a67b54c8ca0 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -208,28 +208,21 @@ fn scale_selected( } // FIXME: make this work using `EventWriter` syntax and specialized behavior -// FIXME: all input events are duplicated, due to just_pressed behavior /// Dispatches actions to entities based on the input /// Note that we can store several events at once! -/// Try pressing both "Enter" and "Space" at the same time to cycle colors twice, -/// Or both "1" and "3" to add 4 all at the same time to the selected display +/// Try pressing both "1" and "3" to add 4 to the selected display fn input_dispatch( mut query: Query< (&mut Events, &mut Events), With, >, selected: Res, - keyboard_input: Res>, + mut keyboard_input: ResMut>, ) { let (mut cycle_actions, mut add_actions) = query.get_mut(selected.entity).unwrap(); // Inputs for cycling colors - // Normally, you'd probably want to use || on the inputs here, - // but we're demonstrating the ability to process multiple events at once - if keyboard_input.just_pressed(KeyCode::Return) { - cycle_actions.send(CycleColorAction); - } - if keyboard_input.just_pressed(KeyCode::Space) { + if keyboard_input.just_pressed(KeyCode::Return) || keyboard_input.just_pressed(KeyCode::Space) { cycle_actions.send(CycleColorAction); } @@ -281,6 +274,7 @@ fn update_text_color(mut query: Query<(&mut Text, &Rainbow), Changed>) // Just as when using Events as a resource, you can work with `Events` directly instead // EventReader and EventWriter are just convenient wrappers that better communicate intent +// FIXME: Prevent event duplication by storing a Local resource fn add_number(mut query: Query<(&mut Text, &Events)>) { // To add events manually, use events.send(MyEvent::new()) for (mut text, action_queue) in query.iter_mut() { From 61b0befc52d2e128edf3e97aa60e9284509ff32e Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 5 May 2021 19:53:25 -0400 Subject: [PATCH 17/39] Fixed double-counting by storing an EventReader --- examples/ecs/per_entity_events.rs | 39 ++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 12a67b54c8ca0..88407dffa3bdb 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -1,4 +1,4 @@ -use bevy::app::Events; +use bevy::app::{Events, ManualEventReader}; use bevy::prelude::*; /// In this example, we show how to store events of a given type @@ -212,12 +212,14 @@ fn scale_selected( /// Note that we can store several events at once! /// Try pressing both "1" and "3" to add 4 to the selected display fn input_dispatch( + // You could also access the &Events component directly + // then send events to that component with `Events::send` mut query: Query< (&mut Events, &mut Events), With, >, selected: Res, - mut keyboard_input: ResMut>, + keyboard_input: ResMut>, ) { let (mut cycle_actions, mut add_actions) = query.get_mut(selected.entity).unwrap(); @@ -256,7 +258,7 @@ fn input_dispatch( } } -// FIXME: make this work using `EventReader` syntax and specialized behavior +// FIXME: make this work without duplication using `EventReader` syntax and specialized behavior fn cycle_color(mut query: Query<(&mut Rainbow, &mut Events)>) { for (mut rainbow, action_queue) in query.iter_mut() { let mut reader = action_queue.get_reader(); @@ -274,16 +276,25 @@ fn update_text_color(mut query: Query<(&mut Text, &Rainbow), Changed>) // Just as when using Events as a resource, you can work with `Events` directly instead // EventReader and EventWriter are just convenient wrappers that better communicate intent -// FIXME: Prevent event duplication by storing a Local resource -fn add_number(mut query: Query<(&mut Text, &Events)>) { - // To add events manually, use events.send(MyEvent::new()) - for (mut text, action_queue) in query.iter_mut() { - let mut reader = action_queue.get_reader(); - for action in reader.iter(&action_queue) { - let current_number: u8 = text.sections[0].value.clone().parse().unwrap(); - // Wrap addition, rather than overflowing - let new_number = ((current_number + action.number) as u16) % std::u8::MAX as u16; - text.sections[0].value = new_number.to_string(); - } +// And store state automatically for you +fn add_number( + mut query: Query<(&mut Text, &Events)>, + mut reader: Local>, + selected: Res, +) { + let (mut text, action_queue) = query.get_mut(selected.entity).unwrap(); + // Because we only care about one entity at a time, we can store the event reader manually + // in a Local resource as part of the system's data + // This logic is handled for you, storing one EventReader per entity when you query for an EventReader + if selected.is_changed() { + // If the resource selected is changed, we need to rebuild a new event reader + *reader = action_queue.get_reader(); + } + + for action in reader.iter(&action_queue) { + let current_number: u8 = text.sections[0].value.clone().parse().unwrap(); + // Wrap addition, rather than overflowing + let new_number = ((current_number + action.number) as u16) % std::u8::MAX as u16; + text.sections[0].value = new_number.to_string(); } } From 489294e49bcdda72c2bb3012379c447b4cbf3756 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 5 May 2021 20:37:53 -0400 Subject: [PATCH 18/39] Removed left-right controls for brevity --- examples/ecs/per_entity_events.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 88407dffa3bdb..916b839b21901 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -166,10 +166,6 @@ fn select_entity( } else { CycleBehavior::Forward } - } else if keyboard_input.just_pressed(KeyCode::Right) { - CycleBehavior::Forward - } else if keyboard_input.just_pressed(KeyCode::Left) { - CycleBehavior::Back } else { return; }; From 1b9c1cce66abbf1916f9226350a4c6d60ddbecc3 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 5 May 2021 20:40:10 -0400 Subject: [PATCH 19/39] Less colors, more brevity --- examples/ecs/per_entity_events.rs | 35 ++++++++++++------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 916b839b21901..c283151a33cad 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -53,7 +53,7 @@ struct InteractableBundle { #[bundle] text_bundle: TextBundle, selectable: Selectable, - rainbow: Rainbow, + rainbow: ColorChoices, cycle_color_events: Events, add_number_events: Events, } @@ -79,46 +79,37 @@ impl InteractableBundle { ..Default::default() }, selectable: Selectable, - rainbow: Rainbow::Red, + rainbow: ColorChoices::Red, cycle_color_events: Events::::default(), add_number_events: Events::::default(), } } } -enum Rainbow { +enum ColorChoices { Red, - Orange, - Yellow, - Green, Blue, Violet, } -impl Iterator for Rainbow { +impl Iterator for ColorChoices { type Item = Self; - fn next(&mut self) -> Option { - use Rainbow::*; + fn next(&mut self) -> Option { + use ColorChoices::*; Some(match *self { - Red => Orange, - Orange => Yellow, - Yellow => Green, - Green => Blue, + Red => Blue, Blue => Violet, Violet => Red, }) } } -impl From<&Rainbow> for Color { - fn from(rainbow: &Rainbow) -> Color { - use Rainbow::*; +impl From<&ColorChoices> for Color { + fn from(rainbow: &ColorChoices) -> Color { + use ColorChoices::*; match rainbow { Red => Color::RED, - Orange => Color::ORANGE, - Yellow => Color::YELLOW, - Green => Color::GREEN, Blue => Color::BLUE, Violet => Color::VIOLET, } @@ -256,15 +247,15 @@ fn input_dispatch( // FIXME: make this work without duplication using `EventReader` syntax and specialized behavior fn cycle_color(mut query: Query<(&mut Rainbow, &mut Events)>) { - for (mut rainbow, action_queue) in query.iter_mut() { + for (mut color, action_queue) in query.iter_mut() { let mut reader = action_queue.get_reader(); for _ in reader.iter(&action_queue) { - *rainbow = rainbow.next().unwrap(); + *color = color.next().unwrap(); } } } -fn update_text_color(mut query: Query<(&mut Text, &Rainbow), Changed>) { +fn update_text_color(mut query: Query<(&mut Text, &ColorChoices), Changed>) { for (mut text, rainbow) in query.iter_mut() { text.sections[0].style.color = rainbow.into(); } From a952c1b6a70bf1c4de0a11d89370140bbe7faece Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 5 May 2021 20:50:45 -0400 Subject: [PATCH 20/39] Simpler input handling --- examples/ecs/per_entity_events.rs | 50 ++++++++++++++----------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index c283151a33cad..97a2d864639da 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -208,40 +208,36 @@ fn input_dispatch( selected: Res, keyboard_input: ResMut>, ) { + use KeyCode::*; + let (mut cycle_actions, mut add_actions) = query.get_mut(selected.entity).unwrap(); // Inputs for cycling colors - if keyboard_input.just_pressed(KeyCode::Return) || keyboard_input.just_pressed(KeyCode::Space) { + if keyboard_input.just_pressed(Space) { cycle_actions.send(CycleColorAction); } // Inputs for sending numbers to be added - if keyboard_input.just_pressed(KeyCode::Key1) { - add_actions.send(AddNumberAction { number: 1 }); - } - if keyboard_input.just_pressed(KeyCode::Key2) { - add_actions.send(AddNumberAction { number: 2 }); - } - if keyboard_input.just_pressed(KeyCode::Key3) { - add_actions.send(AddNumberAction { number: 3 }); - } - if keyboard_input.just_pressed(KeyCode::Key4) { - add_actions.send(AddNumberAction { number: 4 }); - } - if keyboard_input.just_pressed(KeyCode::Key5) { - add_actions.send(AddNumberAction { number: 5 }); - } - if keyboard_input.just_pressed(KeyCode::Key6) { - add_actions.send(AddNumberAction { number: 6 }); - } - if keyboard_input.just_pressed(KeyCode::Key7) { - add_actions.send(AddNumberAction { number: 7 }); - } - if keyboard_input.just_pressed(KeyCode::Key8) { - add_actions.send(AddNumberAction { number: 8 }); - } - if keyboard_input.just_pressed(KeyCode::Key9) { - add_actions.send(AddNumberAction { number: 9 }); + for k in keyboard_input.get_just_pressed() { + let num = match key_code { + Key0 => Some(0), + Key1 => Some(1), + Key2 => Some(2), + Key3 => Some(3), + Key4 => Some(4), + Key5 => Some(5), + Key6 => Some(6), + Key7 => Some(7), + Key8 => Some(8), + Key9 => Some(9), + _ => None, + }; + + if num.is_some() { + add_actions.send(AddNumberAction { + number: num.unwrap(), + }); + } } } From 45478f9d1d14a68157c5886a3fa77802f81b76a1 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 5 May 2021 21:07:16 -0400 Subject: [PATCH 21/39] Quick and easy keycode math Credit to @mockersf for the idea --- examples/ecs/per_entity_events.rs | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 97a2d864639da..6d4546116c179 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -218,31 +218,17 @@ fn input_dispatch( } // Inputs for sending numbers to be added - for k in keyboard_input.get_just_pressed() { - let num = match key_code { - Key0 => Some(0), - Key1 => Some(1), - Key2 => Some(2), - Key3 => Some(3), - Key4 => Some(4), - Key5 => Some(5), - Key6 => Some(6), - Key7 => Some(7), - Key8 => Some(8), - Key9 => Some(9), - _ => None, - }; - - if num.is_some() { + for key_code in keyboard_input.get_just_pressed() { + if (key_code as u8) < 10 { add_actions.send(AddNumberAction { - number: num.unwrap(), + // The keycode for KeyCode::Key1 is 0 + number: key_code as u8 + 1, }); } } } -// FIXME: make this work without duplication using `EventReader` syntax and specialized behavior -fn cycle_color(mut query: Query<(&mut Rainbow, &mut Events)>) { +fn cycle_color(mut query: Query<(&mut ColorChoices, EventReader)>) { for (mut color, action_queue) in query.iter_mut() { let mut reader = action_queue.get_reader(); for _ in reader.iter(&action_queue) { From 532a544f1d280cd00589082af4c61fd386e22023 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 5 May 2021 21:39:52 -0400 Subject: [PATCH 22/39] Foundations for EventReader etc Query parameters --- crates/bevy_ecs/src/query/event.rs | 71 ++++++++++++++++++++++++++++++ crates/bevy_ecs/src/query/mod.rs | 2 + examples/ecs/per_entity_events.rs | 6 +-- 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 crates/bevy_ecs/src/query/event.rs diff --git a/crates/bevy_ecs/src/query/event.rs b/crates/bevy_ecs/src/query/event.rs new file mode 100644 index 0000000000000..a7626ea88cc86 --- /dev/null +++ b/crates/bevy_ecs/src/query/event.rs @@ -0,0 +1,71 @@ +use super::{ + super::event::{EventReader, EventWriter, Events, ManualEventReader}, + Access, Fetch, FetchState, FilteredAccess, WorldQuery, +}; +use crate::{ + archetype::{Archetype, ArchetypeComponentId}, + component::{Component, ComponentId}, + prelude::World, + storage::{Table, Tables}, +}; +use std::marker::PhantomData; + +impl<'a, T: Component> WorldQuery for EventWriter<'a, T> { + type Fetch = EventWriterFetch; + type State = EventWriterState; +} + +struct EventWriterFetch { + marker: PhantomData, +} + +impl<'a, T: Component> Fetch<'a> for EventWriterFetch { + // EventWriter queries return an EventWriter in each item + type Item = EventWriter<'a, T>; + // This is the corresponding S: FetchState type + type State = EventWriterState; + + unsafe fn init( + world: &World, + state: &Self::State, + last_change_tick: u32, + change_tick: u32, + ) -> Self { + } + + fn is_dense(&self) -> bool {} + + unsafe fn set_archetype( + &mut self, + state: &Self::State, + archetype: &Archetype, + tables: &Tables, + ) { + } + + unsafe fn set_table(&mut self, state: &Self::State, table: &Table) {} + + unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item {} + + unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item {} +} +struct EventWriterState { + marker: PhantomData, +} + +unsafe impl FetchState for EventWriterState { + fn init(world: &mut World) -> Self {} + + fn update_component_access(&self, access: &mut FilteredAccess) {} + + fn update_archetype_component_access( + &self, + archetype: &Archetype, + access: &mut Access, + ) { + } + + fn matches_archetype(&self, archetype: &Archetype) -> bool {} + + fn matches_table(&self, table: &Table) -> bool {} +} diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index 95b4d7b6e8912..27ea0181c61ec 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -1,4 +1,5 @@ mod access; +mod event; mod fetch; mod filter; mod iter; @@ -9,6 +10,7 @@ pub use fetch::*; pub use filter::*; pub use iter::*; pub use state::*; +pub use state::*; #[cfg(test)] mod tests { diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 6d4546116c179..74724ae911273 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -194,7 +194,6 @@ fn scale_selected( } } -// FIXME: make this work using `EventWriter` syntax and specialized behavior /// Dispatches actions to entities based on the input /// Note that we can store several events at once! /// Try pressing both "1" and "3" to add 4 to the selected display @@ -202,7 +201,7 @@ fn input_dispatch( // You could also access the &Events component directly // then send events to that component with `Events::send` mut query: Query< - (&mut Events, &mut Events), + (EventWriter, EventWriter), With, >, selected: Res, @@ -230,8 +229,7 @@ fn input_dispatch( fn cycle_color(mut query: Query<(&mut ColorChoices, EventReader)>) { for (mut color, action_queue) in query.iter_mut() { - let mut reader = action_queue.get_reader(); - for _ in reader.iter(&action_queue) { + for _ in action_queue.iter() { *color = color.next().unwrap(); } } From aceea35144e62d6da68848e3d547ecf870c3a23c Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 6 May 2021 00:47:14 -0400 Subject: [PATCH 23/39] Nearly-working impl of EventWriter components Had to change EventWriter and Reader to take vanilla refs, rather than Res smart pointers to accommodate mixed resource and component usage. Lifetimes are still sad. --- crates/bevy_ecs/src/event.rs | 17 +++-- crates/bevy_ecs/src/query/event.rs | 109 +++++++++++++++++++++++------ 2 files changed, 100 insertions(+), 26 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index cd37b3361e4f3..a45c34a3d2867 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -1,7 +1,6 @@ -use crate as bevy_ecs; use crate::{ component::Component, - system::{Local, Query, Res, ResMut, SystemParam}, + system::{Local, Query, ResMut, ResMutState, ResState, SystemParam}, }; use bevy_utils::tracing::trace; use std::{ @@ -150,16 +149,22 @@ fn map_instance_event(event_instance: &EventInstance) -> &T { } /// Reads events of type `T` in order and tracks which events have already been read. -#[derive(SystemParam)] pub struct EventReader<'a, T: Component> { last_event_count: Local<'a, (usize, PhantomData)>, - events: Res<'a, Events>, + events: &'a Events, +} + +impl<'a, T: Component> SystemParam for EventReader<'a, T> { + type Fetch = ResState>; } /// Sends events of type `T`. -#[derive(SystemParam)] pub struct EventWriter<'a, T: Component> { - events: ResMut<'a, Events>, + events: &'a mut Events, +} + +impl<'a, T: Component> SystemParam for EventWriter<'a, T> { + type Fetch = ResMutState>; } impl<'a, T: Component> EventWriter<'a, T> { diff --git a/crates/bevy_ecs/src/query/event.rs b/crates/bevy_ecs/src/query/event.rs index a7626ea88cc86..759929950bc0b 100644 --- a/crates/bevy_ecs/src/query/event.rs +++ b/crates/bevy_ecs/src/query/event.rs @@ -1,71 +1,140 @@ use super::{ - super::event::{EventReader, EventWriter, Events, ManualEventReader}, - Access, Fetch, FetchState, FilteredAccess, WorldQuery, + super::event::{EventWriter, Events}, + Access, Fetch, FetchState, FilteredAccess, WorldQuery, WriteFetch, WriteState, }; use crate::{ archetype::{Archetype, ArchetypeComponentId}, - component::{Component, ComponentId}, + component::{Component, ComponentId, StorageType}, prelude::World, storage::{Table, Tables}, }; -use std::marker::PhantomData; +use std::any::TypeId; impl<'a, T: Component> WorldQuery for EventWriter<'a, T> { - type Fetch = EventWriterFetch; + type Fetch = EventWriterFetch<'a, T>; type State = EventWriterState; } -struct EventWriterFetch { - marker: PhantomData, +struct EventWriterFetch<'s, T> { + /// EventWriter query parameters require write access to &mut Events + write_fetch: WriteFetch>, + state: &'s EventWriterState, } -impl<'a, T: Component> Fetch<'a> for EventWriterFetch { - // EventWriter queries return an EventWriter in each item +impl<'a, T: Component> Fetch<'a> for EventWriterFetch<'a, T> { + /// EventWriter queries return an EventWriter in each item type Item = EventWriter<'a, T>; - // This is the corresponding S: FetchState type + /// This is the corresponding S: FetchState type type State = EventWriterState; + /// Checks the storage type of the corresponding Events component + fn is_dense(&self) -> bool { + match self.state.event_storage_type { + StorageType::SparseSet => false, + StorageType::Table => true, + } + } + unsafe fn init( world: &World, state: &Self::State, last_change_tick: u32, change_tick: u32, ) -> Self { + EventWriterFetch { + write_fetch: WriteFetch::>::init( + world, + &state.write_state, + last_change_tick, + change_tick, + ), + state, + } } - fn is_dense(&self) -> bool {} - unsafe fn set_archetype( &mut self, state: &Self::State, archetype: &Archetype, tables: &Tables, ) { + self.write_fetch + .set_archetype(&state.write_state, archetype, tables); } - unsafe fn set_table(&mut self, state: &Self::State, table: &Table) {} + unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { + self.write_fetch.set_table(&state.write_state, table); + } - unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item {} + /// Returns the EventWriter of the next entity when the storage type of the query is sparse + unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { + let events = *self.write_fetch.archetype_fetch(archetype_index); + EventWriter { + events: &mut events, + } + } - unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item {} + /// Returns the EventWriter of the next entity when the storage type of the query is dense + unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { + let events = *self.write_fetch.archetype_fetch(table_row); + EventWriter { + events: &mut events, + } + } } struct EventWriterState { - marker: PhantomData, + event_component_id: ComponentId, + event_storage_type: StorageType, + /// EventWriter query parameters require write access to &mut Events + write_state: WriteState>, } unsafe impl FetchState for EventWriterState { - fn init(world: &mut World) -> Self {} + fn init(world: &mut World) -> Self { + let event_component_id = world.components.get_id(TypeId::of::>()).unwrap(); + EventWriterState { + event_component_id, + event_storage_type: world + .components + .get_info(event_component_id) + .unwrap() + .storage_type(), + write_state: WriteState::>::init(world), + } + } - fn update_component_access(&self, access: &mut FilteredAccess) {} + /// Access is based on &Events + fn update_component_access(&self, access: &mut FilteredAccess) { + if access.access().has_write(self.event_component_id) { + panic!("&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + std::any::type_name::>()); + } + access.add_read(self.event_component_id) + } + /// Access is based on &Events fn update_archetype_component_access( &self, archetype: &Archetype, access: &mut Access, ) { + if let Some(archetype_component_id) = + archetype.get_archetype_component_id(self.event_component_id) + { + access.add_read(archetype_component_id); + } } - fn matches_archetype(&self, archetype: &Archetype) -> bool {} + /// Matches based on &Events + fn matches_archetype(&self, archetype: &Archetype) -> bool { + archetype.contains(self.event_component_id) + } - fn matches_table(&self, table: &Table) -> bool {} + /// Matches based on &Events + fn matches_table(&self, table: &Table) -> bool { + table.has_column(self.event_component_id) + } } + +// TODO: add tests +mod tests {} From 56eece56cd3c05c8763126a7f17eb5204264817e Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Fri, 7 May 2021 22:44:20 -0400 Subject: [PATCH 24/39] Removed lifetime by storing StorageType directly See https://github.com/bevyengine/bevy/issues/2119 to clean this up. --- crates/bevy_ecs/src/query/event.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/query/event.rs b/crates/bevy_ecs/src/query/event.rs index 759929950bc0b..aed06374a9d59 100644 --- a/crates/bevy_ecs/src/query/event.rs +++ b/crates/bevy_ecs/src/query/event.rs @@ -11,17 +11,17 @@ use crate::{ use std::any::TypeId; impl<'a, T: Component> WorldQuery for EventWriter<'a, T> { - type Fetch = EventWriterFetch<'a, T>; + type Fetch = EventWriterFetch; type State = EventWriterState; } -struct EventWriterFetch<'s, T> { +struct EventWriterFetch { /// EventWriter query parameters require write access to &mut Events write_fetch: WriteFetch>, - state: &'s EventWriterState, + storage_type: StorageType, } -impl<'a, T: Component> Fetch<'a> for EventWriterFetch<'a, T> { +impl<'a, T: Component> Fetch<'a> for EventWriterFetch { /// EventWriter queries return an EventWriter in each item type Item = EventWriter<'a, T>; /// This is the corresponding S: FetchState type @@ -29,7 +29,7 @@ impl<'a, T: Component> Fetch<'a> for EventWriterFetch<'a, T> { /// Checks the storage type of the corresponding Events component fn is_dense(&self) -> bool { - match self.state.event_storage_type { + match self.storage_type { StorageType::SparseSet => false, StorageType::Table => true, } @@ -48,7 +48,11 @@ impl<'a, T: Component> Fetch<'a> for EventWriterFetch<'a, T> { last_change_tick, change_tick, ), - state, + storage_type: world + .components + .get_info(world.components.get_id(TypeId::of::>()).unwrap()) + .unwrap() + .storage_type(), } } From 77dc53155af859c88fa7a2049fd44ca6bdc17dfb Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Fri, 7 May 2021 22:51:01 -0400 Subject: [PATCH 25/39] Use into_inner to properly convert Mut to &mut --- crates/bevy_ecs/src/query/event.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ecs/src/query/event.rs b/crates/bevy_ecs/src/query/event.rs index aed06374a9d59..0fa671e5c0641 100644 --- a/crates/bevy_ecs/src/query/event.rs +++ b/crates/bevy_ecs/src/query/event.rs @@ -7,6 +7,7 @@ use crate::{ component::{Component, ComponentId, StorageType}, prelude::World, storage::{Table, Tables}, + world::Mut, }; use std::any::TypeId; @@ -72,18 +73,17 @@ impl<'a, T: Component> Fetch<'a> for EventWriterFetch { /// Returns the EventWriter of the next entity when the storage type of the query is sparse unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { - let events = *self.write_fetch.archetype_fetch(archetype_index); - EventWriter { - events: &mut events, - } + let events = self + .write_fetch + .archetype_fetch(archetype_index) + .into_inner(); + EventWriter { events } } /// Returns the EventWriter of the next entity when the storage type of the query is dense unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { - let events = *self.write_fetch.archetype_fetch(table_row); - EventWriter { - events: &mut events, - } + let events = self.write_fetch.archetype_fetch(table_row).into_inner(); + EventWriter { events } } } struct EventWriterState { From eda6ffa6e81a4f416599ed75e07b5d948dd89cb9 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Fri, 7 May 2021 22:56:27 -0400 Subject: [PATCH 26/39] Add EventWriter::new() to allow for creation outside of module Field should not be pub to avoid violating guarantees, so we get a constructor instead --- crates/bevy_ecs/src/event.rs | 4 ++++ crates/bevy_ecs/src/query/event.rs | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index a45c34a3d2867..405ece48d3bbe 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -168,6 +168,10 @@ impl<'a, T: Component> SystemParam for EventWriter<'a, T> { } impl<'a, T: Component> EventWriter<'a, T> { + pub fn new(events: &'a mut Events) -> Self { + EventWriter::<'a, T> { events } + } + pub fn send(&mut self, event: T) { self.events.send(event); } diff --git a/crates/bevy_ecs/src/query/event.rs b/crates/bevy_ecs/src/query/event.rs index 0fa671e5c0641..c5e8ddb94cf5a 100644 --- a/crates/bevy_ecs/src/query/event.rs +++ b/crates/bevy_ecs/src/query/event.rs @@ -7,7 +7,6 @@ use crate::{ component::{Component, ComponentId, StorageType}, prelude::World, storage::{Table, Tables}, - world::Mut, }; use std::any::TypeId; @@ -77,13 +76,13 @@ impl<'a, T: Component> Fetch<'a> for EventWriterFetch { .write_fetch .archetype_fetch(archetype_index) .into_inner(); - EventWriter { events } + EventWriter::new(events) } /// Returns the EventWriter of the next entity when the storage type of the query is dense unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { let events = self.write_fetch.archetype_fetch(table_row).into_inner(); - EventWriter { events } + EventWriter::new(events) } } struct EventWriterState { From 769edc15af70db0f2569f879418aeecd384c9e93 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Fri, 7 May 2021 22:59:31 -0400 Subject: [PATCH 27/39] Removed redundant storage type in EventWriterState --- crates/bevy_ecs/src/query/event.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/bevy_ecs/src/query/event.rs b/crates/bevy_ecs/src/query/event.rs index c5e8ddb94cf5a..37788cf5f3c29 100644 --- a/crates/bevy_ecs/src/query/event.rs +++ b/crates/bevy_ecs/src/query/event.rs @@ -87,7 +87,6 @@ impl<'a, T: Component> Fetch<'a> for EventWriterFetch { } struct EventWriterState { event_component_id: ComponentId, - event_storage_type: StorageType, /// EventWriter query parameters require write access to &mut Events write_state: WriteState>, } @@ -97,11 +96,6 @@ unsafe impl FetchState for EventWriterState { let event_component_id = world.components.get_id(TypeId::of::>()).unwrap(); EventWriterState { event_component_id, - event_storage_type: world - .components - .get_info(event_component_id) - .unwrap() - .storage_type(), write_state: WriteState::>::init(world), } } From 92358df0e4b8ac700863a178a6f5e4bb3a5f3e73 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Fri, 7 May 2021 23:01:09 -0400 Subject: [PATCH 28/39] Fixed visibility to make everything compile! --- crates/bevy_ecs/src/query/event.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/query/event.rs b/crates/bevy_ecs/src/query/event.rs index 37788cf5f3c29..32ec7c110e0a5 100644 --- a/crates/bevy_ecs/src/query/event.rs +++ b/crates/bevy_ecs/src/query/event.rs @@ -15,7 +15,7 @@ impl<'a, T: Component> WorldQuery for EventWriter<'a, T> { type State = EventWriterState; } -struct EventWriterFetch { +pub struct EventWriterFetch { /// EventWriter query parameters require write access to &mut Events write_fetch: WriteFetch>, storage_type: StorageType, @@ -85,7 +85,7 @@ impl<'a, T: Component> Fetch<'a> for EventWriterFetch { EventWriter::new(events) } } -struct EventWriterState { +pub struct EventWriterState { event_component_id: ComponentId, /// EventWriter query parameters require write access to &mut Events write_state: WriteState>, From 4b6cd8ceb0527bf0e635b24709b01eee4ce19ab1 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Fri, 7 May 2021 23:10:50 -0400 Subject: [PATCH 29/39] Code organization Moves impl blocks directly after types, organizes like-types together, presents shared functionality first --- crates/bevy_ecs/src/event.rs | 275 +++++++++++++++++------------------ 1 file changed, 137 insertions(+), 138 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 405ece48d3bbe..57d52c6fccc7a 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -140,144 +140,6 @@ impl Default for Events { } } -fn map_instance_event_with_id(event_instance: &EventInstance) -> (&T, EventId) { - (&event_instance.event, event_instance.event_id) -} - -fn map_instance_event(event_instance: &EventInstance) -> &T { - &event_instance.event -} - -/// Reads events of type `T` in order and tracks which events have already been read. -pub struct EventReader<'a, T: Component> { - last_event_count: Local<'a, (usize, PhantomData)>, - events: &'a Events, -} - -impl<'a, T: Component> SystemParam for EventReader<'a, T> { - type Fetch = ResState>; -} - -/// Sends events of type `T`. -pub struct EventWriter<'a, T: Component> { - events: &'a mut Events, -} - -impl<'a, T: Component> SystemParam for EventWriter<'a, T> { - type Fetch = ResMutState>; -} - -impl<'a, T: Component> EventWriter<'a, T> { - pub fn new(events: &'a mut Events) -> Self { - EventWriter::<'a, T> { events } - } - - pub fn send(&mut self, event: T) { - self.events.send(event); - } - - pub fn send_batch(&mut self, events: impl Iterator) { - self.events.extend(events); - } -} - -pub struct ManualEventReader { - last_event_count: usize, - _marker: PhantomData, -} - -impl Default for ManualEventReader { - fn default() -> Self { - ManualEventReader { - last_event_count: 0, - _marker: Default::default(), - } - } -} - -impl ManualEventReader { - /// See [`EventReader::iter`] - pub fn iter<'a>(&mut self, events: &'a Events) -> impl DoubleEndedIterator { - internal_event_reader(&mut self.last_event_count, events).map(|(e, _)| e) - } - - /// See [`EventReader::iter_with_id`] - pub fn iter_with_id<'a>( - &mut self, - events: &'a Events, - ) -> impl DoubleEndedIterator)> { - internal_event_reader(&mut self.last_event_count, events) - } -} - -/// Like [`iter_with_id`](EventReader::iter_with_id) except not emitting any traces for read -/// messages. -fn internal_event_reader<'a, T>( - last_event_count: &mut usize, - events: &'a Events, -) -> impl DoubleEndedIterator)> { - // if the reader has seen some of the events in a buffer, find the proper index offset. - // otherwise read all events in the buffer - let a_index = if *last_event_count > events.a_start_event_count { - *last_event_count - events.a_start_event_count - } else { - 0 - }; - let b_index = if *last_event_count > events.b_start_event_count { - *last_event_count - events.b_start_event_count - } else { - 0 - }; - *last_event_count = events.event_count; - match events.state { - State::A => events - .events_b - .get(b_index..) - .unwrap_or_else(|| &[]) - .iter() - .map(map_instance_event_with_id) - .chain( - events - .events_a - .get(a_index..) - .unwrap_or_else(|| &[]) - .iter() - .map(map_instance_event_with_id), - ), - State::B => events - .events_a - .get(a_index..) - .unwrap_or_else(|| &[]) - .iter() - .map(map_instance_event_with_id) - .chain( - events - .events_b - .get(b_index..) - .unwrap_or_else(|| &[]) - .iter() - .map(map_instance_event_with_id), - ), - } -} - -impl<'a, T: Component> EventReader<'a, T> { - /// Iterates over the events this EventReader has not seen yet. This updates the EventReader's - /// event counter, which means subsequent event reads will not include events that happened - /// before now. - pub fn iter(&mut self) -> impl DoubleEndedIterator { - self.iter_with_id().map(|(event, _id)| event) - } - - /// Like [`iter`](Self::iter), except also returning the [`EventId`] of the events. - pub fn iter_with_id(&mut self) -> impl DoubleEndedIterator)> { - internal_event_reader(&mut self.last_event_count.0, &self.events).map(|(event, id)| { - trace!("EventReader::iter() -> {}", id); - (event, id) - }) - } -} - impl Events { /// "Sends" an `event` by writing it to the current event buffer. [EventReader]s can then read /// the event. @@ -390,6 +252,143 @@ impl Events { } } +fn map_instance_event_with_id(event_instance: &EventInstance) -> (&T, EventId) { + (&event_instance.event, event_instance.event_id) +} + +fn map_instance_event(event_instance: &EventInstance) -> &T { + &event_instance.event +} + +/// Like [`iter_with_id`](EventReader::iter_with_id) except not emitting any traces for read +/// messages. +fn internal_event_reader<'a, T>( + last_event_count: &mut usize, + events: &'a Events, +) -> impl DoubleEndedIterator)> { + // if the reader has seen some of the events in a buffer, find the proper index offset. + // otherwise read all events in the buffer + let a_index = if *last_event_count > events.a_start_event_count { + *last_event_count - events.a_start_event_count + } else { + 0 + }; + let b_index = if *last_event_count > events.b_start_event_count { + *last_event_count - events.b_start_event_count + } else { + 0 + }; + *last_event_count = events.event_count; + match events.state { + State::A => events + .events_b + .get(b_index..) + .unwrap_or_else(|| &[]) + .iter() + .map(map_instance_event_with_id) + .chain( + events + .events_a + .get(a_index..) + .unwrap_or_else(|| &[]) + .iter() + .map(map_instance_event_with_id), + ), + State::B => events + .events_a + .get(a_index..) + .unwrap_or_else(|| &[]) + .iter() + .map(map_instance_event_with_id) + .chain( + events + .events_b + .get(b_index..) + .unwrap_or_else(|| &[]) + .iter() + .map(map_instance_event_with_id), + ), + } +} +/// Sends events of type `T`. +pub struct EventWriter<'a, T: Component> { + events: &'a mut Events, +} + +impl<'a, T: Component> SystemParam for EventWriter<'a, T> { + type Fetch = ResMutState>; +} + +impl<'a, T: Component> EventWriter<'a, T> { + pub fn new(events: &'a mut Events) -> Self { + EventWriter::<'a, T> { events } + } + + pub fn send(&mut self, event: T) { + self.events.send(event); + } + + pub fn send_batch(&mut self, events: impl Iterator) { + self.events.extend(events); + } +} + +/// Reads events of type `T` in order and tracks which events have already been read. +pub struct EventReader<'a, T: Component> { + last_event_count: Local<'a, (usize, PhantomData)>, + events: &'a Events, +} + +impl<'a, T: Component> SystemParam for EventReader<'a, T> { + type Fetch = ResState>; +} + +impl<'a, T: Component> EventReader<'a, T> { + /// Iterates over the events this EventReader has not seen yet. This updates the EventReader's + /// event counter, which means subsequent event reads will not include events that happened + /// before now. + pub fn iter(&mut self) -> impl DoubleEndedIterator { + self.iter_with_id().map(|(event, _id)| event) + } + + /// Like [`iter`](Self::iter), except also returning the [`EventId`] of the events. + pub fn iter_with_id(&mut self) -> impl DoubleEndedIterator)> { + internal_event_reader(&mut self.last_event_count.0, &self.events).map(|(event, id)| { + trace!("EventReader::iter() -> {}", id); + (event, id) + }) + } +} + +pub struct ManualEventReader { + last_event_count: usize, + _marker: PhantomData, +} + +impl Default for ManualEventReader { + fn default() -> Self { + ManualEventReader { + last_event_count: 0, + _marker: Default::default(), + } + } +} + +impl ManualEventReader { + /// See [`EventReader::iter`] + pub fn iter<'a>(&mut self, events: &'a Events) -> impl DoubleEndedIterator { + internal_event_reader(&mut self.last_event_count, events).map(|(e, _)| e) + } + + /// See [`EventReader::iter_with_id`] + pub fn iter_with_id<'a>( + &mut self, + events: &'a Events, + ) -> impl DoubleEndedIterator)> { + internal_event_reader(&mut self.last_event_count, events) + } +} + #[cfg(test)] mod tests { use super::*; From ac56e120f3779ff59543b9eed246c440347e2cd3 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Fri, 7 May 2021 23:12:03 -0400 Subject: [PATCH 30/39] Renamed State to BufferState for clarity Event buffer's state should not clash with State concept --- crates/bevy_ecs/src/event.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 57d52c6fccc7a..25afb5e320c2f 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -50,7 +50,7 @@ struct EventInstance { } #[derive(Debug)] -enum State { +enum BufferState { A, B, } @@ -124,7 +124,7 @@ pub struct Events { a_start_event_count: usize, b_start_event_count: usize, event_count: usize, - state: State, + state: BufferState, } impl Default for Events { @@ -135,7 +135,7 @@ impl Default for Events { event_count: 0, events_a: Vec::new(), events_b: Vec::new(), - state: State::A, + state: BufferState::A, } } } @@ -153,8 +153,8 @@ impl Events { let event_instance = EventInstance { event_id, event }; match self.state { - State::A => self.events_a.push(event_instance), - State::B => self.events_b.push(event_instance), + BufferState::A => self.events_a.push(event_instance), + BufferState::B => self.events_b.push(event_instance), } self.event_count += 1; @@ -181,14 +181,14 @@ impl Events { /// called once per frame/update. pub fn update(&mut self) { match self.state { - State::A => { + BufferState::A => { self.events_b = Vec::new(); - self.state = State::B; + self.state = BufferState::B; self.b_start_event_count = self.event_count; } - State::B => { + BufferState::B => { self.events_a = Vec::new(); - self.state = State::A; + self.state = BufferState::A; self.a_start_event_count = self.event_count; } } @@ -216,12 +216,12 @@ impl Events { pub fn drain(&mut self) -> impl Iterator + '_ { let map = |i: EventInstance| i.event; match self.state { - State::A => self + BufferState::A => self .events_b .drain(..) .map(map) .chain(self.events_a.drain(..).map(map)), - State::B => self + BufferState::B => self .events_a .drain(..) .map(map) @@ -246,8 +246,8 @@ impl Events { /// happen after this call and before the next `update()` call will be dropped. pub fn iter_current_update_events(&self) -> impl DoubleEndedIterator { match self.state { - State::A => self.events_a.iter().map(map_instance_event), - State::B => self.events_b.iter().map(map_instance_event), + BufferState::A => self.events_a.iter().map(map_instance_event), + BufferState::B => self.events_b.iter().map(map_instance_event), } } } @@ -280,7 +280,7 @@ fn internal_event_reader<'a, T>( }; *last_event_count = events.event_count; match events.state { - State::A => events + BufferState::A => events .events_b .get(b_index..) .unwrap_or_else(|| &[]) @@ -294,7 +294,7 @@ fn internal_event_reader<'a, T>( .iter() .map(map_instance_event_with_id), ), - State::B => events + BufferState::B => events .events_a .get(a_index..) .unwrap_or_else(|| &[]) From 44e8dcba0c12e5dce346aed01c2529e09e5e4053 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Fri, 7 May 2021 23:24:42 -0400 Subject: [PATCH 31/39] Added comments explaining need for tiny map functions --- crates/bevy_ecs/src/event.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 25afb5e320c2f..04c8280cb8519 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -252,10 +252,14 @@ impl Events { } } +// Needed to ensure match branches have same type signature +// As closures never have the same type fn map_instance_event_with_id(event_instance: &EventInstance) -> (&T, EventId) { (&event_instance.event, event_instance.event_id) } +// Needed to ensure match branches have same type signature +// As closures never have the same type fn map_instance_event(event_instance: &EventInstance) -> &T { &event_instance.event } From b454712124a41b499b993ecc774936945abcd241 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Fri, 7 May 2021 23:30:48 -0400 Subject: [PATCH 32/39] Fixed fetch for EventReader Thanks @TheRawMeatball --- crates/bevy_ecs/src/event.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 04c8280cb8519..ca7bb90e876cb 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -1,6 +1,6 @@ use crate::{ component::Component, - system::{Local, Query, ResMut, ResMutState, ResState, SystemParam}, + system::{LocalState, Query, ResMut, ResMutState, ResState, SystemParam}, }; use bevy_utils::tracing::trace; use std::{ @@ -339,12 +339,22 @@ impl<'a, T: Component> EventWriter<'a, T> { /// Reads events of type `T` in order and tracks which events have already been read. pub struct EventReader<'a, T: Component> { - last_event_count: Local<'a, (usize, PhantomData)>, + last_event_count: EventCount, events: &'a Events, } +/// Wrapper struct used by [EventReader] to count events +pub struct EventCount(usize, PhantomData); + +// #[derive(Default)] doesn't handle PhantomData properly, so we're stuck with a manual impl +impl Default for EventCount { + fn default() -> Self { + EventCount(0, PhantomData::default()) + } +} + impl<'a, T: Component> SystemParam for EventReader<'a, T> { - type Fetch = ResState>; + type Fetch = (ResState>, LocalState>); } impl<'a, T: Component> EventReader<'a, T> { From a4fa818e408cac0d3f65dacdd932045e777ec971 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 8 May 2021 00:50:32 -0400 Subject: [PATCH 33/39] Added EventConsumer Also added handy drain_with_id method --- crates/bevy_ecs/src/event.rs | 55 +++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index ca7bb90e876cb..cacaa73905956 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -213,20 +213,21 @@ impl Events { } /// Creates a draining iterator that removes all events. - pub fn drain(&mut self) -> impl Iterator + '_ { - let map = |i: EventInstance| i.event; - match self.state { - BufferState::A => self - .events_b - .drain(..) - .map(map) - .chain(self.events_a.drain(..).map(map)), - BufferState::B => self - .events_a - .drain(..) - .map(map) - .chain(self.events_b.drain(..).map(map)), - } + pub fn drain(&mut self) -> impl DoubleEndedIterator + '_ { + self.drain_with_id().map(|(e, _)| e) + } + + /// Creates a draining iterator that returns both events and their ids + pub fn drain_with_id(&mut self) -> impl DoubleEndedIterator)> + '_ { + let event_instances = match self.state { + BufferState::A => self.events_b.drain(..).chain(self.events_a.drain(..)), + BufferState::B => self.events_a.drain(..).chain(self.events_b.drain(..)), + }; + + event_instances.map(|ei| { + trace!("Events::drain_with_id -> {}", ei.event_id); + (ei.event, ei.event_id) + }) } pub fn extend(&mut self, events: I) @@ -374,6 +375,32 @@ impl<'a, T: Component> EventReader<'a, T> { } } +/// Reads and consumes all events of type T +/// +/// Useful for manual event cleanup when [AppBuilder::add_event::] is omitted, +/// allowing events to accumulate on your components or resources until consumed. +/// Note: due to the draining nature of this reader, you probably only want one +/// EventConsumer per event storage location + event type combination. +pub struct EventConsumer<'a, T: Component> { + events: &'a mut Events, +} + +impl<'a, T: Component> SystemParam for EventConsumer<'a, T> { + type Fetch = ResMutState>; +} + +impl<'a, T: Component> EventConsumer<'a, T> { + /// Drains all available events this EventConsumer has access to into an iterator + pub fn drain(self) -> impl DoubleEndedIterator + 'a { + self.events.drain() + } + + /// Drains all available events this EventConsumer has access to into an iterator and returns the id + pub fn drain_with_id(self) -> impl DoubleEndedIterator)> + 'a { + self.events.drain_with_id() + } +} + pub struct ManualEventReader { last_event_count: usize, _marker: PhantomData, From 88052f3ab4bf25858392166337c9306df3d3e7ef Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 8 May 2021 01:19:53 -0400 Subject: [PATCH 34/39] Added EventConsumer example --- examples/ecs/per_entity_events.rs | 65 +++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 74724ae911273..911b52626291f 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -1,5 +1,8 @@ -use bevy::app::{Events, ManualEventReader}; use bevy::prelude::*; +use bevy::{ + app::{Events, ManualEventReader}, + core::FixedTimestep, +}; /// In this example, we show how to store events of a given type /// as a component on individual entities rather than in a single resource. @@ -39,6 +42,11 @@ fn main() { .add_system(add_number.system().label("action_handling")) .add_system(scale_selected.system().after("action_handling")) .add_system(update_text_color.system().after("action_handling")) + .add_system( + move_text + .system() + .with_run_criteria(FixedTimestep::step(TIME_STEP)), + ) .run() } @@ -55,6 +63,7 @@ struct InteractableBundle { selectable: Selectable, rainbow: ColorChoices, cycle_color_events: Events, + move_events: Events, add_number_events: Events, } @@ -81,6 +90,7 @@ impl InteractableBundle { selectable: Selectable, rainbow: ColorChoices::Red, cycle_color_events: Events::::default(), + move_events: Events::default(), add_number_events: Events::::default(), } } @@ -201,7 +211,11 @@ fn input_dispatch( // You could also access the &Events component directly // then send events to that component with `Events::send` mut query: Query< - (EventWriter, EventWriter), + ( + EventWriter, + EventWriter, + EventWriter, + ), With, >, selected: Res, @@ -211,13 +225,27 @@ fn input_dispatch( let (mut cycle_actions, mut add_actions) = query.get_mut(selected.entity).unwrap(); - // Inputs for cycling colors - if keyboard_input.just_pressed(Space) { - cycle_actions.send(CycleColorAction); - } - - // Inputs for sending numbers to be added for key_code in keyboard_input.get_just_pressed() { + match key_code { + // Color changing + Space => cycle_actions.send(CycleColorAction), + // Movement + Left => move_actions.send(MoveAction { + transform: Transform::from_xyz(-MOVE_DISTANCE, 0.0, 0.0), + }), + Right => move_actions.send(MoveAction { + transform: Transform::from_xyz(MOVE_DISTANCE, 0.0, 0.0), + }), + Down => move_actions.send(MoveAction { + transform: Transform::from_xyz(0.0, -MOVE_DISTANCE, 0.0), + }), + Up => move_actions.send(MoveAction { + transform: Transform::from_xyz(0.0, MOVE_DISTANCE, 0.0), + }), + _ => (), + } + + // Inputs for sending numbers to be added if (key_code as u8) < 10 { add_actions.send(AddNumberAction { // The keycode for KeyCode::Key1 is 0 @@ -265,3 +293,24 @@ fn add_number( text.sections[0].value = new_number.to_string(); } } + +const MOVE_DISTANCE: f32 = 100.0; +const TIME_STEP: f32 = 2; +#[derive(Default)] +struct MoveAction { + transform: Transform, +} + +// When events are registered in the AppBuilder using .add_event(), +// a system will automatically be created to clean them up after two frames. +// This can be problematic if your event-consuming systems ever skip frames (such as due to a fixed timestep run criteria). +// We can get around this by not registering them, and instead handling clean-up by consuming them when read. +// Be careful though: once consumed, other systems will not be able to read them! +fn move_text(query: Query<(&mut Transform, EventConsumer)>) { + for (mut transform, events) in query.iter() { + // Unlike EventReaders which simply iterate, EventConsumers drain the events they read + for move_action in events.drain() { + *transform += move_action.transform * TIME_STEP; + } + } +} From feb3d37555c4acad6617ddd98f7cb853557e8f78 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 8 May 2021 01:28:31 -0400 Subject: [PATCH 35/39] Cleaned up example by organizing code into modules --- examples/ecs/per_entity_events.rs | 258 ++++++++++++++++-------------- 1 file changed, 137 insertions(+), 121 deletions(-) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs index 911b52626291f..ddded15c46c45 100644 --- a/examples/ecs/per_entity_events.rs +++ b/examples/ecs/per_entity_events.rs @@ -19,7 +19,6 @@ use bevy::{ /// /// This specific example shows a simple input -> action dispatch use case, /// where this pattern helps to avoid messy rechecking and allows simple merging of multiple event input streams. -/// fn main() { App::build() .add_plugins(DefaultPlugins) @@ -45,110 +44,78 @@ fn main() { .add_system( move_text .system() + .label("action_handling") .with_run_criteria(FixedTimestep::step(TIME_STEP)), ) .run() } -// Tracks which entity is selected -struct Selected { - entity: Entity, -} -// Marks entities as selectable -struct Selectable; -#[derive(Bundle)] -struct InteractableBundle { - #[bundle] - text_bundle: TextBundle, - selectable: Selectable, - rainbow: ColorChoices, - cycle_color_events: Events, - move_events: Events, - add_number_events: Events, -} - -impl InteractableBundle { - // FIXME: fix position - fn new(x: f32, y: f32, font_handle: &Handle) -> Self { - InteractableBundle { - text_bundle: TextBundle { - text: Text::with_section( - "0", - TextStyle { - font: font_handle.clone(), - font_size: 60.0, - color: Color::WHITE, - }, - TextAlignment { - vertical: VerticalAlign::Center, - horizontal: HorizontalAlign::Center, - }, - ), - transform: Transform::from_xyz(x, y, 0.0), - ..Default::default() - }, - selectable: Selectable, - rainbow: ColorChoices::Red, - cycle_color_events: Events::::default(), - move_events: Events::default(), - add_number_events: Events::::default(), - } +pub mod setup { + #[derive(Bundle)] + struct InteractableBundle { + #[bundle] + text_bundle: TextBundle, + selectable: Selectable, + rainbow: ColorChoices, + cycle_color_events: Events, + move_events: Events, + add_number_events: Events, } -} - -enum ColorChoices { - Red, - Blue, - Violet, -} - -impl Iterator for ColorChoices { - type Item = Self; - - fn next(&mut self) -> Option { - use ColorChoices::*; - Some(match *self { - Red => Blue, - Blue => Violet, - Violet => Red, - }) - } -} - -impl From<&ColorChoices> for Color { - fn from(rainbow: &ColorChoices) -> Color { - use ColorChoices::*; - match rainbow { - Red => Color::RED, - Blue => Color::BLUE, - Violet => Color::VIOLET, + + impl InteractableBundle { + // FIXME: fix position + fn new(x: f32, y: f32, font_handle: &Handle) -> Self { + InteractableBundle { + text_bundle: TextBundle { + text: Text::with_section( + "0", + TextStyle { + font: font_handle.clone(), + font_size: 60.0, + color: Color::WHITE, + }, + TextAlignment { + vertical: VerticalAlign::Center, + horizontal: HorizontalAlign::Center, + }, + ), + transform: Transform::from_xyz(x, y, 0.0), + ..Default::default() + }, + selectable: Selectable, + rainbow: ColorChoices::Red, + cycle_color_events: Events::::default(), + move_events: Events::default(), + add_number_events: Events::::default(), + } } } + + fn setup(mut commands: Commands, asset_server: Res) { + commands.spawn_bundle(OrthographicCameraBundle::new_2d()); + + let font_handle = asset_server.load("fonts/FiraSans-Bold.ttf"); + // Spawns the first entity, and grabs the Entity id that is being allocated + let first_entity = commands + .spawn_bundle(InteractableBundle::new(-200.0, 0.0, &font_handle)) + .id(); + commands.insert_resource(Selected { + entity: first_entity, + }); + + commands.spawn_bundle(InteractableBundle::new(0.0, 0.0, &font_handle)); + commands.spawn_bundle(InteractableBundle::new(200.0, 0.0, &font_handle)); + } + } -// Events can be simple unit structs -struct CycleColorAction; -// Or store data to be responded to -struct AddNumberAction { - number: u8, -} - -fn setup(mut commands: Commands, asset_server: Res) { - // Don't forget to include a camera! - commands.spawn_bundle(UiCameraBundle::default()); - - let font_handle = asset_server.load("fonts/FiraSans-Bold.ttf"); - // Spawns the first entity, and grabs the Entity id that is being allocated - let first_entity = commands - .spawn_bundle(InteractableBundle::new(-200.0, 400.0, &font_handle)) - .id(); - commands.insert_resource(Selected { - entity: first_entity, - }); - - commands.spawn_bundle(InteractableBundle::new(0.0, 400.0, &font_handle)); - commands.spawn_bundle(InteractableBundle::new(200.0, 400.0, &font_handle)); +pub mod selection { +// Tracks which entity is selected +struct Selected { + entity: Entity, } +// Marks entities as selectable +struct Selectable; enum CycleBehavior { Forward, @@ -204,6 +171,9 @@ fn scale_selected( } } +} + +pub mod input { /// Dispatches actions to entities based on the input /// Note that we can store several events at once! /// Try pressing both "1" and "3" to add 4 to the selected display @@ -255,20 +225,85 @@ fn input_dispatch( } } -fn cycle_color(mut query: Query<(&mut ColorChoices, EventReader)>) { - for (mut color, action_queue) in query.iter_mut() { - for _ in action_queue.iter() { - *color = color.next().unwrap(); +} + +pub mod colors { + struct CycleColorAction; + enum ColorChoices { + Red, + Blue, + Violet, + } + + impl Iterator for ColorChoices { + type Item = Self; + + fn next(&mut self) -> Option { + use ColorChoices::*; + Some(match *self { + Red => Blue, + Blue => Violet, + Violet => Red, + }) + } + } + + impl From<&ColorChoices> for Color { + fn from(rainbow: &ColorChoices) -> Color { + use ColorChoices::*; + match rainbow { + Red => Color::RED, + Blue => Color::BLUE, + Violet => Color::VIOLET, + } } } + + fn cycle_color(mut query: Query<(&mut ColorChoices, EventReader)>) { + for (mut color, action_queue) in query.iter_mut() { + for _ in action_queue.iter() { + *color = color.next().unwrap(); + } + } + } + + fn update_text_color(mut query: Query<(&mut Text, &ColorChoices), Changed>) { + for (mut text, rainbow) in query.iter_mut() { + text.sections[0].style.color = rainbow.into(); + } + } + } -fn update_text_color(mut query: Query<(&mut Text, &ColorChoices), Changed>) { - for (mut text, rainbow) in query.iter_mut() { - text.sections[0].style.color = rainbow.into(); +pub mod movement { + struct MoveAction { + transform: Transform, + } + + const MOVE_DISTANCE: f32 = 100.0; + const TIME_STEP: f32 = 2; + + // When events are registered in the AppBuilder using .add_event(), + // a system will automatically be created to clean them up after two frames. + // This can be problematic if your event-consuming systems ever skip frames (such as due to a fixed timestep run criteria). + // We can get around this by not registering them, and instead handling clean-up by consuming them when read. + // Be careful though: once consumed, other systems will not be able to read them! + fn move_text(query: Query<(&mut Transform, EventConsumer)>) { + for (mut transform, events) in query.iter() { + // Unlike EventReaders which simply iterate, EventConsumers drain the events they read + for move_action in events.drain() { + *transform += move_action.transform * TIME_STEP; + } + } } + } +pub mod addition { +// Or store data to be responded to +struct AddNumberAction { + number: u8, +} // Just as when using Events as a resource, you can work with `Events` directly instead // EventReader and EventWriter are just convenient wrappers that better communicate intent // And store state automatically for you @@ -294,23 +329,4 @@ fn add_number( } } -const MOVE_DISTANCE: f32 = 100.0; -const TIME_STEP: f32 = 2; -#[derive(Default)] -struct MoveAction { - transform: Transform, -} - -// When events are registered in the AppBuilder using .add_event(), -// a system will automatically be created to clean them up after two frames. -// This can be problematic if your event-consuming systems ever skip frames (such as due to a fixed timestep run criteria). -// We can get around this by not registering them, and instead handling clean-up by consuming them when read. -// Be careful though: once consumed, other systems will not be able to read them! -fn move_text(query: Query<(&mut Transform, EventConsumer)>) { - for (mut transform, events) in query.iter() { - // Unlike EventReaders which simply iterate, EventConsumers drain the events they read - for move_action in events.drain() { - *transform += move_action.transform * TIME_STEP; - } - } -} +} \ No newline at end of file From cd4b83cd6494da095f42a4cddc1f93f4b0f24304 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 8 May 2021 02:29:31 -0400 Subject: [PATCH 36/39] Added test for Event* as system parameters --- crates/bevy_ecs/src/event.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index cacaa73905956..cc8c2e1e743f1 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -433,12 +433,45 @@ impl ManualEventReader { #[cfg(test)] mod tests { use super::*; + use crate::schedule::{Stage, SystemStage}; + use crate::system::IntoSystem; + use crate::world::World; #[derive(Copy, Clone, PartialEq, Eq, Debug)] struct TestEvent { i: usize, } + #[test] + fn event_system_params() { + let world = World::default(); + struct E; + fn writes(ew: EventWriter) { + ew.send(E) + } + fn reads(er: EventReader) { + er.iter(); + } + fn consumes(ec: EventConsumer) { + ec.drain(); + } + + let mut stage1 = SystemStage::parallel(); + stage1.add_system(writes.system()); + stage1.add_system(reads.system()); + + stage1.run(&mut World::default()); + let current_events = world.get_resource::>().unwrap(); + assert!(current_events.events_a.len() == 1); + + let mut stage2 = SystemStage::parallel(); + stage2.add_system(consumes.system()); + + stage2.run(&mut World::default()); + let current_events = world.get_resource::>().unwrap(); + assert!(current_events.events_a.len() == 0); + } + #[test] fn test_events() { let mut events = Events::::default(); From 3c8eb92f8263f04fc5b4d64df96f7934f61bf1fd Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 8 May 2021 02:39:34 -0400 Subject: [PATCH 37/39] Moved back to #[derive(SystemParam)] --- crates/bevy_ecs/src/event.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index cc8c2e1e743f1..26024e2ffe063 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -316,14 +316,11 @@ fn internal_event_reader<'a, T>( } } /// Sends events of type `T`. +#[derive(SystemParam)] pub struct EventWriter<'a, T: Component> { events: &'a mut Events, } -impl<'a, T: Component> SystemParam for EventWriter<'a, T> { - type Fetch = ResMutState>; -} - impl<'a, T: Component> EventWriter<'a, T> { pub fn new(events: &'a mut Events) -> Self { EventWriter::<'a, T> { events } @@ -339,6 +336,7 @@ impl<'a, T: Component> EventWriter<'a, T> { } /// Reads events of type `T` in order and tracks which events have already been read. +#[derive(SystemParam)] pub struct EventReader<'a, T: Component> { last_event_count: EventCount, events: &'a Events, @@ -354,10 +352,6 @@ impl Default for EventCount { } } -impl<'a, T: Component> SystemParam for EventReader<'a, T> { - type Fetch = (ResState>, LocalState>); -} - impl<'a, T: Component> EventReader<'a, T> { /// Iterates over the events this EventReader has not seen yet. This updates the EventReader's /// event counter, which means subsequent event reads will not include events that happened @@ -381,14 +375,11 @@ impl<'a, T: Component> EventReader<'a, T> { /// allowing events to accumulate on your components or resources until consumed. /// Note: due to the draining nature of this reader, you probably only want one /// EventConsumer per event storage location + event type combination. +#[derive(SystemParam)] pub struct EventConsumer<'a, T: Component> { events: &'a mut Events, } -impl<'a, T: Component> SystemParam for EventConsumer<'a, T> { - type Fetch = ResMutState>; -} - impl<'a, T: Component> EventConsumer<'a, T> { /// Drains all available events this EventConsumer has access to into an iterator pub fn drain(self) -> impl DoubleEndedIterator + 'a { From f8a36c1e388940443628cea32ed8f9c782043819 Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 11 May 2021 16:28:00 -0400 Subject: [PATCH 38/39] Revert "Clean up on Examples README" This reverts commit 525ccd50838e52905cb15cbaaf80f25ad21dca8b. --- examples/README.md | 275 ++++++++++++++++++++++----------------------- 1 file changed, 137 insertions(+), 138 deletions(-) diff --git a/examples/README.md b/examples/README.md index 79e25455180cf..096afc63e4193 100644 --- a/examples/README.md +++ b/examples/README.md @@ -71,177 +71,176 @@ git checkout v0.4.0 ## Hello, World! -| Example | Main | Description | -| ------------- | ------------------------------------ | ------------------------------------------------- | -| `hello_world` | [`hello_world.rs`](./hello_world.rs) | Runs a minimal example that outputs "hello world" | +Example | Main | Description +--- | --- | --- +`hello_world` | [`hello_world.rs`](./hello_world.rs) | Runs a minimal example that outputs "hello world" # Cross-Platform Examples ## 2D Rendering -| Example | Main | Description | -| ----------------- | -------------------------------------------------- | -------------------------------------------------------------------------- | -| `contributors` | [`2d/contributors.rs`](./2d/contributors.rs) | Displays each contributor as a bouncy bevy-ball! | -| `many_sprites` | [`2d/many_sprites.rs`](./2d/many_sprites.rs) | Displays many sprites in a grid arrangement! Used for performance testing. | -| `mesh` | [`2d/mesh.rs`](./2d/mesh.rs) | Renders a custom mesh | -| `sprite` | [`2d/sprite.rs`](./2d/sprite.rs) | Renders a sprite | -| `sprite_sheet` | [`2d/sprite_sheet.rs`](./2d/sprite_sheet.rs) | Renders an animated sprite | -| `text2d` | [`2d/text2d.rs`](./2d/text2d.rs) | Generates text in 2d | -| `sprite_flipping` | [`2d/sprite_flipping.rs`](./2d/sprite_flipping.rs) | Renders a sprite flipped along an axis | -| `texture_atlas` | [`2d/texture_atlas.rs`](./2d/texture_atlas.rs) | Generates a texture atlas (sprite sheet) from individual sprites | +Example | Main | Description +--- | --- | --- +`contributors` | [`2d/contributors.rs`](./2d/contributors.rs) | Displays each contributor as a bouncy bevy-ball! +`many_sprites` | [`2d/many_sprites.rs`](./2d/many_sprites.rs) | Displays many sprites in a grid arragement! Used for performance testing. +`mesh` | [`2d/mesh.rs`](./2d/mesh.rs) | Renders a custom mesh +`sprite` | [`2d/sprite.rs`](./2d/sprite.rs) | Renders a sprite +`sprite_sheet` | [`2d/sprite_sheet.rs`](./2d/sprite_sheet.rs) | Renders an animated sprite +`text2d` | [`2d/text2d.rs`](./2d/text2d.rs) | Generates text in 2d +`sprite_flipping` | [`2d/sprite_flipping.rs`](./2d/sprite_flipping.rs) | Renders a sprite flipped along an axis +`texture_atlas` | [`2d/texture_atlas.rs`](./2d/texture_atlas.rs) | Generates a texture atlas (sprite sheet) from individual sprites ## 3D Rendering -| Example | File | Description | -| ------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | -| `3d_scene` | [`3d/3d_scene.rs`](./3d/3d_scene.rs) | Simple 3D scene with basic shapes and lighting | -| `load_gltf` | [`3d/load_gltf.rs`](./3d/load_gltf.rs) | Loads and renders a gltf file as a scene | -| `msaa` | [`3d/msaa.rs`](./3d/msaa.rs) | Configures MSAA (Multi-Sample Anti-Aliasing) for smoother edges | -| `orthographic` | [`3d/orthographic.rs`](./3d/orthographic.rs) | Shows how to create a 3D orthographic view (for isometric-look games or CAD applications) | -| `parenting` | [`3d/parenting.rs`](./3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations | -| `pbr` | [`3d/pbr.rs`](./3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties | -| `render_to_texture` | [`3d/render_to_texture.rs`](./3d/render_to_texture.rs) | Shows how to render to texture | -| `spawner` | [`3d/spawner.rs`](./3d/spawner.rs) | Renders a large number of cubes with changing position and material | -| `texture` | [`3d/texture.rs`](./3d/texture.rs) | Shows configuration of texture materials | -| `update_gltf_scene` | [`3d/update_gltf_scene.rs`](./3d/update_gltf_scene.rs) | Update a scene from a gltf file, either by spawning the scene as a child of another entity, or by accessing the entities of the scene | -| `wireframe` | [`3d/wireframe.rs`](./3d/wireframe.rs) | Showcases wireframe rendering | -| `z_sort_debug` | [`3d/z_sort_debug.rs`](./3d/z_sort_debug.rs) | Visualizes camera Z-ordering | +Example | File | Description +--- | --- | --- +`3d_scene` | [`3d/3d_scene.rs`](./3d/3d_scene.rs) | Simple 3D scene with basic shapes and lighting +`load_gltf` | [`3d/load_gltf.rs`](./3d/load_gltf.rs) | Loads and renders a gltf file as a scene +`msaa` | [`3d/msaa.rs`](./3d/msaa.rs) | Configures MSAA (Multi-Sample Anti-Aliasing) for smoother edges +`orthographic` | [`3d/orthographic.rs`](./3d/orthographic.rs) | Shows how to create a 3D orthographic view (for isometric-look games or CAD applications) +`parenting` | [`3d/parenting.rs`](./3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations +`pbr` | [`3d/pbr.rs`](./3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties +`render_to_texture` | [`3d/render_to_texture.rs`](./3d/render_to_texture.rs) | Shows how to render to texture +`spawner` | [`3d/spawner.rs`](./3d/spawner.rs) | Renders a large number of cubes with changing position and material +`texture` | [`3d/texture.rs`](./3d/texture.rs) | Shows configuration of texture materials +`update_gltf_scene` | [`3d/update_gltf_scene.rs`](./3d/update_gltf_scene.rs) | Update a scene from a gltf file, either by spawning the scene as a child of another entity, or by accessing the entities of the scene +`wireframe` | [`3d/wireframe.rs`](./3d/wireframe.rs) | Showcases wireframe rendering +`z_sort_debug` | [`3d/z_sort_debug.rs`](./3d/z_sort_debug.rs) | Visualizes camera Z-ordering ## Application -| Example | File | Description | -| ----------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------- | -| `custom_loop` | [`app/custom_loop.rs`](./app/custom_loop.rs) | Demonstrates how to create a custom runner (to update an app manually). | -| `drag_and_drop` | [`app/drag_and_drop.rs`](./app/drag_and_drop.rs) | An example that shows how to handle drag and drop in an app. | -| `empty` | [`app/empty.rs`](./app/empty.rs) | An empty application (does nothing) | -| `empty_defaults` | [`app/empty_defaults.rs`](./app/empty_defaults.rs) | An empty application with default plugins | -| `headless` | [`app/headless.rs`](./app/headless.rs) | An application that runs without default plugins | -| `logs` | [`app/logs.rs`](./app/logs.rs) | Illustrate how to use generate log output | -| `plugin` | [`app/plugin.rs`](./app/plugin.rs) | Demonstrates the creation and registration of a custom plugin | -| `plugin_group` | [`app/plugin_group.rs`](./app/plugin_group.rs) | Demonstrates the creation and registration of a custom plugin group | -| `return_after_run` | [`app/return_after_run.rs`](./app/return_after_run.rs) | Show how to return to main after the Bevy app has exited | -| `thread_pool_resources` | [`app/thread_pool_resources.rs`](./app/thread_pool_resources.rs) | Creates and customizes the internal thread pool | +Example | File | Description +--- | --- | --- +`custom_loop` | [`app/custom_loop.rs`](./app/custom_loop.rs) | Demonstrates how to create a custom runner (to update an app manually). +`drag_and_drop` | [`app/drag_and_drop.rs`](./app/drag_and_drop.rs) | An example that shows how to handle drag and drop in an app. +`empty` | [`app/empty.rs`](./app/empty.rs) | An empty application (does nothing) +`empty_defaults` | [`app/empty_defaults.rs`](./app/empty_defaults.rs) | An empty application with default plugins +`headless` | [`app/headless.rs`](./app/headless.rs) | An application that runs without default plugins +`logs` | [`app/logs.rs`](./app/logs.rs) | Illustrate how to use generate log output +`plugin` | [`app/plugin.rs`](./app/plugin.rs) | Demonstrates the creation and registration of a custom plugin +`plugin_group` | [`app/plugin_group.rs`](./app/plugin_group.rs) | Demonstrates the creation and registration of a custom plugin group +`return_after_run` | [`app/return_after_run.rs`](./app/return_after_run.rs) | Show how to return to main after the Bevy app has exited +`thread_pool_resources` | [`app/thread_pool_resources.rs`](./app/thread_pool_resources.rs) | Creates and customizes the internal thread pool ## Assets -| Example | File | Description | -| --------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------- | -| `asset_loading` | [`asset/asset_loading.rs`](./asset/asset_loading.rs) | Demonstrates various methods to load assets | -| `custom_asset` | [`asset/custom_asset.rs`](./asset/custom_asset.rs) | Implements a custom asset loader | -| `custom_asset_io` | [`asset/custom_asset_io.rs`](./asset/custom_asset_io.rs) | Implements a custom asset I/O loader | -| `hot_asset_reloading` | [`asset/hot_asset_reloading.rs`](./asset/hot_asset_reloading.rs) | Demonstrates automatic reloading of assets when modified on disk | +Example | File | Description +--- | --- | --- +`asset_loading` | [`asset/asset_loading.rs`](./asset/asset_loading.rs) | Demonstrates various methods to load assets +`custom_asset` | [`asset/custom_asset.rs`](./asset/custom_asset.rs) | Implements a custom asset loader +`custom_asset_io` | [`asset/custom_asset_io.rs`](./asset/custom_asset_io.rs) | Implements a custom asset io loader +`hot_asset_reloading` | [`asset/hot_asset_reloading.rs`](./asset/hot_asset_reloading.rs) | Demonstrates automatic reloading of assets when modified on disk ## Audio -| Example | File | Description | -| ------- | ------------------------------------ | ---------------------------------------- | -| `audio` | [`audio/audio.rs`](./audio/audio.rs) | Shows how to load and play an audio file | +Example | File | Description +--- | --- | --- +`audio` | [`audio/audio.rs`](./audio/audio.rs) | Shows how to load and play an audio file ## Diagnostics -| Example | File | Description | -| ------------------- | ------------------------------------------------------------------------ | ------------------------------------------------- | -| `custom_diagnostic` | [`diagnostics/custom_diagnostic.rs`](./diagnostics/custom_diagnostic.rs) | Shows how to create a custom diagnostic | -| `log_diagnostics` | [`diagnostics/log_diagnostics.rs`](./diagnostics/log_diagnostics.rs) | Add a plugin that logs diagnostics to the console | +Example | File | Description +--- | --- | --- +`custom_diagnostic` | [`diagnostics/custom_diagnostic.rs`](./diagnostics/custom_diagnostic.rs) | Shows how to create a custom diagnostic +`log_diagnostics` | [`diagnostics/log_diagnostics.rs`](./diagnostics/log_diagnostics.rs) | Add a plugin that logs diagnostics to the console ## ECS (Entity Component System) -| Example | File | Description | -| ------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | -| `ecs_guide` | [`ecs/ecs_guide.rs`](./ecs/ecs_guide.rs) | Full guide to Bevy's ECS | -| `change_detection` | [`ecs/change_detection.rs`](./ecs/change_detection.rs) | Change detection on components | -| `event` | [`ecs/event.rs`](./ecs/event.rs) | Illustrates event creation, activation, and reception | -| `per_entity_events` | [`ecs/per_entity_events.rs`](./ecs/per_entity_events.rs) | Demonstrates how to store events on individual entities in a channel-like fashion. | -| `fixed_timestep` | [`ecs/fixed_timestep.rs`](./ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick | -| `hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities | -| `parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` | -| `query_bundle` | [`ecs/query_bundle.rs`](./ecs/query_bundle.rs) | Shows how to query entities that contain components in a `Bundle` | -| `removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame. | -| `startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) | -| `state` | [`ecs/state.rs`](./ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state | -| `system_chaining` | [`ecs/system_chaining.rs`](./ecs/system_chaining.rs) | Chain two systems together, specifying a return type in a system (such as `Result`) | -| `system_param` | [`ecs/system_param.rs`](./ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam` | -| `system_sets` | [`ecs/system_sets.rs`](./ecs/system_sets.rs) | Shows `SystemSet` use along with run criterion | -| `timers` | [`ecs/timers.rs`](./ecs/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state | +Example | File | Description +--- | --- | --- +`ecs_guide` | [`ecs/ecs_guide.rs`](./ecs/ecs_guide.rs) | Full guide to Bevy's ECS +`change_detection` | [`ecs/change_detection.rs`](./ecs/change_detection.rs) | Change detection on components +`event` | [`ecs/event.rs`](./ecs/event.rs) | Illustrates event creation, activation, and reception +`fixed_timestep` | [`ecs/fixed_timestep.rs`](./ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick +`hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities +`parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` +`query_bundle` | [`ecs/query_bundle.rs`](./ecs/query_bundle.rs) | Shows how to query entities that contain components in a `Bundle` +`removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame. +`startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) +`state` | [`ecs/state.rs`](./ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state +`system_chaining` | [`ecs/system_chaining.rs`](./ecs/system_chaining.rs) | Chain two systems together, specifying a return type in a system (such as `Result`) +`system_param` | [`ecs/system_param.rs`](./ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam` +`system_sets` | [`ecs/system_sets.rs`](./ecs/system_sets.rs) | Shows `SystemSet` use along with run criterion +`timers` | [`ecs/timers.rs`](./ecs/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state ## Games -| Example | File | Description | -| ------------------- | ---------------------------------------------------------- | ------------------------------------------------ | -| `alien_cake_addict` | [`game/alien_cake_addict.rs`](./game/alien_cake_addict.rs) | Eat the cakes. Eat them all. An example 3D game | -| `breakout` | [`game/breakout.rs`](./game/breakout.rs) | An implementation of the classic game "Breakout" | +Example | File | Description +--- | --- | --- +`alien_cake_addict` | [`game/alien_cake_addict.rs`](./game/alien_cake_addict.rs) | Eat the cakes. Eat them all. An example 3D game +`breakout` | [`game/breakout.rs`](./game/breakout.rs) | An implementation of the classic game "Breakout" ## Input -| Example | File | Description | -| ----------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------- | -| `char_input_events` | [`input/char_input_events.rs`](./input/char_input_events.rs) | Prints out all chars as they are inputted. | -| `gamepad_input` | [`input/gamepad_input.rs`](./input/gamepad_input.rs) | Shows handling of gamepad input, connections, and disconnections | -| `gamepad_input_events` | [`input/gamepad_input_events.rs`](./input/gamepad_input_events.rs) | Iterates and prints gamepad input and connection events | -| `keyboard_input` | [`input/keyboard_input.rs`](./input/keyboard_input.rs) | Demonstrates handling a key press/release | -| `keyboard_input_events` | [`input/keyboard_input_events.rs`](./input/keyboard_input_events.rs) | Prints out all keyboard events | -| `keyboard_modifiers` | [`input/keyboard_modifiers.rs`](./input/keyboard_modifiers.rs) | Demonstrates using key modifiers (ctrl, shift) | -| `mouse_input` | [`input/mouse_input.rs`](./input/mouse_input.rs) | Demonstrates handling a mouse button press/release | -| `mouse_input_events` | [`input/mouse_input_events.rs`](./input/mouse_input_events.rs) | Prints out all mouse events (buttons, movement, etc.) | -| `touch_input` | [`input/touch_input.rs`](./input/touch_input.rs) | Displays touch presses, releases, and cancels | -| `touch_input_events` | [`input/touch_input_events.rs`](./input/touch_input_events.rs) | Prints out all touch inputs | +Example | File | Description +--- | --- | --- +`char_input_events` | [`input/char_input_events.rs`](./input/char_input_events.rs) | Prints out all chars as they are inputted. +`gamepad_input` | [`input/gamepad_input.rs`](./input/gamepad_input.rs) | Shows handling of gamepad input, connections, and disconnections +`gamepad_input_events` | [`input/gamepad_input_events.rs`](./input/gamepad_input_events.rs) | Iterates and prints gamepad input and connection events +`keyboard_input` | [`input/keyboard_input.rs`](./input/keyboard_input.rs) | Demonstrates handling a key press/release +`keyboard_input_events` | [`input/keyboard_input_events.rs`](./input/keyboard_input_events.rs) | Prints out all keyboard events +`keyboard_modifiers` | [`input/keyboard_modifiers.rs`](./input/keyboard_modifiers.rs) | Demonstrates using key modifiers (ctrl, shift) +`mouse_input` | [`input/mouse_input.rs`](./input/mouse_input.rs) | Demonstrates handling a mouse button press/release +`mouse_input_events` | [`input/mouse_input_events.rs`](./input/mouse_input_events.rs) | Prints out all mouse events (buttons, movement, etc.) +`touch_input` | [`input/touch_input.rs`](./input/touch_input.rs) | Displays touch presses, releases, and cancels +`touch_input_events` | [`input/touch_input_events.rs`](./input/touch_input_events.rs) | Prints out all touch inputs ## Reflection -| Example | File | Description | -| -------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | -| `reflection` | [`reflection/reflection.rs`](reflection/reflection.rs) | Demonstrates how reflection in Bevy provides a way to dynamically interact with Rust types | -| `generic_reflection` | [`reflection/generic_reflection.rs`](reflection/generic_reflection.rs) | Registers concrete instances of generic types that may be used with reflection | -| `reflection_types` | [`reflection/reflection_types.rs`](reflection/reflection_types.rs) | Illustrates the various reflection types available | -| `trait_reflection` | [`reflection/trait_reflection.rs`](reflection/trait_reflection.rs) | Allows reflection with trait objects | +Example | File | Description +--- | --- | --- +`reflection` | [`reflection/reflection.rs`](reflection/reflection.rs) | Demonstrates how reflection in Bevy provides a way to dynamically interact with Rust types +`generic_reflection` | [`reflection/generic_reflection.rs`](reflection/generic_reflection.rs) | Registers concrete instances of generic types that may be used with reflection +`reflection_types` | [`reflection/reflection_types.rs`](reflection/reflection_types.rs) | Illustrates the various reflection types available +`trait_reflection` | [`reflection/trait_reflection.rs`](reflection/trait_reflection.rs) | Allows reflection with trait objects ## Scene -| Example | File | Description | -| ------- | ------------------------------------ | ---------------------------------------------------- | -| `scene` | [`scene/scene.rs`](./scene/scene.rs) | Demonstrates loading from and saving scenes to files | +Example | File | Description +--- | --- | --- +`scene` | [`scene/scene.rs`](./scene/scene.rs) | Demonstrates loading from and saving scenes to files ## Shaders -| Example | File | Description | -| ------------------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | -| `animate_shader` | [`shader/animate_shader.rs`](./shader/animate_shader.rs) | Shows how to animate a shader by accessing a time uniform variable | -| `array_texture` | [`shader/array_texture.rs`](./shader/array_texture.rs) | Illustrates how to create a texture for use with a texture2DArray shader uniform variable | -| `hot_shader_reloading` | [`shader/hot_shader_reloading.rs`](./shader/hot_shader_reloading.rs) | Illustrates how to load shaders such that they can be edited while the example is still running | -| `mesh_custom_attribute` | [`shader/mesh_custom_attribute.rs`](./shader/mesh_custom_attribute.rs) | Illustrates how to add a custom attribute to a mesh and use it in a custom shader | -| `shader_custom_material` | [`shader/shader_custom_material.rs`](./shader/shader_custom_material.rs) | Illustrates creating a custom material and a shader that uses it | -| `shader_defs` | [`shader/shader_defs.rs`](./shader/shader_defs.rs) | Demonstrates creating a custom material that uses "shaders defs" (a tool to selectively toggle parts of a shader) | +Example | File | Description +--- | --- | --- +`animate_shader` | [`shader/animate_shader.rs`](./shader/animate_shader.rs) | Shows how to animate a shader by accessing a time uniform variable +`array_texture` | [`shader/array_texture.rs`](./shader/array_texture.rs) | Illustrates how to create a texture for use with a texture2DArray shader uniform variable +`hot_shader_reloading` | [`shader/hot_shader_reloading.rs`](./shader/hot_shader_reloading.rs) | Illustrates how to load shaders such that they can be edited while the example is still running +`mesh_custom_attribute` | [`shader/mesh_custom_attribute.rs`](./shader/mesh_custom_attribute.rs) | Illustrates how to add a custom attribute to a mesh and use it in a custom shader +`shader_custom_material` | [`shader/shader_custom_material.rs`](./shader/shader_custom_material.rs) | Illustrates creating a custom material and a shader that uses it +`shader_defs` | [`shader/shader_defs.rs`](./shader/shader_defs.rs) | Demonstrates creating a custom material that uses "shaders defs" (a tool to selectively toggle parts of a shader) ## Tests -| Example | File | Description | -| --------------------- | -------------------------------------------------------------------- | ------------------------------------------------------- | -| `how_to_test_systems` | [`../tests/how_to_test_systems.rs`](../tests/how_to_test_systems.rs) | How to test systems with commands, queries or resources | +Example | File | Description +--- | --- | --- +`how_to_test_systems` | [`../tests/how_to_test_systems.rs`](../tests/how_to_test_systems.rs) | How to test systems with commands, queries or resources ## Tools -| Example | File | Description | -| ---------- | ------------------------------------------ | --------------------------------------------------- | -| `bevymark` | [`tools/bevymark.rs`](./tools/bevymark.rs) | A heavy workload to benchmark your system with Bevy | +Example | File | Description +--- | --- | --- +`bevymark` | [`tools/bevymark.rs`](./tools/bevymark.rs) | A heavy workload to benchmark your system with Bevy ## UI (User Interface) -| Example | File | Description | -| ------------------ | ---------------------------------------------------- | -------------------------------------------------------------------------------------- | -| `button` | [`ui/button.rs`](./ui/button.rs) | Illustrates creating and updating a button | -| `font_atlas_debug` | [`ui/font_atlas_debug.rs`](./ui/font_atlas_debug.rs) | Illustrates how FontAtlases are populated (used to optimize text rendering internally) | -| `text` | [`ui/text.rs`](./ui/text.rs) | Illustrates creating and updating text | -| `text_debug` | [`ui/text_debug.rs`](./ui/text_debug.rs) | An example for debugging text layout | -| `ui` | [`ui/ui.rs`](./ui/ui.rs) | Illustrates various features of Bevy UI | +Example | File | Description +--- | --- | --- +`button` | [`ui/button.rs`](./ui/button.rs) | Illustrates creating and updating a button +`font_atlas_debug` | [`ui/font_atlas_debug.rs`](./ui/font_atlas_debug.rs) | Illustrates how FontAtlases are populated (used to optimize text rendering internally) +`text` | [`ui/text.rs`](./ui/text.rs) | Illustrates creating and updating text +`text_debug` | [`ui/text_debug.rs`](./ui/text_debug.rs) | An example for debugging text layout +`ui` | [`ui/ui.rs`](./ui/ui.rs) | Illustrates various features of Bevy UI ## Window -| Example | File | Description | -| ----------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------- | -| `clear_color` | [`window/clear_color.rs`](./window/clear_color.rs) | Creates a solid color window | -| `multiple_windows` | [`window/multiple_windows.rs`](./window/multiple_windows.rs) | Creates two windows and cameras viewing the same mesh | -| `scale_factor_override` | [`window/scale_factor_override.rs`](./window/scale_factor_override.rs) | Illustrates how to customize the default window settings | -| `window_settings` | [`window/window_settings.rs`](./window/window_settings.rs) | Demonstrates customizing default window settings | +Example | File | Description +--- | --- | --- +`clear_color` | [`window/clear_color.rs`](./window/clear_color.rs) | Creates a solid color window +`multiple_windows` | [`window/multiple_windows.rs`](./window/multiple_windows.rs) | Creates two windows and cameras viewing the same mesh +`scale_factor_override` | [`window/scale_factor_override.rs`](./window/scale_factor_override.rs) | Illustrates how to customize the default window settings +`window_settings` | [`window/window_settings.rs`](./window/window_settings.rs) | Demonstrates customizing default window settings # Platform-Specific Examples @@ -285,7 +284,7 @@ Bevy by default targets Android API level 29 in its examples which is the Users of older phones may want to use an older API when testing. -To use a different API, the following fields must be updated in `Cargo.toml`: +To use a different API, the following fields must be updated in Cargo.toml: ```toml [package.metadata.android] @@ -293,9 +292,9 @@ target_sdk_version = >>API<< min_sdk_version = >>API or less<< ``` -| Example | File | Description | -| --------- | -------------------------------------------- | ---------------------------------------- | -| `android` | [`android/android.rs`](./android/android.rs) | The `3d/3d_scene.rs` example for Android | +Example | File | Description +--- | --- | --- +`android` | [`android/android.rs`](./android/android.rs) | The `3d/3d_scene.rs` example for Android ## iOS @@ -323,13 +322,13 @@ specify the simulator device UUID via: DEVICE_ID=${YOUR_DEVICE_ID} make run ``` -If you'd like to see Xcode do stuff, you can run +If you'd like to see xcode do stuff, you can run ```sh open bevy_ios_example.xcodeproj/ ``` -which will open Xcode. You then must push the zoom zoom play button and wait +which will open xcode. You then must push the zoom zoom play button and wait for the magic. The Xcode build GUI will by default build the rust library for both @@ -341,9 +340,9 @@ variable in the "`cargo_ios` target" to be either `x86_64-apple-ios` or Note: if you update this variable in Xcode, it will also change the default used for the `Makefile`. -| Example | File | Description | -| ------- | ------------------------------------ | ------------------------------------ | -| `ios` | [`ios/src/lib.rs`](./ios/src/lib.rs) | The `3d/3d_scene.rs` example for iOS | +Example | File | Description +--- | --- | --- +`ios` | [`ios/src/lib.rs`](./ios/src/lib.rs) | The `3d/3d_scene.rs` example for iOS ## WASM @@ -356,7 +355,7 @@ cargo install wasm-bindgen-cli ### Build & Run -Following is an example for `headless_wasm`. For other examples in the `wasm/` directory, +Following is an example for `headless_wasm`. For other examples in wasm/ directory, change the `headless_wasm` in the following commands **and edit** `examples/wasm/index.html` to point to the correct `.js` file. @@ -365,15 +364,15 @@ cargo build --example headless_wasm --target wasm32-unknown-unknown --no-default wasm-bindgen --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/debug/examples/headless_wasm.wasm ``` -Then serve `examples/wasm` the directory to your browser. i.e. +Then serve `examples/wasm` dir to browser. i.e. ```sh basic-http-server examples/wasm ``` -| Example | File | Description | -| --------------- | -------------------------------------------------- | --------------------------------------------------------------------------------- | -| `hello_wasm` | [`wasm/hello_wasm.rs`](./wasm/hello_wasm.rs) | Runs a minimal example that logs "hello world" to the browser's console | -| `assets_wasm` | [`wasm/assets_wasm.rs`](./wasm/assets_wasm.rs) | Demonstrates how to load assets from wasm | -| `headless_wasm` | [`wasm/headless_wasm.rs`](./wasm/headless_wasm.rs) | Sets up a schedule runner and continually logs a counter to the browser's console | -| `winit_wasm` | [`wasm/winit_wasm.rs`](./wasm/winit_wasm.rs) | Logs user input to the browser's console. Requires the `bevy_winit` features | +Example | File | Description +--- | --- | --- +`hello_wasm` | [`wasm/hello_wasm.rs`](./wasm/hello_wasm.rs) | Runs a minimal example that logs "hello world" to the browser's console +`assets_wasm` | [`wasm/assets_wasm.rs`](./wasm/assets_wasm.rs) | Demonstrates how to load assets from wasm +`headless_wasm` | [`wasm/headless_wasm.rs`](./wasm/headless_wasm.rs) | Sets up a schedule runner and continually logs a counter to the browser's console +`winit_wasm` | [`wasm/winit_wasm.rs`](./wasm/winit_wasm.rs) | Logs user input to the browser's console. Requires the `bevy_winit` features From 2e31afd92faf23a679a9603e2136ae840d9e625b Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 11 May 2021 16:32:36 -0400 Subject: [PATCH 39/39] Add per-entity-events example to README.md --- examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README.md b/examples/README.md index 096afc63e4193..42246f7c8d5a6 100644 --- a/examples/README.md +++ b/examples/README.md @@ -154,6 +154,7 @@ Example | File | Description `fixed_timestep` | [`ecs/fixed_timestep.rs`](./ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick `hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities `parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` +`per_entity_events` | [`ecs/per_entity_events.rs`](./ecs/per_entity_events.rs) | Demonstrates how to store events on individual entities in a channel-like fashion. `query_bundle` | [`ecs/query_bundle.rs`](./ecs/query_bundle.rs) | Shows how to query entities that contain components in a `Bundle` `removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame. `startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up)