2022-05-08 02:57:00 +00:00
|
|
|
//! Loads animations from a skinned glTF, spawns many of them, and plays the
|
|
|
|
//! animation to stress test skinned meshes.
|
|
|
|
|
2022-08-30 19:52:11 +00:00
|
|
|
use std::f32::consts::PI;
|
2023-01-09 19:24:51 +00:00
|
|
|
use std::time::Duration;
|
2022-08-30 19:52:11 +00:00
|
|
|
|
2023-10-09 22:45:02 +00:00
|
|
|
use argh::FromArgs;
|
2022-05-08 02:57:00 +00:00
|
|
|
use bevy::{
|
|
|
|
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
2023-02-05 08:06:32 +00:00
|
|
|
pbr::CascadeShadowConfigBuilder,
|
2022-05-08 02:57:00 +00:00
|
|
|
prelude::*,
|
2023-01-19 00:38:28 +00:00
|
|
|
window::{PresentMode, WindowPlugin},
|
2022-05-08 02:57:00 +00:00
|
|
|
};
|
|
|
|
|
2023-10-09 22:45:02 +00:00
|
|
|
#[derive(FromArgs, Resource)]
|
|
|
|
/// `many_foxes` stress test
|
|
|
|
struct Args {
|
|
|
|
/// wether all foxes run in sync.
|
|
|
|
#[argh(switch)]
|
|
|
|
sync: bool,
|
|
|
|
|
|
|
|
/// total number of foxes.
|
|
|
|
#[argh(option, default = "1000")]
|
|
|
|
count: usize,
|
|
|
|
}
|
|
|
|
|
Make `Resource` trait opt-in, requiring `#[derive(Resource)]` V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.
While ergonomic, this results in several drawbacks:
* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
* Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
*ira: My commits are not as well organized :')*
* I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
* I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.
## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.
## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.
If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.
`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.
Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00
|
|
|
#[derive(Resource)]
|
2022-05-08 02:57:00 +00:00
|
|
|
struct Foxes {
|
|
|
|
count: usize,
|
|
|
|
speed: f32,
|
|
|
|
moving: bool,
|
2023-10-09 22:45:02 +00:00
|
|
|
sync: bool,
|
2022-05-08 02:57:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
2023-10-09 22:45:02 +00:00
|
|
|
let args: Args = argh::from_env();
|
|
|
|
|
2022-05-08 02:57:00 +00:00
|
|
|
App::new()
|
2023-06-21 20:51:03 +00:00
|
|
|
.add_plugins((
|
|
|
|
DefaultPlugins.set(WindowPlugin {
|
|
|
|
primary_window: Some(Window {
|
|
|
|
title: "🦊🦊🦊 Many Foxes! 🦊🦊🦊".into(),
|
|
|
|
present_mode: PresentMode::AutoNoVsync,
|
|
|
|
..default()
|
|
|
|
}),
|
Plugins own their settings. Rework PluginGroup trait. (#6336)
# Objective
Fixes #5884 #2879
Alternative to #2988 #5885 #2886
"Immutable" Plugin settings are currently represented as normal ECS resources, which are read as part of plugin init. This presents a number of problems:
1. If a user inserts the plugin settings resource after the plugin is initialized, it will be silently ignored (and use the defaults instead)
2. Users can modify the plugin settings resource after the plugin has been initialized. This creates a false sense of control over settings that can no longer be changed.
(1) and (2) are especially problematic and confusing for the `WindowDescriptor` resource, but this is a general problem.
## Solution
Immutable Plugin settings now live on each Plugin struct (ex: `WindowPlugin`). PluginGroups have been reworked to support overriding plugin values. This also removes the need for the `add_plugins_with` api, as the `add_plugins` api can use the builder pattern directly. Settings that can be used at runtime continue to be represented as ECS resources.
Plugins are now configured like this:
```rust
app.add_plugin(AssetPlugin {
watch_for_changes: true,
..default()
})
```
PluginGroups are now configured like this:
```rust
app.add_plugins(DefaultPlugins
.set(AssetPlugin {
watch_for_changes: true,
..default()
})
)
```
This is an alternative to #2988, which is similar. But I personally prefer this solution for a couple of reasons:
* ~~#2988 doesn't solve (1)~~ #2988 does solve (1) and will panic in that case. I was wrong!
* This PR directly ties plugin settings to Plugin types in a 1:1 relationship, rather than a loose "setup resource" <-> plugin coupling (where the setup resource is consumed by the first plugin that uses it).
* I'm not a huge fan of overloading the ECS resource concept and implementation for something that has very different use cases and constraints.
## Changelog
- PluginGroups can now be configured directly using the builder pattern. Individual plugin values can be overridden by using `plugin_group.set(SomePlugin {})`, which enables overriding default plugin values.
- `WindowDescriptor` plugin settings have been moved to `WindowPlugin` and `AssetServerSettings` have been moved to `AssetPlugin`
- `app.add_plugins_with` has been replaced by using `add_plugins` with the builder pattern.
## Migration Guide
The `WindowDescriptor` settings have been moved from a resource to `WindowPlugin::window`:
```rust
// Old (Bevy 0.8)
app
.insert_resource(WindowDescriptor {
width: 400.0,
..default()
})
.add_plugins(DefaultPlugins)
// New (Bevy 0.9)
app.add_plugins(DefaultPlugins.set(WindowPlugin {
window: WindowDescriptor {
width: 400.0,
..default()
},
..default()
}))
```
The `AssetServerSettings` resource has been removed in favor of direct `AssetPlugin` configuration:
```rust
// Old (Bevy 0.8)
app
.insert_resource(AssetServerSettings {
watch_for_changes: true,
..default()
})
.add_plugins(DefaultPlugins)
// New (Bevy 0.9)
app.add_plugins(DefaultPlugins.set(AssetPlugin {
watch_for_changes: true,
..default()
}))
```
`add_plugins_with` has been replaced by `add_plugins` in combination with the builder pattern:
```rust
// Old (Bevy 0.8)
app.add_plugins_with(DefaultPlugins, |group| group.disable::<AssetPlugin>());
// New (Bevy 0.9)
app.add_plugins(DefaultPlugins.build().disable::<AssetPlugin>());
```
2022-10-24 21:20:33 +00:00
|
|
|
..default()
|
2023-01-19 00:38:28 +00:00
|
|
|
}),
|
2023-06-21 20:51:03 +00:00
|
|
|
FrameTimeDiagnosticsPlugin,
|
|
|
|
LogDiagnosticsPlugin::default(),
|
|
|
|
))
|
2022-05-08 02:57:00 +00:00
|
|
|
.insert_resource(Foxes {
|
2023-10-09 22:45:02 +00:00
|
|
|
count: args.count,
|
2022-05-08 02:57:00 +00:00
|
|
|
speed: 2.0,
|
|
|
|
moving: true,
|
2023-10-09 22:45:02 +00:00
|
|
|
sync: args.sync,
|
2022-05-08 02:57:00 +00:00
|
|
|
})
|
|
|
|
.insert_resource(AmbientLight {
|
|
|
|
color: Color::WHITE,
|
|
|
|
brightness: 1.0,
|
|
|
|
})
|
2023-03-18 01:45:34 +00:00
|
|
|
.add_systems(Startup, setup)
|
|
|
|
.add_systems(
|
|
|
|
Update,
|
|
|
|
(
|
|
|
|
setup_scene_once_loaded,
|
|
|
|
keyboard_animation_control,
|
|
|
|
update_fox_rings.after(keyboard_animation_control),
|
|
|
|
),
|
|
|
|
)
|
2022-05-08 02:57:00 +00:00
|
|
|
.run();
|
|
|
|
}
|
|
|
|
|
Make `Resource` trait opt-in, requiring `#[derive(Resource)]` V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.
While ergonomic, this results in several drawbacks:
* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
* Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
*ira: My commits are not as well organized :')*
* I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
* I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.
## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.
## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.
If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.
`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.
Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00
|
|
|
#[derive(Resource)]
|
2022-05-08 02:57:00 +00:00
|
|
|
struct Animations(Vec<Handle<AnimationClip>>);
|
|
|
|
|
|
|
|
const RING_SPACING: f32 = 2.0;
|
|
|
|
const FOX_SPACING: f32 = 2.0;
|
|
|
|
|
|
|
|
#[derive(Component, Clone, Copy)]
|
|
|
|
enum RotationDirection {
|
|
|
|
CounterClockwise,
|
|
|
|
Clockwise,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RotationDirection {
|
|
|
|
fn sign(&self) -> f32 {
|
|
|
|
match self {
|
|
|
|
RotationDirection::CounterClockwise => 1.0,
|
|
|
|
RotationDirection::Clockwise => -1.0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
struct Ring {
|
|
|
|
radius: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn setup(
|
|
|
|
mut commands: Commands,
|
|
|
|
asset_server: Res<AssetServer>,
|
|
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
|
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
|
|
foxes: Res<Foxes>,
|
|
|
|
) {
|
2022-07-13 19:13:46 +00:00
|
|
|
warn!(include_str!("warning_string.txt"));
|
|
|
|
|
2022-05-08 02:57:00 +00:00
|
|
|
// Insert a resource with the current scene information
|
|
|
|
commands.insert_resource(Animations(vec![
|
|
|
|
asset_server.load("models/animated/Fox.glb#Animation2"),
|
|
|
|
asset_server.load("models/animated/Fox.glb#Animation1"),
|
|
|
|
asset_server.load("models/animated/Fox.glb#Animation0"),
|
|
|
|
]));
|
|
|
|
|
|
|
|
// Foxes
|
|
|
|
// Concentric rings of foxes, running in opposite directions. The rings are spaced at 2m radius intervals.
|
|
|
|
// The foxes in each ring are spaced at least 2m apart around its circumference.'
|
|
|
|
|
|
|
|
// NOTE: This fox model faces +z
|
|
|
|
let fox_handle = asset_server.load("models/animated/Fox.glb#Scene0");
|
|
|
|
|
|
|
|
let ring_directions = [
|
|
|
|
(
|
2022-08-30 19:52:11 +00:00
|
|
|
Quat::from_rotation_y(PI),
|
2022-05-08 02:57:00 +00:00
|
|
|
RotationDirection::CounterClockwise,
|
|
|
|
),
|
|
|
|
(Quat::IDENTITY, RotationDirection::Clockwise),
|
|
|
|
];
|
|
|
|
|
|
|
|
let mut ring_index = 0;
|
|
|
|
let mut radius = RING_SPACING;
|
|
|
|
let mut foxes_remaining = foxes.count;
|
|
|
|
|
|
|
|
info!("Spawning {} foxes...", foxes.count);
|
|
|
|
|
|
|
|
while foxes_remaining > 0 {
|
|
|
|
let (base_rotation, ring_direction) = ring_directions[ring_index % 2];
|
|
|
|
let ring_parent = commands
|
Spawn now takes a Bundle (#6054)
# Objective
Now that we can consolidate Bundles and Components under a single insert (thanks to #2975 and #6039), almost 100% of world spawns now look like `world.spawn().insert((Some, Tuple, Here))`. Spawning an entity without any components is an extremely uncommon pattern, so it makes sense to give spawn the "first class" ergonomic api. This consolidated api should be made consistent across all spawn apis (such as World and Commands).
## Solution
All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input:
```rust
// before:
commands
.spawn()
.insert((A, B, C));
world
.spawn()
.insert((A, B, C);
// after
commands.spawn((A, B, C));
world.spawn((A, B, C));
```
All existing instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api. A new `spawn_empty` has been added, replacing the old `spawn` api.
By allowing `world.spawn(some_bundle)` to replace `world.spawn().insert(some_bundle)`, this opened the door to removing the initial entity allocation in the "empty" archetype / table done in `spawn()` (and subsequent move to the actual archetype in `.insert(some_bundle)`).
This improves spawn performance by over 10%:
![image](https://user-images.githubusercontent.com/2694663/191627587-4ab2f949-4ccd-4231-80eb-80dd4d9ad6b9.png)
To take this measurement, I added a new `world_spawn` benchmark.
Unfortunately, optimizing `Commands::spawn` is slightly less trivial, as Commands expose the Entity id of spawned entities prior to actually spawning. Doing the optimization would (naively) require assurances that the `spawn(some_bundle)` command is applied before all other commands involving the entity (which would not necessarily be true, if memory serves). Optimizing `Commands::spawn` this way does feel possible, but it will require careful thought (and maybe some additional checks), which deserves its own PR. For now, it has the same performance characteristics of the current `Commands::spawn_bundle` on main.
**Note that 99% of this PR is simple renames and refactors. The only code that needs careful scrutiny is the new `World::spawn()` impl, which is relatively straightforward, but it has some new unsafe code (which re-uses battle tested BundlerSpawner code path).**
---
## Changelog
- All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input
- All instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api
- World and Commands now have `spawn_empty()`, which is equivalent to the old `spawn()` behavior.
## Migration Guide
```rust
// Old (0.8):
commands
.spawn()
.insert_bundle((A, B, C));
// New (0.9)
commands.spawn((A, B, C));
// Old (0.8):
commands.spawn_bundle((A, B, C));
// New (0.9)
commands.spawn((A, B, C));
// Old (0.8):
let entity = commands.spawn().id();
// New (0.9)
let entity = commands.spawn_empty().id();
// Old (0.8)
let entity = world.spawn().id();
// New (0.9)
let entity = world.spawn_empty();
```
2022-09-23 19:55:54 +00:00
|
|
|
.spawn((
|
2022-12-25 00:39:29 +00:00
|
|
|
SpatialBundle::INHERITED_IDENTITY,
|
2022-05-08 02:57:00 +00:00
|
|
|
ring_direction,
|
|
|
|
Ring { radius },
|
|
|
|
))
|
|
|
|
.id();
|
|
|
|
|
2022-08-30 19:52:11 +00:00
|
|
|
let circumference = PI * 2. * radius;
|
2022-05-08 02:57:00 +00:00
|
|
|
let foxes_in_ring = ((circumference / FOX_SPACING) as usize).min(foxes_remaining);
|
|
|
|
let fox_spacing_angle = circumference / (foxes_in_ring as f32 * radius);
|
|
|
|
|
|
|
|
for fox_i in 0..foxes_in_ring {
|
|
|
|
let fox_angle = fox_i as f32 * fox_spacing_angle;
|
|
|
|
let (s, c) = fox_angle.sin_cos();
|
|
|
|
let (x, z) = (radius * c, radius * s);
|
|
|
|
|
|
|
|
commands.entity(ring_parent).with_children(|builder| {
|
Spawn now takes a Bundle (#6054)
# Objective
Now that we can consolidate Bundles and Components under a single insert (thanks to #2975 and #6039), almost 100% of world spawns now look like `world.spawn().insert((Some, Tuple, Here))`. Spawning an entity without any components is an extremely uncommon pattern, so it makes sense to give spawn the "first class" ergonomic api. This consolidated api should be made consistent across all spawn apis (such as World and Commands).
## Solution
All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input:
```rust
// before:
commands
.spawn()
.insert((A, B, C));
world
.spawn()
.insert((A, B, C);
// after
commands.spawn((A, B, C));
world.spawn((A, B, C));
```
All existing instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api. A new `spawn_empty` has been added, replacing the old `spawn` api.
By allowing `world.spawn(some_bundle)` to replace `world.spawn().insert(some_bundle)`, this opened the door to removing the initial entity allocation in the "empty" archetype / table done in `spawn()` (and subsequent move to the actual archetype in `.insert(some_bundle)`).
This improves spawn performance by over 10%:
![image](https://user-images.githubusercontent.com/2694663/191627587-4ab2f949-4ccd-4231-80eb-80dd4d9ad6b9.png)
To take this measurement, I added a new `world_spawn` benchmark.
Unfortunately, optimizing `Commands::spawn` is slightly less trivial, as Commands expose the Entity id of spawned entities prior to actually spawning. Doing the optimization would (naively) require assurances that the `spawn(some_bundle)` command is applied before all other commands involving the entity (which would not necessarily be true, if memory serves). Optimizing `Commands::spawn` this way does feel possible, but it will require careful thought (and maybe some additional checks), which deserves its own PR. For now, it has the same performance characteristics of the current `Commands::spawn_bundle` on main.
**Note that 99% of this PR is simple renames and refactors. The only code that needs careful scrutiny is the new `World::spawn()` impl, which is relatively straightforward, but it has some new unsafe code (which re-uses battle tested BundlerSpawner code path).**
---
## Changelog
- All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input
- All instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api
- World and Commands now have `spawn_empty()`, which is equivalent to the old `spawn()` behavior.
## Migration Guide
```rust
// Old (0.8):
commands
.spawn()
.insert_bundle((A, B, C));
// New (0.9)
commands.spawn((A, B, C));
// Old (0.8):
commands.spawn_bundle((A, B, C));
// New (0.9)
commands.spawn((A, B, C));
// Old (0.8):
let entity = commands.spawn().id();
// New (0.9)
let entity = commands.spawn_empty().id();
// Old (0.8)
let entity = world.spawn().id();
// New (0.9)
let entity = world.spawn_empty();
```
2022-09-23 19:55:54 +00:00
|
|
|
builder.spawn(SceneBundle {
|
2022-06-09 20:34:09 +00:00
|
|
|
scene: fox_handle.clone(),
|
2022-10-28 21:03:01 +00:00
|
|
|
transform: Transform::from_xyz(x, 0.0, z)
|
2022-06-09 20:34:09 +00:00
|
|
|
.with_scale(Vec3::splat(0.01))
|
|
|
|
.with_rotation(base_rotation * Quat::from_rotation_y(-fox_angle)),
|
|
|
|
..default()
|
|
|
|
});
|
2022-05-08 02:57:00 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
foxes_remaining -= foxes_in_ring;
|
|
|
|
radius += RING_SPACING;
|
|
|
|
ring_index += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Camera
|
|
|
|
let zoom = 0.8;
|
|
|
|
let translation = Vec3::new(
|
|
|
|
radius * 1.25 * zoom,
|
|
|
|
radius * 0.5 * zoom,
|
|
|
|
radius * 1.5 * zoom,
|
|
|
|
);
|
Spawn now takes a Bundle (#6054)
# Objective
Now that we can consolidate Bundles and Components under a single insert (thanks to #2975 and #6039), almost 100% of world spawns now look like `world.spawn().insert((Some, Tuple, Here))`. Spawning an entity without any components is an extremely uncommon pattern, so it makes sense to give spawn the "first class" ergonomic api. This consolidated api should be made consistent across all spawn apis (such as World and Commands).
## Solution
All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input:
```rust
// before:
commands
.spawn()
.insert((A, B, C));
world
.spawn()
.insert((A, B, C);
// after
commands.spawn((A, B, C));
world.spawn((A, B, C));
```
All existing instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api. A new `spawn_empty` has been added, replacing the old `spawn` api.
By allowing `world.spawn(some_bundle)` to replace `world.spawn().insert(some_bundle)`, this opened the door to removing the initial entity allocation in the "empty" archetype / table done in `spawn()` (and subsequent move to the actual archetype in `.insert(some_bundle)`).
This improves spawn performance by over 10%:
![image](https://user-images.githubusercontent.com/2694663/191627587-4ab2f949-4ccd-4231-80eb-80dd4d9ad6b9.png)
To take this measurement, I added a new `world_spawn` benchmark.
Unfortunately, optimizing `Commands::spawn` is slightly less trivial, as Commands expose the Entity id of spawned entities prior to actually spawning. Doing the optimization would (naively) require assurances that the `spawn(some_bundle)` command is applied before all other commands involving the entity (which would not necessarily be true, if memory serves). Optimizing `Commands::spawn` this way does feel possible, but it will require careful thought (and maybe some additional checks), which deserves its own PR. For now, it has the same performance characteristics of the current `Commands::spawn_bundle` on main.
**Note that 99% of this PR is simple renames and refactors. The only code that needs careful scrutiny is the new `World::spawn()` impl, which is relatively straightforward, but it has some new unsafe code (which re-uses battle tested BundlerSpawner code path).**
---
## Changelog
- All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input
- All instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api
- World and Commands now have `spawn_empty()`, which is equivalent to the old `spawn()` behavior.
## Migration Guide
```rust
// Old (0.8):
commands
.spawn()
.insert_bundle((A, B, C));
// New (0.9)
commands.spawn((A, B, C));
// Old (0.8):
commands.spawn_bundle((A, B, C));
// New (0.9)
commands.spawn((A, B, C));
// Old (0.8):
let entity = commands.spawn().id();
// New (0.9)
let entity = commands.spawn_empty().id();
// Old (0.8)
let entity = world.spawn().id();
// New (0.9)
let entity = world.spawn_empty();
```
2022-09-23 19:55:54 +00:00
|
|
|
commands.spawn(Camera3dBundle {
|
2022-05-08 02:57:00 +00:00
|
|
|
transform: Transform::from_translation(translation)
|
|
|
|
.looking_at(0.2 * Vec3::new(translation.x, 0.0, translation.z), Vec3::Y),
|
2022-06-07 02:16:47 +00:00
|
|
|
..default()
|
2022-05-08 02:57:00 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Plane
|
Spawn now takes a Bundle (#6054)
# Objective
Now that we can consolidate Bundles and Components under a single insert (thanks to #2975 and #6039), almost 100% of world spawns now look like `world.spawn().insert((Some, Tuple, Here))`. Spawning an entity without any components is an extremely uncommon pattern, so it makes sense to give spawn the "first class" ergonomic api. This consolidated api should be made consistent across all spawn apis (such as World and Commands).
## Solution
All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input:
```rust
// before:
commands
.spawn()
.insert((A, B, C));
world
.spawn()
.insert((A, B, C);
// after
commands.spawn((A, B, C));
world.spawn((A, B, C));
```
All existing instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api. A new `spawn_empty` has been added, replacing the old `spawn` api.
By allowing `world.spawn(some_bundle)` to replace `world.spawn().insert(some_bundle)`, this opened the door to removing the initial entity allocation in the "empty" archetype / table done in `spawn()` (and subsequent move to the actual archetype in `.insert(some_bundle)`).
This improves spawn performance by over 10%:
![image](https://user-images.githubusercontent.com/2694663/191627587-4ab2f949-4ccd-4231-80eb-80dd4d9ad6b9.png)
To take this measurement, I added a new `world_spawn` benchmark.
Unfortunately, optimizing `Commands::spawn` is slightly less trivial, as Commands expose the Entity id of spawned entities prior to actually spawning. Doing the optimization would (naively) require assurances that the `spawn(some_bundle)` command is applied before all other commands involving the entity (which would not necessarily be true, if memory serves). Optimizing `Commands::spawn` this way does feel possible, but it will require careful thought (and maybe some additional checks), which deserves its own PR. For now, it has the same performance characteristics of the current `Commands::spawn_bundle` on main.
**Note that 99% of this PR is simple renames and refactors. The only code that needs careful scrutiny is the new `World::spawn()` impl, which is relatively straightforward, but it has some new unsafe code (which re-uses battle tested BundlerSpawner code path).**
---
## Changelog
- All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input
- All instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api
- World and Commands now have `spawn_empty()`, which is equivalent to the old `spawn()` behavior.
## Migration Guide
```rust
// Old (0.8):
commands
.spawn()
.insert_bundle((A, B, C));
// New (0.9)
commands.spawn((A, B, C));
// Old (0.8):
commands.spawn_bundle((A, B, C));
// New (0.9)
commands.spawn((A, B, C));
// Old (0.8):
let entity = commands.spawn().id();
// New (0.9)
let entity = commands.spawn_empty().id();
// Old (0.8)
let entity = world.spawn().id();
// New (0.9)
let entity = world.spawn_empty();
```
2022-09-23 19:55:54 +00:00
|
|
|
commands.spawn(PbrBundle {
|
2023-02-13 18:20:20 +00:00
|
|
|
mesh: meshes.add(shape::Plane::from_size(5000.0).into()),
|
2022-05-08 02:57:00 +00:00
|
|
|
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
|
|
|
|
..default()
|
|
|
|
});
|
|
|
|
|
|
|
|
// Light
|
Spawn now takes a Bundle (#6054)
# Objective
Now that we can consolidate Bundles and Components under a single insert (thanks to #2975 and #6039), almost 100% of world spawns now look like `world.spawn().insert((Some, Tuple, Here))`. Spawning an entity without any components is an extremely uncommon pattern, so it makes sense to give spawn the "first class" ergonomic api. This consolidated api should be made consistent across all spawn apis (such as World and Commands).
## Solution
All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input:
```rust
// before:
commands
.spawn()
.insert((A, B, C));
world
.spawn()
.insert((A, B, C);
// after
commands.spawn((A, B, C));
world.spawn((A, B, C));
```
All existing instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api. A new `spawn_empty` has been added, replacing the old `spawn` api.
By allowing `world.spawn(some_bundle)` to replace `world.spawn().insert(some_bundle)`, this opened the door to removing the initial entity allocation in the "empty" archetype / table done in `spawn()` (and subsequent move to the actual archetype in `.insert(some_bundle)`).
This improves spawn performance by over 10%:
![image](https://user-images.githubusercontent.com/2694663/191627587-4ab2f949-4ccd-4231-80eb-80dd4d9ad6b9.png)
To take this measurement, I added a new `world_spawn` benchmark.
Unfortunately, optimizing `Commands::spawn` is slightly less trivial, as Commands expose the Entity id of spawned entities prior to actually spawning. Doing the optimization would (naively) require assurances that the `spawn(some_bundle)` command is applied before all other commands involving the entity (which would not necessarily be true, if memory serves). Optimizing `Commands::spawn` this way does feel possible, but it will require careful thought (and maybe some additional checks), which deserves its own PR. For now, it has the same performance characteristics of the current `Commands::spawn_bundle` on main.
**Note that 99% of this PR is simple renames and refactors. The only code that needs careful scrutiny is the new `World::spawn()` impl, which is relatively straightforward, but it has some new unsafe code (which re-uses battle tested BundlerSpawner code path).**
---
## Changelog
- All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input
- All instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api
- World and Commands now have `spawn_empty()`, which is equivalent to the old `spawn()` behavior.
## Migration Guide
```rust
// Old (0.8):
commands
.spawn()
.insert_bundle((A, B, C));
// New (0.9)
commands.spawn((A, B, C));
// Old (0.8):
commands.spawn_bundle((A, B, C));
// New (0.9)
commands.spawn((A, B, C));
// Old (0.8):
let entity = commands.spawn().id();
// New (0.9)
let entity = commands.spawn_empty().id();
// Old (0.8)
let entity = world.spawn().id();
// New (0.9)
let entity = world.spawn_empty();
```
2022-09-23 19:55:54 +00:00
|
|
|
commands.spawn(DirectionalLightBundle {
|
2022-08-30 19:52:11 +00:00
|
|
|
transform: Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
|
2022-05-08 02:57:00 +00:00
|
|
|
directional_light: DirectionalLight {
|
|
|
|
shadows_enabled: true,
|
|
|
|
..default()
|
|
|
|
},
|
2023-02-05 08:06:32 +00:00
|
|
|
cascade_shadow_config: CascadeShadowConfigBuilder {
|
|
|
|
first_cascade_far_bound: 0.9 * radius,
|
|
|
|
maximum_distance: 2.8 * radius,
|
|
|
|
..default()
|
|
|
|
}
|
|
|
|
.into(),
|
2022-05-08 02:57:00 +00:00
|
|
|
..default()
|
|
|
|
});
|
|
|
|
|
|
|
|
println!("Animation controls:");
|
|
|
|
println!(" - spacebar: play / pause");
|
|
|
|
println!(" - arrow up / down: speed up / slow down animation playback");
|
|
|
|
println!(" - arrow left / right: seek backward / forward");
|
|
|
|
println!(" - return: change animation");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Once the scene is loaded, start the animation
|
|
|
|
fn setup_scene_once_loaded(
|
|
|
|
animations: Res<Animations>,
|
|
|
|
foxes: Res<Foxes>,
|
2023-10-09 22:45:02 +00:00
|
|
|
mut player: Query<(Entity, &mut AnimationPlayer)>,
|
2022-05-08 02:57:00 +00:00
|
|
|
mut done: Local<bool>,
|
|
|
|
) {
|
|
|
|
if !*done && player.iter().len() == foxes.count {
|
2023-10-09 22:45:02 +00:00
|
|
|
for (entity, mut player) in &mut player {
|
2022-05-08 02:57:00 +00:00
|
|
|
player.play(animations.0[0].clone_weak()).repeat();
|
2023-10-09 22:45:02 +00:00
|
|
|
if !foxes.sync {
|
|
|
|
player.seek_to(entity.index() as f32 / 10.0);
|
|
|
|
}
|
2022-05-08 02:57:00 +00:00
|
|
|
}
|
|
|
|
*done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_fox_rings(
|
|
|
|
time: Res<Time>,
|
|
|
|
foxes: Res<Foxes>,
|
|
|
|
mut rings: Query<(&Ring, &RotationDirection, &mut Transform)>,
|
|
|
|
) {
|
|
|
|
if !foxes.moving {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let dt = time.delta_seconds();
|
2022-07-11 15:28:50 +00:00
|
|
|
for (ring, rotation_direction, mut transform) in &mut rings {
|
2022-05-08 02:57:00 +00:00
|
|
|
let angular_velocity = foxes.speed / ring.radius;
|
2022-07-01 03:58:54 +00:00
|
|
|
transform.rotate_y(rotation_direction.sign() * angular_velocity * dt);
|
2022-05-08 02:57:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn keyboard_animation_control(
|
|
|
|
keyboard_input: Res<Input<KeyCode>>,
|
|
|
|
mut animation_player: Query<&mut AnimationPlayer>,
|
|
|
|
animations: Res<Animations>,
|
|
|
|
mut current_animation: Local<usize>,
|
|
|
|
mut foxes: ResMut<Foxes>,
|
|
|
|
) {
|
|
|
|
if keyboard_input.just_pressed(KeyCode::Space) {
|
|
|
|
foxes.moving = !foxes.moving;
|
|
|
|
}
|
|
|
|
|
|
|
|
if keyboard_input.just_pressed(KeyCode::Up) {
|
|
|
|
foxes.speed *= 1.25;
|
|
|
|
}
|
|
|
|
|
|
|
|
if keyboard_input.just_pressed(KeyCode::Down) {
|
|
|
|
foxes.speed *= 0.8;
|
|
|
|
}
|
|
|
|
|
2022-06-23 02:00:45 +00:00
|
|
|
if keyboard_input.just_pressed(KeyCode::Return) {
|
|
|
|
*current_animation = (*current_animation + 1) % animations.0.len();
|
|
|
|
}
|
|
|
|
|
2022-07-11 15:28:50 +00:00
|
|
|
for mut player in &mut animation_player {
|
2022-05-08 02:57:00 +00:00
|
|
|
if keyboard_input.just_pressed(KeyCode::Space) {
|
|
|
|
if player.is_paused() {
|
|
|
|
player.resume();
|
|
|
|
} else {
|
|
|
|
player.pause();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if keyboard_input.just_pressed(KeyCode::Up) {
|
|
|
|
let speed = player.speed();
|
|
|
|
player.set_speed(speed * 1.25);
|
|
|
|
}
|
|
|
|
|
|
|
|
if keyboard_input.just_pressed(KeyCode::Down) {
|
|
|
|
let speed = player.speed();
|
|
|
|
player.set_speed(speed * 0.8);
|
|
|
|
}
|
|
|
|
|
|
|
|
if keyboard_input.just_pressed(KeyCode::Left) {
|
API updates to the AnimationPlayer (#9002)
# Objective
Added `AnimationPlayer` API UX improvements.
- Succestor to https://github.com/bevyengine/bevy/pull/5912
- Fixes https://github.com/bevyengine/bevy/issues/5848
_(Credits to @asafigan for filing #5848, creating the initial pull
request, and the discussion in #5912)_
## Solution
- Created `RepeatAnimation` enum to describe an animation repetition
behavior.
- Added `is_finished()`, `set_repeat()`, and `is_playback_reversed()`
methods to the animation player.
- ~~Made the animation clip optional as per the comment from #5912~~
> ~~My problem is that the default handle [used the initialize a
`PlayingAnimation`] could actually refer to an actual animation if an
AnimationClip is set for the default handle, which leads me to ask,
"Should animation_clip should be an Option?"~~
- Added an accessor for the animation clip `animation_clip()` to the
animation player.
To determine if an animation is finished, we use the number of times the
animation has completed and the repetition behavior. If the animation is
playing in reverse then `elapsed < 0.0` counts as a completion.
Otherwise, `elapsed > animation.duration` counts as a completion. This
is what I would expect, personally. If there's any ambiguity, perhaps we
could add some `AnimationCompletionBehavior`, to specify that kind of
completion behavior to use.
Update: Previously `PlayingAnimation::elapsed` was being used as the
seek time into the animation clip. This was misleading because if you
increased the speed of the animation it would also increase (or
decrease) the elapsed time. In other words, the elapsed time was not
actually the elapsed time. To solve this, we introduce
`PlayingAnimation::seek_time` to serve as the value we manipulate the
move between keyframes. Consequently, `elapsed()` now returns the actual
elapsed time, and is not effected by the animation speed. Because
`set_elapsed` was being used to manipulate the displayed keyframe, we
introduce `AnimationPlayer::seek_to` and `AnimationPlayer::replay` to
provide this functionality.
## Migration Guide
- Removed `set_elapsed`.
- Removed `stop_repeating` in favour of
`AnimationPlayer::set_repeat(RepeatAnimation::Never)`.
- Introduced `seek_to` to seek to a given timestamp inside of the
animation.
- Introduced `seek_time` accessor for the `PlayingAnimation::seek_to`.
- Introduced `AnimationPlayer::replay` to reset the `PlayingAnimation`
to a state where no time has elapsed.
---------
Co-authored-by: Hennadii Chernyshchyk <genaloner@gmail.com>
Co-authored-by: François <mockersf@gmail.com>
2023-08-28 16:43:04 +00:00
|
|
|
let elapsed = player.seek_time();
|
|
|
|
player.seek_to(elapsed - 0.1);
|
2022-05-08 02:57:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if keyboard_input.just_pressed(KeyCode::Right) {
|
API updates to the AnimationPlayer (#9002)
# Objective
Added `AnimationPlayer` API UX improvements.
- Succestor to https://github.com/bevyengine/bevy/pull/5912
- Fixes https://github.com/bevyengine/bevy/issues/5848
_(Credits to @asafigan for filing #5848, creating the initial pull
request, and the discussion in #5912)_
## Solution
- Created `RepeatAnimation` enum to describe an animation repetition
behavior.
- Added `is_finished()`, `set_repeat()`, and `is_playback_reversed()`
methods to the animation player.
- ~~Made the animation clip optional as per the comment from #5912~~
> ~~My problem is that the default handle [used the initialize a
`PlayingAnimation`] could actually refer to an actual animation if an
AnimationClip is set for the default handle, which leads me to ask,
"Should animation_clip should be an Option?"~~
- Added an accessor for the animation clip `animation_clip()` to the
animation player.
To determine if an animation is finished, we use the number of times the
animation has completed and the repetition behavior. If the animation is
playing in reverse then `elapsed < 0.0` counts as a completion.
Otherwise, `elapsed > animation.duration` counts as a completion. This
is what I would expect, personally. If there's any ambiguity, perhaps we
could add some `AnimationCompletionBehavior`, to specify that kind of
completion behavior to use.
Update: Previously `PlayingAnimation::elapsed` was being used as the
seek time into the animation clip. This was misleading because if you
increased the speed of the animation it would also increase (or
decrease) the elapsed time. In other words, the elapsed time was not
actually the elapsed time. To solve this, we introduce
`PlayingAnimation::seek_time` to serve as the value we manipulate the
move between keyframes. Consequently, `elapsed()` now returns the actual
elapsed time, and is not effected by the animation speed. Because
`set_elapsed` was being used to manipulate the displayed keyframe, we
introduce `AnimationPlayer::seek_to` and `AnimationPlayer::replay` to
provide this functionality.
## Migration Guide
- Removed `set_elapsed`.
- Removed `stop_repeating` in favour of
`AnimationPlayer::set_repeat(RepeatAnimation::Never)`.
- Introduced `seek_to` to seek to a given timestamp inside of the
animation.
- Introduced `seek_time` accessor for the `PlayingAnimation::seek_to`.
- Introduced `AnimationPlayer::replay` to reset the `PlayingAnimation`
to a state where no time has elapsed.
---------
Co-authored-by: Hennadii Chernyshchyk <genaloner@gmail.com>
Co-authored-by: François <mockersf@gmail.com>
2023-08-28 16:43:04 +00:00
|
|
|
let elapsed = player.seek_time();
|
|
|
|
player.seek_to(elapsed + 0.1);
|
2022-05-08 02:57:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if keyboard_input.just_pressed(KeyCode::Return) {
|
|
|
|
player
|
2023-01-09 19:24:51 +00:00
|
|
|
.play_with_transition(
|
|
|
|
animations.0[*current_animation].clone_weak(),
|
|
|
|
Duration::from_millis(250),
|
|
|
|
)
|
2022-05-08 02:57:00 +00:00
|
|
|
.repeat();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|