From f4a628a1dfde71e155bb7fabd3d7717f51ef8b65 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Fri, 27 Sep 2024 22:38:54 -0700 Subject: [PATCH 01/18] migrate bevy_sprite --- crates/bevy_sprite/src/bundle.rs | 5 + crates/bevy_sprite/src/lib.rs | 16 +- crates/bevy_sprite/src/picking_backend.rs | 174 +++++++++--------- crates/bevy_sprite/src/render/mod.rs | 14 +- crates/bevy_sprite/src/sprite.rs | 40 +++- .../src/texture_slice/computed_slices.rs | 37 +--- 6 files changed, 147 insertions(+), 139 deletions(-) diff --git a/crates/bevy_sprite/src/bundle.rs b/crates/bevy_sprite/src/bundle.rs index ca962c40b1b2c..f0c433de08485 100644 --- a/crates/bevy_sprite/src/bundle.rs +++ b/crates/bevy_sprite/src/bundle.rs @@ -1,3 +1,4 @@ +#![expect(deprecated)] use crate::Sprite; use bevy_asset::Handle; use bevy_ecs::bundle::Bundle; @@ -15,6 +16,10 @@ use bevy_transform::components::{GlobalTransform, Transform}; /// - [`ImageScaleMode`](crate::ImageScaleMode) to enable either slicing or tiling of the texture /// - [`TextureAtlas`](crate::TextureAtlas) to draw a specific section of the texture #[derive(Bundle, Clone, Debug, Default)] +#[deprecated( + since = "0.15.0", + note = "Use the `Sprite` component instead. Inserting it will now also insert `Transform` and `Visibility` automatically." +)] pub struct SpriteBundle { /// Specifies the rendering properties of the sprite, such as color tint and flip. pub sprite: Sprite, diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index a411cef4afc1f..2d798f9234368 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -25,6 +25,7 @@ mod texture_slice; /// The sprite prelude. /// /// This includes the most common types in this crate, re-exported for your convenience. +#[expect(deprecated)] pub mod prelude { #[doc(hidden)] pub use crate::{ @@ -188,9 +189,9 @@ pub fn calculate_bounds_2d( atlases: Res>, meshes_without_aabb: Query<(Entity, &Mesh2dHandle), (Without, Without)>, sprites_to_recalculate_aabb: Query< - (Entity, &Sprite, &Handle, Option<&TextureAtlas>), + (Entity, &Sprite), ( - Or<(Without, Changed, Changed)>, + Or<(Without, Changed)>, Without, ), >, @@ -202,13 +203,13 @@ pub fn calculate_bounds_2d( } } } - for (entity, sprite, texture_handle, atlas) in &sprites_to_recalculate_aabb { + for (entity, sprite) in &sprites_to_recalculate_aabb { if let Some(size) = sprite .custom_size .or_else(|| sprite.rect.map(|rect| rect.size())) - .or_else(|| match atlas { + .or_else(|| match &sprite.atlas { // We default to the texture size for regular sprites - None => images.get(texture_handle).map(Image::size_f32), + None => images.get(&sprite.image).map(Image::size_f32), // We default to the drawn rect for atlas sprites Some(atlas) => atlas .texture_rect(&atlases) @@ -262,10 +263,7 @@ mod test { app.add_systems(Update, calculate_bounds_2d); // Add entities - let entity = app - .world_mut() - .spawn((Sprite::default(), image_handle)) - .id(); + let entity = app.world_mut().spawn(Sprite::from_image(image_handle)).id(); // Verify that the entity does not have an AABB assert!(!app diff --git a/crates/bevy_sprite/src/picking_backend.rs b/crates/bevy_sprite/src/picking_backend.rs index baf06d1a143b4..6e981ffb1d8ad 100644 --- a/crates/bevy_sprite/src/picking_backend.rs +++ b/crates/bevy_sprite/src/picking_backend.rs @@ -29,25 +29,20 @@ pub fn sprite_picking( primary_window: Query>, images: Res>, texture_atlas_layout: Res>, - sprite_query: Query< - ( - Entity, - Option<&Sprite>, - Option<&TextureAtlas>, - Option<&Handle>, - &GlobalTransform, - Option<&Pickable>, - &ViewVisibility, - ), - Or<(With, With)>, - >, + sprite_query: Query<( + Entity, + &Sprite, + &GlobalTransform, + Option<&Pickable>, + &ViewVisibility, + )>, mut output: EventWriter, ) { let mut sorted_sprites: Vec<_> = sprite_query .iter() - .filter(|x| !x.4.affine().is_nan()) + .filter(|x| !x.2.affine().is_nan()) .collect(); - sorted_sprites.sort_by_key(|x| Reverse(FloatOrd(x.4.translation().z))); + sorted_sprites.sort_by_key(|x| Reverse(FloatOrd(x.2.translation().z))); let primary_window = primary_window.get_single().ok(); @@ -80,88 +75,83 @@ pub fn sprite_picking( .iter() .copied() .filter(|(.., visibility)| visibility.get()) - .filter_map( - |(entity, sprite, atlas, image, sprite_transform, pickable, ..)| { - if blocked { - return None; - } - - // Hit box in sprite coordinate system - let (extents, anchor) = if let Some((sprite, atlas)) = sprite.zip(atlas) { - let extents = sprite.custom_size.or_else(|| { + .filter_map(|(entity, sprite, sprite_transform, pickable, ..)| { + if blocked { + return None; + } + + // Hit box in sprite coordinate system + let Some(extents) = sprite + .custom_size + .or_else(|| { + sprite.atlas.as_ref().and_then(|atlas| { texture_atlas_layout .get(&atlas.layout) .map(|f| f.textures[atlas.index].size().as_vec2()) - })?; - let anchor = sprite.anchor.as_vec(); - (extents, anchor) - } else if let Some((sprite, image)) = sprite.zip(image) { - let extents = sprite - .custom_size - .or_else(|| images.get(image).map(|f| f.size().as_vec2()))?; - let anchor = sprite.anchor.as_vec(); - (extents, anchor) - } else { - return None; - }; - - let center = -anchor * extents; - let rect = Rect::from_center_half_size(center, extents / 2.0); - - // Transform cursor line segment to sprite coordinate system - let world_to_sprite = sprite_transform.affine().inverse(); - let cursor_start_sprite = - world_to_sprite.transform_point3(cursor_ray_world.origin); - let cursor_end_sprite = world_to_sprite.transform_point3(cursor_ray_end); - - // Find where the cursor segment intersects the plane Z=0 (which is the sprite's - // plane in sprite-local space). It may not intersect if, for example, we're - // viewing the sprite side-on - if cursor_start_sprite.z == cursor_end_sprite.z { - // Cursor ray is parallel to the sprite and misses it - return None; - } - let lerp_factor = - f32::inverse_lerp(cursor_start_sprite.z, cursor_end_sprite.z, 0.0); - if !(0.0..=1.0).contains(&lerp_factor) { - // Lerp factor is out of range, meaning that while an infinite line cast by - // the cursor would intersect the sprite, the sprite is not between the - // camera's near and far planes - return None; - } - // Otherwise we can interpolate the xy of the start and end positions by the - // lerp factor to get the cursor position in sprite space! - let cursor_pos_sprite = cursor_start_sprite - .lerp(cursor_end_sprite, lerp_factor) - .xy(); - - let is_cursor_in_sprite = rect.contains(cursor_pos_sprite); - - blocked = is_cursor_in_sprite - && pickable.map(|p| p.should_block_lower) != Some(false); - - is_cursor_in_sprite.then(|| { - let hit_pos_world = - sprite_transform.transform_point(cursor_pos_sprite.extend(0.0)); - // Transform point from world to camera space to get the Z distance - let hit_pos_cam = cam_transform - .affine() - .inverse() - .transform_point3(hit_pos_world); - // HitData requires a depth as calculated from the camera's near clipping plane - let depth = -cam_ortho.near - hit_pos_cam.z; - ( - entity, - HitData::new( - cam_entity, - depth, - Some(hit_pos_world), - Some(*sprite_transform.back()), - ), - ) + }) }) - }, - ) + .or_else(|| images.get(&sprite.image).map(|f| f.size().as_vec2())) + else { + return None; + }; + + let anchor = sprite.anchor.as_vec(); + + let center = -anchor * extents; + let rect = Rect::from_center_half_size(center, extents / 2.0); + + // Transform cursor line segment to sprite coordinate system + let world_to_sprite = sprite_transform.affine().inverse(); + let cursor_start_sprite = world_to_sprite.transform_point3(cursor_ray_world.origin); + let cursor_end_sprite = world_to_sprite.transform_point3(cursor_ray_end); + + // Find where the cursor segment intersects the plane Z=0 (which is the sprite's + // plane in sprite-local space). It may not intersect if, for example, we're + // viewing the sprite side-on + if cursor_start_sprite.z == cursor_end_sprite.z { + // Cursor ray is parallel to the sprite and misses it + return None; + } + let lerp_factor = + f32::inverse_lerp(cursor_start_sprite.z, cursor_end_sprite.z, 0.0); + if !(0.0..=1.0).contains(&lerp_factor) { + // Lerp factor is out of range, meaning that while an infinite line cast by + // the cursor would intersect the sprite, the sprite is not between the + // camera's near and far planes + return None; + } + // Otherwise we can interpolate the xy of the start and end positions by the + // lerp factor to get the cursor position in sprite space! + let cursor_pos_sprite = cursor_start_sprite + .lerp(cursor_end_sprite, lerp_factor) + .xy(); + + let is_cursor_in_sprite = rect.contains(cursor_pos_sprite); + + blocked = + is_cursor_in_sprite && pickable.map(|p| p.should_block_lower) != Some(false); + + is_cursor_in_sprite.then(|| { + let hit_pos_world = + sprite_transform.transform_point(cursor_pos_sprite.extend(0.0)); + // Transform point from world to camera space to get the Z distance + let hit_pos_cam = cam_transform + .affine() + .inverse() + .transform_point3(hit_pos_world); + // HitData requires a depth as calculated from the camera's near clipping plane + let depth = -cam_ortho.near - hit_pos_cam.z; + ( + entity, + HitData::new( + cam_entity, + depth, + Some(hit_pos_world), + Some(*sprite_transform.back()), + ), + ) + }) + }) .collect(); let order = camera.order as f32; diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 1efa866ec7e64..26eaafbf7c4a3 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -375,14 +375,12 @@ pub fn extract_sprites( &ViewVisibility, &Sprite, &GlobalTransform, - &Handle, - Option<&TextureAtlas>, Option<&ComputedTextureSlices>, )>, >, ) { extracted_sprites.sprites.clear(); - for (entity, view_visibility, sprite, transform, handle, sheet, slices) in sprite_query.iter() { + for (entity, view_visibility, sprite, transform, slices) in sprite_query.iter() { if !view_visibility.get() { continue; } @@ -390,12 +388,14 @@ pub fn extract_sprites( if let Some(slices) = slices { extracted_sprites.sprites.extend( slices - .extract_sprites(transform, entity, sprite, handle) + .extract_sprites(transform, entity, sprite, &sprite.image) .map(|e| (commands.spawn_empty().id(), e)), ); } else { - let atlas_rect = - sheet.and_then(|s| s.texture_rect(&texture_atlases).map(|r| r.as_rect())); + let atlas_rect = sprite + .atlas + .as_ref() + .and_then(|s| s.texture_rect(&texture_atlases).map(|r| r.as_rect())); let rect = match (atlas_rect, sprite.rect) { (None, None) => None, (None, Some(sprite_rect)) => Some(sprite_rect), @@ -419,7 +419,7 @@ pub fn extract_sprites( custom_size: sprite.custom_size, flip_x: sprite.flip_x, flip_y: sprite.flip_y, - image_handle_id: handle.id(), + image_handle_id: sprite.image.id(), anchor: sprite.anchor.as_vec(), original_entity: None, }, diff --git a/crates/bevy_sprite/src/sprite.rs b/crates/bevy_sprite/src/sprite.rs index 13e37ee56cadd..97e6f6f835988 100644 --- a/crates/bevy_sprite/src/sprite.rs +++ b/crates/bevy_sprite/src/sprite.rs @@ -1,16 +1,22 @@ +use bevy_asset::Handle; use bevy_color::Color; use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_math::{Rect, Vec2}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::{texture::Image, view::Visibility}; +use bevy_transform::components::Transform; -use crate::TextureSlicer; +use crate::{TextureAtlas, TextureSlicer}; -/// Specifies the rendering properties of a sprite. -/// -/// This is commonly used as a component within [`SpriteBundle`](crate::bundle::SpriteBundle). +/// Describes a sprite to be rendered to a 2D camera #[derive(Component, Debug, Default, Clone, Reflect)] +#[require(Transform, Visibility)] #[reflect(Component, Default, Debug)] pub struct Sprite { + /// The image used to render the sprite + pub image: Handle, + /// The (optional) texture atlas used to render the sprite + pub atlas: Option, /// The sprite's color tint pub color: Color, /// Flip the sprite along the `X` axis @@ -38,6 +44,32 @@ impl Sprite { ..Default::default() } } + + /// Create a sprite from an image + pub fn from_image(image: Handle) -> Self { + Self { + image, + ..Default::default() + } + } + + /// Create a sprite from an image, with an associated texture atlas + pub fn from_atlas_image(image: Handle, atlas: TextureAtlas) -> Self { + Self { + image, + atlas: Some(atlas), + ..Default::default() + } + } + + /// Create a sprite from a solid color + pub fn from_color(color: impl Into, size: Vec2) -> Self { + Self { + color: color.into(), + custom_size: Some(size), + ..Default::default() + } + } } /// Controls how the image is altered when scaled. diff --git a/crates/bevy_sprite/src/texture_slice/computed_slices.rs b/crates/bevy_sprite/src/texture_slice/computed_slices.rs index c49987e1bb93a..626e3bc42c233 100644 --- a/crates/bevy_sprite/src/texture_slice/computed_slices.rs +++ b/crates/bevy_sprite/src/texture_slice/computed_slices.rs @@ -139,13 +139,7 @@ pub(crate) fn compute_slices_on_asset_event( mut events: EventReader>, images: Res>, atlas_layouts: Res>, - sprites: Query<( - Entity, - &ImageScaleMode, - &Sprite, - &Handle, - Option<&TextureAtlas>, - )>, + sprites: Query<(Entity, &ImageScaleMode, &Sprite)>, ) { // We store the asset ids of added/modified image assets let added_handles: HashSet<_> = events @@ -159,16 +153,16 @@ pub(crate) fn compute_slices_on_asset_event( return; } // We recompute the sprite slices for sprite entities with a matching asset handle id - for (entity, scale_mode, sprite, image_handle, atlas) in &sprites { - if !added_handles.contains(&image_handle.id()) { + for (entity, scale_mode, sprite) in &sprites { + if !added_handles.contains(&sprite.image.id()) { continue; } if let Some(slices) = compute_sprite_slices( sprite, scale_mode, - image_handle, + &sprite.image, &images, - atlas, + sprite.atlas.as_ref(), &atlas_layouts, ) { commands.entity(entity).insert(slices); @@ -183,28 +177,17 @@ pub(crate) fn compute_slices_on_sprite_change( images: Res>, atlas_layouts: Res>, changed_sprites: Query< - ( - Entity, - &ImageScaleMode, - &Sprite, - &Handle, - Option<&TextureAtlas>, - ), - Or<( - Changed, - Changed>, - Changed, - Changed, - )>, + (Entity, &ImageScaleMode, &Sprite), + Or<(Changed, Changed)>, >, ) { - for (entity, scale_mode, sprite, image_handle, atlas) in &changed_sprites { + for (entity, scale_mode, sprite) in &changed_sprites { if let Some(slices) = compute_sprite_slices( sprite, scale_mode, - image_handle, + &sprite.image, &images, - atlas, + sprite.atlas.as_ref(), &atlas_layouts, ) { commands.entity(entity).insert(slices); From a52c0e811390ebf8d390df1197f4a2cb47b6fb3c Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Fri, 27 Sep 2024 22:39:00 -0700 Subject: [PATCH 02/18] migrate engine bundles --- crates/bevy_ecs/src/bundle.rs | 4 +- .../bevy_sprite/src/texture_atlas_builder.rs | 5 +- examples/2d/bloom_2d.rs | 11 ++-- examples/2d/move_sprite.rs | 7 +-- examples/2d/pixel_grid_snap.rs | 23 ++------ examples/2d/rotation.rs | 33 +++-------- examples/2d/sprite.rs | 7 +-- examples/2d/sprite_animation.rs | 31 +++++----- examples/2d/sprite_flipping.rs | 15 ++--- examples/2d/sprite_sheet.rs | 15 +++-- examples/2d/sprite_slice.rs | 11 ++-- examples/2d/sprite_tile.rs | 5 +- examples/2d/text2d.rs | 26 +++------ examples/2d/texture_atlas.rs | 39 ++++++------- examples/2d/transparency_2d.rs | 27 ++++----- examples/animation/color_animation.rs | 20 ++----- examples/asset/alter_sprite.rs | 17 ++---- examples/asset/asset_settings.rs | 43 +++++++------- examples/asset/custom_asset_reader.rs | 5 +- examples/asset/embedded_asset.rs | 5 +- examples/asset/extra_source.rs | 5 +- examples/audio/spatial_audio_2d.rs | 26 +++------ examples/ecs/hierarchy.rs | 31 +++++----- examples/ecs/parallel_query.rs | 7 +-- examples/ecs/removal_detection.rs | 5 +- examples/games/breakout.rs | 58 +++++++------------ examples/games/contributors.rs | 15 ++--- examples/games/desk_toy.rs | 5 +- .../movement/physics_in_fixed_timestep.rs | 7 +-- examples/picking/sprite_picking.rs | 45 ++++++-------- .../shader/compute_shader_game_of_life.rs | 11 ++-- examples/state/computed_states.rs | 5 +- examples/state/custom_transitions.rs | 5 +- examples/state/states.rs | 5 +- examples/state/sub_states.rs | 5 +- examples/stress_tests/bevymark.rs | 8 +-- .../stress_tests/many_animated_sprites.rs | 19 +++--- examples/stress_tests/many_sprites.rs | 19 +++--- examples/time/virtual_time.rs | 26 ++++----- examples/tools/gamepad_viewer.rs | 38 ++++-------- examples/window/transparent_window.rs | 5 +- tests/window/minimising.rs | 12 ++-- tests/window/resizing.rs | 12 ++-- 43 files changed, 264 insertions(+), 459 deletions(-) diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index bbe114a2fee87..932baed73a380 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -80,8 +80,8 @@ use core::{any::TypeId, ptr::NonNull}; /// Additionally, [Tuples](`tuple`) of bundles are also [`Bundle`] (with up to 15 bundles). /// These bundles contain the items of the 'inner' bundles. /// This is a convenient shorthand which is primarily used when spawning entities. -/// For example, spawning an entity using the bundle `(SpriteBundle {...}, PlayerMarker)` -/// will spawn an entity with components required for a 2d sprite, and the `PlayerMarker` component. +/// For example, spawning an entity using the bundle `(SpatialBundle {...}, PlayerMarker)` +/// will spawn an entity with components required for transform and visibility, and the `PlayerMarker` component. /// /// [`unit`], otherwise known as [`()`](`unit`), is a [`Bundle`] containing no components (since it /// can also be considered as the empty tuple). diff --git a/crates/bevy_sprite/src/texture_atlas_builder.rs b/crates/bevy_sprite/src/texture_atlas_builder.rs index 8aad5216c02aa..db652b8163709 100644 --- a/crates/bevy_sprite/src/texture_atlas_builder.rs +++ b/crates/bevy_sprite/src/texture_atlas_builder.rs @@ -188,10 +188,7 @@ impl<'a> TextureAtlasBuilder<'a> { /// let texture = textures.add(texture); /// let layout = layouts.add(atlas_layout); /// // Spawn your sprite - /// commands.spawn(( - /// SpriteBundle { texture, ..Default::default() }, - /// TextureAtlas::from(layout), - /// )); + /// commands.spawn(Sprite::from_atlas_image(texture, TextureAtlas::from(layout))); /// } /// ``` /// diff --git a/examples/2d/bloom_2d.rs b/examples/2d/bloom_2d.rs index a603ced251bf9..0c967e864dfe3 100644 --- a/examples/2d/bloom_2d.rs +++ b/examples/2d/bloom_2d.rs @@ -36,13 +36,10 @@ fn setup( )); // Sprite - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/bevy_bird_dark.png"), - sprite: Sprite { - color: Color::srgb(5.0, 5.0, 5.0), // 4. Put something bright in a dark environment to see the effect - custom_size: Some(Vec2::splat(160.0)), - ..default() - }, + commands.spawn(Sprite { + image: asset_server.load("branding/bevy_bird_dark.png"), + color: Color::srgb(5.0, 5.0, 5.0), // 4. Put something bright in a dark environment to see the effect + custom_size: Some(Vec2::splat(160.0)), ..default() }); diff --git a/examples/2d/move_sprite.rs b/examples/2d/move_sprite.rs index 6f0efa53cbe76..8247798c90823 100644 --- a/examples/2d/move_sprite.rs +++ b/examples/2d/move_sprite.rs @@ -19,11 +19,8 @@ enum Direction { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); commands.spawn(( - SpriteBundle { - texture: asset_server.load("branding/icon.png"), - transform: Transform::from_xyz(100., 0., 0.), - ..default() - }, + Sprite::from_image(asset_server.load("branding/icon.png")), + Transform::from_xyz(100., 0., 0.), Direction::Up, )); } diff --git a/examples/2d/pixel_grid_snap.rs b/examples/2d/pixel_grid_snap.rs index 95c7079dddea4..de1bf9904d3f5 100644 --- a/examples/2d/pixel_grid_snap.rs +++ b/examples/2d/pixel_grid_snap.rs @@ -53,22 +53,16 @@ struct Rotate; fn setup_sprite(mut commands: Commands, asset_server: Res) { // the sample sprite that will be rendered to the pixel-perfect canvas commands.spawn(( - SpriteBundle { - texture: asset_server.load("pixel/bevy_pixel_dark.png"), - transform: Transform::from_xyz(-40., 20., 2.), - ..default() - }, + Sprite::from_image(asset_server.load("pixel/bevy_pixel_dark.png")), + Transform::from_xyz(-40., 20., 2.), Rotate, PIXEL_PERFECT_LAYERS, )); // the sample sprite that will be rendered to the high-res "outer world" commands.spawn(( - SpriteBundle { - texture: asset_server.load("pixel/bevy_pixel_light.png"), - transform: Transform::from_xyz(-40., -20., 2.), - ..default() - }, + Sprite::from_image(asset_server.load("pixel/bevy_pixel_light.png")), + Transform::from_xyz(-40., -20., 2.), Rotate, HIGH_RES_LAYERS, )); @@ -138,14 +132,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { )); // spawn the canvas - commands.spawn(( - SpriteBundle { - texture: image_handle, - ..default() - }, - Canvas, - HIGH_RES_LAYERS, - )); + commands.spawn((Sprite::from_image(image_handle), Canvas, HIGH_RES_LAYERS)); // the "outer" camera renders whatever is on `HIGH_RES_LAYERS` to the screen. // here, the canvas and one of the sample sprites will be rendered by this camera diff --git a/examples/2d/rotation.rs b/examples/2d/rotation.rs index 43227ed5027be..031b9479fcc3b 100644 --- a/examples/2d/rotation.rs +++ b/examples/2d/rotation.rs @@ -62,10 +62,7 @@ fn setup(mut commands: Commands, asset_server: Res) { // player controlled ship commands.spawn(( - SpriteBundle { - texture: ship_handle, - ..default() - }, + Sprite::from_image(ship_handle), Player { movement_speed: 500.0, // meters per second rotation_speed: f32::to_radians(360.0), // degrees per second @@ -74,39 +71,27 @@ fn setup(mut commands: Commands, asset_server: Res) { // enemy that snaps to face the player spawns on the bottom and left commands.spawn(( - SpriteBundle { - texture: enemy_a_handle.clone(), - transform: Transform::from_xyz(0.0 - horizontal_margin, 0.0, 0.0), - ..default() - }, + Sprite::from_image(enemy_a_handle.clone()), + Transform::from_xyz(0.0 - horizontal_margin, 0.0, 0.0), SnapToPlayer, )); commands.spawn(( - SpriteBundle { - texture: enemy_a_handle, - transform: Transform::from_xyz(0.0, 0.0 - vertical_margin, 0.0), - ..default() - }, + Sprite::from_image(enemy_a_handle), + Transform::from_xyz(0.0, 0.0 - vertical_margin, 0.0), SnapToPlayer, )); // enemy that rotates to face the player enemy spawns on the top and right commands.spawn(( - SpriteBundle { - texture: enemy_b_handle.clone(), - transform: Transform::from_xyz(0.0 + horizontal_margin, 0.0, 0.0), - ..default() - }, + Sprite::from_image(enemy_b_handle.clone()), + Transform::from_xyz(0.0 + horizontal_margin, 0.0, 0.0), RotateToPlayer { rotation_speed: f32::to_radians(45.0), // degrees per second }, )); commands.spawn(( - SpriteBundle { - texture: enemy_b_handle, - transform: Transform::from_xyz(0.0, 0.0 + vertical_margin, 0.0), - ..default() - }, + Sprite::from_image(enemy_b_handle), + Transform::from_xyz(0.0, 0.0 + vertical_margin, 0.0), RotateToPlayer { rotation_speed: f32::to_radians(90.0), // degrees per second }, diff --git a/examples/2d/sprite.rs b/examples/2d/sprite.rs index 5fdf172a5be19..64b42c484ba41 100644 --- a/examples/2d/sprite.rs +++ b/examples/2d/sprite.rs @@ -11,8 +11,7 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/bevy_bird_dark.png"), - ..default() - }); + commands.spawn(Sprite::from_image( + asset_server.load("branding/bevy_bird_dark.png"), + )); } diff --git a/examples/2d/sprite_animation.rs b/examples/2d/sprite_animation.rs index 74d3a2bf43e8f..9c9c6d6f66c01 100644 --- a/examples/2d/sprite_animation.rs +++ b/examples/2d/sprite_animation.rs @@ -104,16 +104,15 @@ fn setup( // create the first (left-hand) sprite commands.spawn(( - SpriteBundle { - transform: Transform::from_scale(Vec3::splat(6.0)) - .with_translation(Vec3::new(-50.0, 0.0, 0.0)), - texture: texture.clone(), + Sprite { + image: texture.clone(), + atlas: Some(TextureAtlas { + layout: texture_atlas_layout.clone(), + index: animation_config_1.first_sprite_index, + }), ..default() }, - TextureAtlas { - layout: texture_atlas_layout.clone(), - index: animation_config_1.first_sprite_index, - }, + Transform::from_scale(Vec3::splat(6.0)).with_translation(Vec3::new(-50.0, 0.0, 0.0)), LeftSprite, animation_config_1, )); @@ -123,15 +122,13 @@ fn setup( // create the second (right-hand) sprite commands.spawn(( - SpriteBundle { - transform: Transform::from_scale(Vec3::splat(6.0)) - .with_translation(Vec3::new(50.0, 0.0, 0.0)), - texture: texture.clone(), - ..default() - }, - TextureAtlas { - layout: texture_atlas_layout.clone(), - index: animation_config_2.first_sprite_index, + Sprite { + image: texture.clone(), + atlas: Some(TextureAtlas { + layout: texture_atlas_layout.clone(), + index: animation_config_2.first_sprite_index, + }), + ..Default::default() }, RightSprite, animation_config_2, diff --git a/examples/2d/sprite_flipping.rs b/examples/2d/sprite_flipping.rs index 33ac914995a99..ca169574a1dff 100644 --- a/examples/2d/sprite_flipping.rs +++ b/examples/2d/sprite_flipping.rs @@ -11,15 +11,12 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/bevy_bird_dark.png"), - sprite: Sprite { - // Flip the logo to the left - flip_x: true, - // And don't flip it upside-down ( the default ) - flip_y: false, - ..default() - }, + commands.spawn(Sprite { + image: asset_server.load("branding/bevy_bird_dark.png"), + // Flip the logo to the left + flip_x: true, + // And don't flip it upside-down ( the default ) + flip_y: false, ..default() }); } diff --git a/examples/2d/sprite_sheet.rs b/examples/2d/sprite_sheet.rs index c1c55914b78f3..b1f1051d9a0b2 100644 --- a/examples/2d/sprite_sheet.rs +++ b/examples/2d/sprite_sheet.rs @@ -48,15 +48,14 @@ fn setup( let animation_indices = AnimationIndices { first: 1, last: 6 }; commands.spawn(Camera2dBundle::default()); commands.spawn(( - SpriteBundle { - transform: Transform::from_scale(Vec3::splat(6.0)), + Sprite::from_atlas_image( texture, - ..default() - }, - TextureAtlas { - layout: texture_atlas_layout, - index: animation_indices.first, - }, + TextureAtlas { + layout: texture_atlas_layout, + index: animation_indices.first, + }, + ), + Transform::from_scale(Vec3::splat(6.0)), animation_indices, AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)), )); diff --git a/examples/2d/sprite_slice.rs b/examples/2d/sprite_slice.rs index 9782710326995..cd4dd312f1c93 100644 --- a/examples/2d/sprite_slice.rs +++ b/examples/2d/sprite_slice.rs @@ -73,15 +73,14 @@ fn spawn_sprites( for (label, text_style, size, scale_mode) in cases { position.x += 0.5 * size.x; - let mut cmd = commands.spawn(SpriteBundle { - transform: Transform::from_translation(position), - texture: texture_handle.clone(), - sprite: Sprite { + let mut cmd = commands.spawn(( + Sprite { + image: texture_handle.clone(), custom_size: Some(size), ..default() }, - ..default() - }); + Transform::from_translation(position), + )); if let Some(scale_mode) = scale_mode { cmd = cmd.insert(scale_mode); } diff --git a/examples/2d/sprite_tile.rs b/examples/2d/sprite_tile.rs index c9b0fe5ecb811..7c2515acd8d47 100644 --- a/examples/2d/sprite_tile.rs +++ b/examples/2d/sprite_tile.rs @@ -27,10 +27,7 @@ fn setup(mut commands: Commands, asset_server: Res) { speed: 50.0, }); commands.spawn(( - SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }, + Sprite::from_image(asset_server.load("branding/icon.png")), ImageScaleMode::Tiled { tile_x: true, tile_y: true, diff --git a/examples/2d/text2d.rs b/examples/2d/text2d.rs index f69abc3bed3c3..ad9ddcd401a48 100644 --- a/examples/2d/text2d.rs +++ b/examples/2d/text2d.rs @@ -79,15 +79,10 @@ fn setup(mut commands: Commands, asset_server: Res) { let box_size = Vec2::new(300.0, 200.0); let box_position = Vec2::new(0.0, -250.0); commands - .spawn(SpriteBundle { - sprite: Sprite { - color: Color::srgb(0.25, 0.25, 0.75), - custom_size: Some(Vec2::new(box_size.x, box_size.y)), - ..default() - }, - transform: Transform::from_translation(box_position.extend(0.0)), - ..default() - }) + .spawn(( + Sprite::from_color(Color::srgb(0.25, 0.25, 0.75), box_size), + Transform::from_translation(box_position.extend(0.0)), + )) .with_children(|builder| { builder.spawn(Text2dBundle { text: Text { @@ -110,15 +105,10 @@ fn setup(mut commands: Commands, asset_server: Res) { let other_box_size = Vec2::new(300.0, 200.0); let other_box_position = Vec2::new(320.0, -250.0); commands - .spawn(SpriteBundle { - sprite: Sprite { - color: Color::srgb(0.20, 0.3, 0.70), - custom_size: Some(Vec2::new(other_box_size.x, other_box_size.y)), - ..default() - }, - transform: Transform::from_translation(other_box_position.extend(0.0)), - ..default() - }) + .spawn(( + Sprite::from_color(Color::srgb(0.20, 0.3, 0.70), other_box_size), + Transform::from_translation(other_box_position.extend(0.0)), + )) .with_children(|builder| { builder.spawn(Text2dBundle { text: Text { diff --git a/examples/2d/texture_atlas.rs b/examples/2d/texture_atlas.rs index 0bb8615f4aa42..6f8efd557559e 100644 --- a/examples/2d/texture_atlas.rs +++ b/examples/2d/texture_atlas.rs @@ -97,26 +97,24 @@ fn setup( // padded textures are to the right, unpadded to the left // draw unpadded texture atlas - commands.spawn(SpriteBundle { - texture: linear_texture.clone(), - transform: Transform { + commands.spawn(( + Sprite::from_image(linear_texture.clone()), + Transform { translation: Vec3::new(-250.0, -130.0, 0.0), scale: Vec3::splat(0.8), ..default() }, - ..default() - }); + )); // draw padded texture atlas - commands.spawn(SpriteBundle { - texture: linear_padded_texture.clone(), - transform: Transform { + commands.spawn(( + Sprite::from_image(linear_padded_texture.clone()), + Transform { translation: Vec3::new(250.0, -130.0, 0.0), scale: Vec3::splat(0.8), ..default() }, - ..default() - }); + )); let font = asset_server.load("fonts/FiraSans-Bold.ttf"); @@ -241,19 +239,18 @@ fn create_sprite_from_atlas( texture: Handle, ) { commands.spawn(( - SpriteBundle { - transform: Transform { - translation: Vec3::new(translation.0, translation.1, translation.2), - scale: Vec3::splat(3.0), - ..default() - }, - texture, + Transform { + translation: Vec3::new(translation.0, translation.1, translation.2), + scale: Vec3::splat(3.0), ..default() }, - TextureAtlas { - layout: atlas_handle, - index: sprite_index, - }, + Sprite::from_atlas_image( + texture, + TextureAtlas { + layout: atlas_handle, + index: sprite_index, + }, + ), )); } diff --git a/examples/2d/transparency_2d.rs b/examples/2d/transparency_2d.rs index 069b73098e688..5ce9da7c9da70 100644 --- a/examples/2d/transparency_2d.rs +++ b/examples/2d/transparency_2d.rs @@ -15,27 +15,22 @@ fn setup(mut commands: Commands, asset_server: Res) { let sprite_handle = asset_server.load("branding/icon.png"); - commands.spawn(SpriteBundle { - texture: sprite_handle.clone(), - ..default() - }); - commands.spawn(SpriteBundle { - sprite: Sprite { + commands.spawn(Sprite::from_image(sprite_handle.clone())); + commands.spawn(( + Sprite { + image: sprite_handle.clone(), // Alpha channel of the color controls transparency. color: Color::srgba(0.0, 0.0, 1.0, 0.7), ..default() }, - texture: sprite_handle.clone(), - transform: Transform::from_xyz(100.0, 0.0, 0.0), - ..default() - }); - commands.spawn(SpriteBundle { - sprite: Sprite { + Transform::from_xyz(100.0, 0.0, 0.0), + )); + commands.spawn(( + Sprite { + image: sprite_handle, color: Color::srgba(0.0, 1.0, 0.0, 0.3), ..default() }, - texture: sprite_handle, - transform: Transform::from_xyz(200.0, 0.0, 0.0), - ..default() - }); + Transform::from_xyz(200.0, 0.0, 0.0), + )); } diff --git a/examples/animation/color_animation.rs b/examples/animation/color_animation.rs index e1b364e39b5e8..eb1f8e06bb055 100644 --- a/examples/animation/color_animation.rs +++ b/examples/animation/color_animation.rs @@ -70,28 +70,16 @@ fn setup(mut commands: Commands) { fn spawn_curve_sprite(commands: &mut Commands, y: f32, points: [T; 4]) { commands.spawn(( - SpriteBundle { - transform: Transform::from_xyz(0., y, 0.), - sprite: Sprite { - custom_size: Some(Vec2::new(75., 75.)), - ..Default::default() - }, - ..Default::default() - }, + Sprite::sized(Vec2::new(75., 75.)), + Transform::from_xyz(0., y, 0.), Curve(CubicBezier::new([points]).to_curve().unwrap()), )); } fn spawn_mixed_sprite(commands: &mut Commands, y: f32, colors: [T; 4]) { commands.spawn(( - SpriteBundle { - transform: Transform::from_xyz(0., y, 0.), - sprite: Sprite { - custom_size: Some(Vec2::new(75., 75.)), - ..Default::default() - }, - ..Default::default() - }, + Transform::from_xyz(0., y, 0.), + Sprite::sized(Vec2::new(75., 75.)), Mixed(colors), )); } diff --git a/examples/asset/alter_sprite.rs b/examples/asset/alter_sprite.rs index 7725ad36eb8f2..e9f02ec01c223 100644 --- a/examples/asset/alter_sprite.rs +++ b/examples/asset/alter_sprite.rs @@ -75,23 +75,16 @@ fn setup(mut commands: Commands, asset_server: Res) { // This marker component ensures we can easily find either of the Birds by using With and // Without query filters. Left, - SpriteBundle { - texture: texture_left, - transform: Transform::from_xyz(-200.0, 0.0, 0.0), - ..default() - }, + Sprite::from_image(texture_left), + Transform::from_xyz(-200.0, 0.0, 0.0), bird_left, )); commands.spawn(( Name::new("Bird Right"), - SpriteBundle { - // In contrast to the above, here we rely on the default `RenderAssetUsages` loader - // setting. - texture: asset_server.load(bird_right.get_texture_path()), - transform: Transform::from_xyz(200.0, 0.0, 0.0), - ..default() - }, + // In contrast to the above, here we rely on the default `RenderAssetUsages` loader setting + Sprite::from_image(asset_server.load(bird_right.get_texture_path())), + Transform::from_xyz(200.0, 0.0, 0.0), bird_right, )); } diff --git a/examples/asset/asset_settings.rs b/examples/asset/asset_settings.rs index d45393b3afa51..5462d3b5eedf4 100644 --- a/examples/asset/asset_settings.rs +++ b/examples/asset/asset_settings.rs @@ -23,15 +23,14 @@ fn setup(mut commands: Commands, asset_server: Res) { // If you are using a very small image and rendering it larger like seen here, the default linear filtering will result in a blurry image. // Useful note: The default sampler specified by the ImagePlugin is *not* the same as the default implementation of sampler. This is why // everything uses linear by default but if you look at the default of sampler, it uses nearest. - commands.spawn(SpriteBundle { - texture: asset_server.load("bevy_pixel_dark.png"), - sprite: Sprite { + commands.spawn(( + Sprite { + image: asset_server.load("bevy_pixel_dark.png"), custom_size: Some(Vec2 { x: 160.0, y: 120.0 }), ..Default::default() }, - transform: Transform::from_xyz(-100.0, 0.0, 0.0), - ..Default::default() - }); + Transform::from_xyz(-100.0, 0.0, 0.0), + )); // When a .meta file is added with the same name as the asset and a '.meta' extension // you can (and must) specify all fields of the asset loader's settings for that @@ -42,15 +41,14 @@ fn setup(mut commands: Commands, asset_server: Res) { // A good reference when filling this out is to check out [ImageLoaderSettings::default()] // and follow to the default implementation of each fields type. // https://docs.rs/bevy/latest/bevy/render/texture/struct.ImageLoaderSettings.html# - commands.spawn(SpriteBundle { - texture: asset_server.load("bevy_pixel_dark_with_meta.png"), - sprite: Sprite { + commands.spawn(( + Sprite { + image: asset_server.load("bevy_pixel_dark_with_meta.png"), custom_size: Some(Vec2 { x: 160.0, y: 120.0 }), ..Default::default() }, - transform: Transform::from_xyz(100.0, 0.0, 0.0), - ..Default::default() - }); + Transform::from_xyz(100.0, 0.0, 0.0), + )); // Another option is to use the AssetServers load_with_settings function. // With this you can specify the same settings upon loading your asset with a @@ -62,20 +60,19 @@ fn setup(mut commands: Commands, asset_server: Res) { // settings changes from any loads after the first of the same asset will be ignored. // This is why this one loads a differently named copy of the asset instead of using // same one as without a .meta file. - commands.spawn(SpriteBundle { - texture: asset_server.load_with_settings( - "bevy_pixel_dark_with_settings.png", - |settings: &mut ImageLoaderSettings| { - settings.sampler = ImageSampler::nearest(); - }, - ), - sprite: Sprite { + commands.spawn(( + Sprite { + image: asset_server.load_with_settings( + "bevy_pixel_dark_with_settings.png", + |settings: &mut ImageLoaderSettings| { + settings.sampler = ImageSampler::nearest(); + }, + ), custom_size: Some(Vec2 { x: 160.0, y: 120.0 }), ..Default::default() }, - transform: Transform::from_xyz(0.0, 150.0, 0.0), - ..Default::default() - }); + Transform::from_xyz(0.0, 150.0, 0.0), + )); commands.spawn(Camera2dBundle::default()); } diff --git a/examples/asset/custom_asset_reader.rs b/examples/asset/custom_asset_reader.rs index 7ee270c2c8f32..05073c6639f83 100644 --- a/examples/asset/custom_asset_reader.rs +++ b/examples/asset/custom_asset_reader.rs @@ -61,8 +61,5 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); } diff --git a/examples/asset/embedded_asset.rs b/examples/asset/embedded_asset.rs index b60ec636b3750..3dd0134f228ca 100644 --- a/examples/asset/embedded_asset.rs +++ b/examples/asset/embedded_asset.rs @@ -48,8 +48,5 @@ fn setup(mut commands: Commands, asset_server: Res) { "embedded://embedded_asset/files/bevy_pixel_light.png".into() ); - commands.spawn(SpriteBundle { - texture: asset_server.load(asset_path), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load(asset_path))); } diff --git a/examples/asset/extra_source.rs b/examples/asset/extra_source.rs index 4e0463e4b1c7d..b8f339a90a5e0 100644 --- a/examples/asset/extra_source.rs +++ b/examples/asset/extra_source.rs @@ -42,8 +42,5 @@ fn setup(mut commands: Commands, asset_server: Res) { // path. assert_eq!(asset_path, "example_files://bevy_pixel_light.png".into()); - commands.spawn(SpriteBundle { - texture: asset_server.load(asset_path), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load(asset_path))); } diff --git a/examples/audio/spatial_audio_2d.rs b/examples/audio/spatial_audio_2d.rs index 72c0b77daa328..6c4f302921338 100644 --- a/examples/audio/spatial_audio_2d.rs +++ b/examples/audio/spatial_audio_2d.rs @@ -52,26 +52,16 @@ fn setup( .spawn((SpatialBundle::default(), listener.clone())) .with_children(|parent| { // left ear - parent.spawn(SpriteBundle { - sprite: Sprite { - color: RED.into(), - custom_size: Some(Vec2::splat(20.0)), - ..default() - }, - transform: Transform::from_xyz(-gap / 2.0, 0.0, 0.0), - ..default() - }); + parent.spawn(( + Sprite::from_color(RED, Vec2::splat(20.0)), + Transform::from_xyz(-gap / 2.0, 0.0, 0.0), + )); // right ear - parent.spawn(SpriteBundle { - sprite: Sprite { - color: LIME.into(), - custom_size: Some(Vec2::splat(20.0)), - ..default() - }, - transform: Transform::from_xyz(gap / 2.0, 0.0, 0.0), - ..default() - }); + parent.spawn(( + Sprite::from_color(LIME, Vec2::splat(20.0)), + Transform::from_xyz(gap / 2.0, 0.0, 0.0), + )); }); // example instructions diff --git a/examples/ecs/hierarchy.rs b/examples/ecs/hierarchy.rs index e3356d28e36f5..b95945e15b43f 100644 --- a/examples/ecs/hierarchy.rs +++ b/examples/ecs/hierarchy.rs @@ -18,23 +18,21 @@ fn setup(mut commands: Commands, asset_server: Res) { // Spawn a root entity with no parent let parent = commands - .spawn(SpriteBundle { - transform: Transform::from_scale(Vec3::splat(0.75)), - texture: texture.clone(), - ..default() - }) + .spawn(( + Sprite::from_image(texture.clone()), + Transform::from_scale(Vec3::splat(0.75)), + )) // With that entity as a parent, run a lambda that spawns its children .with_children(|parent| { // parent is a ChildBuilder, which has a similar API to Commands - parent.spawn(SpriteBundle { - transform: Transform::from_xyz(250.0, 0.0, 0.0).with_scale(Vec3::splat(0.75)), - texture: texture.clone(), - sprite: Sprite { + parent.spawn(( + Transform::from_xyz(250.0, 0.0, 0.0).with_scale(Vec3::splat(0.75)), + Sprite { + image: texture.clone(), color: BLUE.into(), ..default() }, - ..default() - }); + )); }) // Store parent entity for next sections .id(); @@ -42,15 +40,14 @@ fn setup(mut commands: Commands, asset_server: Res) { // Another way is to use the add_child function to add children after the parent // entity has already been spawned. let child = commands - .spawn(SpriteBundle { - transform: Transform::from_xyz(0.0, 250.0, 0.0).with_scale(Vec3::splat(0.75)), - texture, - sprite: Sprite { + .spawn(( + Sprite { + image: texture, color: LIME.into(), ..default() }, - ..default() - }) + Transform::from_xyz(0.0, 250.0, 0.0).with_scale(Vec3::splat(0.75)), + )) .id(); // Add child to the parent. diff --git a/examples/ecs/parallel_query.rs b/examples/ecs/parallel_query.rs index adf7f243da83c..0ab683582c462 100644 --- a/examples/ecs/parallel_query.rs +++ b/examples/ecs/parallel_query.rs @@ -16,11 +16,8 @@ fn spawn_system(mut commands: Commands, asset_server: Res) { let mut rng = ChaCha8Rng::seed_from_u64(19878367467713); for _ in 0..128 { commands.spawn(( - SpriteBundle { - texture: texture.clone(), - transform: Transform::from_scale(Vec3::splat(0.1)), - ..default() - }, + Sprite::from_image(texture.clone()), + Transform::from_scale(Vec3::splat(0.1)), Velocity(20.0 * Vec2::new(rng.gen::() - 0.5, rng.gen::() - 0.5)), )); } diff --git a/examples/ecs/removal_detection.rs b/examples/ecs/removal_detection.rs index 462c5d6cbd98f..a1085866546ed 100644 --- a/examples/ecs/removal_detection.rs +++ b/examples/ecs/removal_detection.rs @@ -29,10 +29,7 @@ struct MyComponent; fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); commands.spawn(( - SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }, + Sprite::from_image(asset_server.load("branding/icon.png")), // Add the `Component`. MyComponent, )); diff --git a/examples/games/breakout.rs b/examples/games/breakout.rs index f6f690b1e4c9f..ff4bde4de1527 100644 --- a/examples/games/breakout.rs +++ b/examples/games/breakout.rs @@ -107,7 +107,8 @@ struct CollisionSound(Handle); struct WallBundle { // You can nest bundles inside of other bundles like this // Allowing you to compose their functionality - sprite_bundle: SpriteBundle, + sprite: Sprite, + transform: Transform, collider: Collider, } @@ -154,21 +155,15 @@ impl WallBundle { // making our code easier to read and less prone to bugs when we change the logic fn new(location: WallLocation) -> WallBundle { WallBundle { - sprite_bundle: SpriteBundle { - transform: Transform { - // We need to convert our Vec2 into a Vec3, by giving it a z-coordinate - // This is used to determine the order of our sprites - translation: location.position().extend(0.0), - // The z-scale of 2D objects must always be 1.0, - // or their ordering will be affected in surprising ways. - // See https://github.com/bevyengine/bevy/issues/4149 - scale: location.size().extend(1.0), - ..default() - }, - sprite: Sprite { - color: WALL_COLOR, - ..default() - }, + sprite: Sprite::from_color(WALL_COLOR, Vec2::ONE), + transform: Transform { + // We need to convert our Vec2 into a Vec3, by giving it a z-coordinate + // This is used to determine the order of our sprites + translation: location.position().extend(0.0), + // The z-scale of 2D objects must always be 1.0, + // or their ordering will be affected in surprising ways. + // See https://github.com/bevyengine/bevy/issues/4149 + scale: location.size().extend(1.0), ..default() }, collider: Collider, @@ -201,16 +196,10 @@ fn setup( let paddle_y = BOTTOM_WALL + GAP_BETWEEN_PADDLE_AND_FLOOR; commands.spawn(( - SpriteBundle { - transform: Transform { - translation: Vec3::new(0.0, paddle_y, 0.0), - scale: PADDLE_SIZE.extend(1.0), - ..default() - }, - sprite: Sprite { - color: PADDLE_COLOR, - ..default() - }, + Sprite::from_color(PADDLE_COLOR, Vec2::ONE), + Transform { + translation: Vec3::new(0.0, paddle_y, 0.0), + scale: PADDLE_SIZE.extend(1.0), ..default() }, Paddle, @@ -298,16 +287,13 @@ fn setup( // brick commands.spawn(( - SpriteBundle { - sprite: Sprite { - color: BRICK_COLOR, - ..default() - }, - transform: Transform { - translation: brick_position.extend(0.0), - scale: Vec3::new(BRICK_SIZE.x, BRICK_SIZE.y, 1.0), - ..default() - }, + Sprite { + color: BRICK_COLOR, + ..default() + }, + Transform { + translation: brick_position.extend(0.0), + scale: Vec3::new(BRICK_SIZE.x, BRICK_SIZE.y, 1.0), ..default() }, Brick, diff --git a/examples/games/contributors.rs b/examples/games/contributors.rs index 067f0bdf58b20..cceaad9489e12 100644 --- a/examples/games/contributors.rs +++ b/examples/games/contributors.rs @@ -106,17 +106,14 @@ fn setup_contributor_selection(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); commands.spawn(( Name::new("Player"), - SpriteBundle { - texture: asset_server.load("branding/icon.png"), - transform: Transform::from_scale(Vec3::splat(0.3)), - ..default() - }, + Sprite::from_image(asset_server.load("branding/icon.png")), + Transform::from_scale(Vec3::splat(0.3)), AccumulatedInput::default(), Velocity::default(), PhysicalTranslation::default(), diff --git a/examples/picking/sprite_picking.rs b/examples/picking/sprite_picking.rs index 67abff50b5cf9..8ee5ea0fde159 100644 --- a/examples/picking/sprite_picking.rs +++ b/examples/picking/sprite_picking.rs @@ -32,7 +32,7 @@ fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); let len = 128.0; - let sprite_size = Some(Vec2::splat(len / 2.0)); + let sprite_size = Vec2::splat(len / 2.0); commands .spawn(SpatialBundle::default()) @@ -57,35 +57,29 @@ fn setup(mut commands: Commands, asset_server: Res) { // spawn black square behind sprite to show anchor point commands - .spawn(SpriteBundle { - sprite: Sprite { - custom_size: sprite_size, - color: Color::BLACK, - ..default() - }, - transform: Transform::from_xyz(i * len - len, j * len - len, -1.0), - ..default() - }) + .spawn(( + Sprite::from_color(Color::BLACK, sprite_size), + Transform::from_xyz(i * len - len, j * len - len, -1.0), + )) .observe(recolor_on::>(Color::srgb(0.0, 1.0, 1.0))) .observe(recolor_on::>(Color::BLACK)) .observe(recolor_on::>(Color::srgb(1.0, 1.0, 0.0))) .observe(recolor_on::>(Color::srgb(0.0, 1.0, 1.0))); commands - .spawn(SpriteBundle { - sprite: Sprite { - custom_size: sprite_size, + .spawn(( + Sprite { + image: asset_server.load("branding/bevy_bird_dark.png"), + custom_size: Some(sprite_size), color: Color::srgb(1.0, 0.0, 0.0), anchor: anchor.to_owned(), ..default() }, - texture: asset_server.load("branding/bevy_bird_dark.png"), // 3x3 grid of anchor examples by changing transform - transform: Transform::from_xyz(i * len - len, j * len - len, 0.0) + Transform::from_xyz(i * len - len, j * len - len, 0.0) .with_scale(Vec3::splat(1.0 + (i - 1.0) * 0.2)) .with_rotation(Quat::from_rotation_z((j - 1.0) * 0.2)), - ..default() - }) + )) .observe(recolor_on::>(Color::srgb(0.0, 1.0, 0.0))) .observe(recolor_on::>(Color::srgb(1.0, 0.0, 0.0))) .observe(recolor_on::>(Color::srgb(0.0, 0.0, 1.0))) @@ -131,15 +125,14 @@ fn setup_atlas( let animation_indices = AnimationIndices { first: 1, last: 6 }; commands .spawn(( - TextureAtlas { - layout: texture_atlas_layout_handle, - index: animation_indices.first, - }, - SpriteBundle { - texture: texture_handle, - transform: Transform::from_xyz(300.0, 0.0, 0.0).with_scale(Vec3::splat(6.0)), - ..default() - }, + Sprite::from_atlas_image( + texture_handle, + TextureAtlas { + layout: texture_atlas_layout_handle, + index: animation_indices.first, + }, + ), + Transform::from_xyz(300.0, 0.0, 0.0).with_scale(Vec3::splat(6.0)), animation_indices, AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)), )) diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index 09c072d1a5c86..7f4381376a694 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -67,15 +67,14 @@ fn setup(mut commands: Commands, mut images: ResMut>) { let image0 = images.add(image.clone()); let image1 = images.add(image); - commands.spawn(SpriteBundle { - sprite: Sprite { + commands.spawn(( + Sprite { + image: image0.clone(), custom_size: Some(Vec2::new(SIZE.0 as f32, SIZE.1 as f32)), ..default() }, - texture: image0.clone(), - transform: Transform::from_scale(Vec3::splat(DISPLAY_FACTOR as f32)), - ..default() - }); + Transform::from_scale(Vec3::splat(DISPLAY_FACTOR as f32)), + )); commands.spawn(Camera2dBundle::default()); commands.insert_resource(GameOfLifeImages { diff --git a/examples/state/computed_states.rs b/examples/state/computed_states.rs index 47dd555ab2736..167e2e88d4607 100644 --- a/examples/state/computed_states.rs +++ b/examples/state/computed_states.rs @@ -423,10 +423,7 @@ mod ui { pub fn setup_game(mut commands: Commands, asset_server: Res) { commands.spawn(( StateScoped(InGame), - SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }, + Sprite::from_image(asset_server.load("branding/icon.png")), )); } diff --git a/examples/state/custom_transitions.rs b/examples/state/custom_transitions.rs index 9baa0a76c6155..4cab6c8968593 100644 --- a/examples/state/custom_transitions.rs +++ b/examples/state/custom_transitions.rs @@ -223,10 +223,7 @@ fn setup(mut commands: Commands) { } fn setup_game(mut commands: Commands, asset_server: Res) { - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); info!("Setup game"); } diff --git a/examples/state/states.rs b/examples/state/states.rs index f5a19ea4578fb..d6e13dfc8d756 100644 --- a/examples/state/states.rs +++ b/examples/state/states.rs @@ -120,10 +120,7 @@ fn cleanup_menu(mut commands: Commands, menu_data: Res) { } fn setup_game(mut commands: Commands, asset_server: Res) { - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); } const SPEED: f32 = 100.0; diff --git a/examples/state/sub_states.rs b/examples/state/sub_states.rs index 849bae29214a1..00b8783a90ffe 100644 --- a/examples/state/sub_states.rs +++ b/examples/state/sub_states.rs @@ -198,10 +198,7 @@ mod ui { } pub fn setup_game(mut commands: Commands, asset_server: Res) { - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); } pub fn setup_paused_screen(mut commands: Commands) { diff --git a/examples/stress_tests/bevymark.rs b/examples/stress_tests/bevymark.rs index e0959cc930eb1..6a6a7d5b72522 100644 --- a/examples/stress_tests/bevymark.rs +++ b/examples/stress_tests/bevymark.rs @@ -433,16 +433,16 @@ fn spawn_birds( color }; ( - SpriteBundle { - texture: bird_resources + Sprite { + image: bird_resources .textures .choose(&mut bird_resources.material_rng) .unwrap() .clone(), - transform, - sprite: Sprite { color, ..default() }, + color, ..default() }, + transform, Bird { velocity }, ) }) diff --git a/examples/stress_tests/many_animated_sprites.rs b/examples/stress_tests/many_animated_sprites.rs index 17be46f0ac572..71f8c646e110d 100644 --- a/examples/stress_tests/many_animated_sprites.rs +++ b/examples/stress_tests/many_animated_sprites.rs @@ -82,19 +82,16 @@ fn setup( timer.set_elapsed(Duration::from_secs_f32(rng.gen::())); commands.spawn(( - SpriteBundle { - texture: texture_handle.clone(), - transform: Transform { - translation, - rotation, - scale, - }, - sprite: Sprite { - custom_size: Some(tile_size), - ..default() - }, + Sprite { + image: texture_handle.clone(), + custom_size: Some(tile_size), ..default() }, + Transform { + translation, + rotation, + scale, + }, TextureAtlas::from(texture_atlas_handle.clone()), AnimationTimer(timer), )); diff --git a/examples/stress_tests/many_sprites.rs b/examples/stress_tests/many_sprites.rs index fb08d2942f4b1..daef33f661262 100644 --- a/examples/stress_tests/many_sprites.rs +++ b/examples/stress_tests/many_sprites.rs @@ -81,14 +81,9 @@ fn setup(mut commands: Commands, assets: Res, color_tint: Res()); let scale = Vec3::splat(rng.gen::() * 2.0); - sprites.push(SpriteBundle { - texture: sprite_handle.clone(), - transform: Transform { - translation, - rotation, - scale, - }, - sprite: Sprite { + sprites.push(( + Sprite { + image: sprite_handle.clone(), custom_size: Some(tile_size), color: if color_tint.0 { COLORS[rng.gen_range(0..3)] @@ -97,8 +92,12 @@ fn setup(mut commands: Commands, assets: Res, color_tint: Res, mut time: ResMu // the sprite moving based on real time commands.spawn(( - SpriteBundle { - texture: texture_handle.clone(), - transform: Transform::from_scale(sprite_scale), - ..default() - }, + Sprite::from_image(texture_handle.clone()), + Transform::from_scale(sprite_scale), RealTime, )); // the sprite moving based on virtual time commands.spawn(( - SpriteBundle { - texture: texture_handle, - sprite: Sprite { - color: virtual_color, - ..default() - }, - transform: Transform { - scale: sprite_scale, - translation: Vec3::new(0., -160., 0.), - ..default() - }, + Sprite { + image: texture_handle, + color: virtual_color, + ..Default::default() + }, + Transform { + scale: sprite_scale, + translation: Vec3::new(0., -160., 0.), ..default() }, VirtualTime, diff --git a/examples/tools/gamepad_viewer.rs b/examples/tools/gamepad_viewer.rs index 7be76fe50a6ad..dad7aa01f9044 100644 --- a/examples/tools/gamepad_viewer.rs +++ b/examples/tools/gamepad_viewer.rs @@ -281,34 +281,20 @@ fn setup_sticks( }) .with_children(|parent| { // full extent - parent.spawn(SpriteBundle { - sprite: Sprite { - custom_size: Some(Vec2::splat(STICK_BOUNDS_SIZE * 2.)), - color: DEAD_COLOR, - ..default() - }, - ..default() - }); + parent.spawn(Sprite::from_color( + DEAD_COLOR, + Vec2::splat(STICK_BOUNDS_SIZE * 2.), + )); // live zone - parent.spawn(SpriteBundle { - transform: Transform::from_xyz(live_mid, live_mid, 2.), - sprite: Sprite { - custom_size: Some(Vec2::new(live_size, live_size)), - color: LIVE_COLOR, - ..default() - }, - ..default() - }); + parent.spawn(( + Sprite::from_color(LIVE_COLOR, Vec2::splat(live_size)), + Transform::from_xyz(live_mid, live_mid, 2.), + )); // dead zone - parent.spawn(SpriteBundle { - transform: Transform::from_xyz(dead_mid, dead_mid, 3.), - sprite: Sprite { - custom_size: Some(Vec2::new(dead_size, dead_size)), - color: DEAD_COLOR, - ..default() - }, - ..default() - }); + parent.spawn(( + Sprite::from_color(DEAD_COLOR, Vec2::splat(dead_size)), + Transform::from_xyz(dead_mid, dead_mid, 3.), + )); // text let style = TextStyle { font_size: 13., diff --git a/examples/window/transparent_window.rs b/examples/window/transparent_window.rs index f95408ff52fe0..236b8a147f399 100644 --- a/examples/window/transparent_window.rs +++ b/examples/window/transparent_window.rs @@ -30,8 +30,5 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); } diff --git a/tests/window/minimising.rs b/tests/window/minimising.rs index 12d0826573eed..3a637896f1ad8 100644 --- a/tests/window/minimising.rs +++ b/tests/window/minimising.rs @@ -74,12 +74,8 @@ fn setup_2d(mut commands: Commands) { }, ..default() }); - commands.spawn(SpriteBundle { - sprite: Sprite { - color: Color::srgb(0.25, 0.25, 0.75), - custom_size: Some(Vec2::new(50.0, 50.0)), - ..default() - }, - ..default() - }); + commands.spawn(Sprite::from_color( + Color::srgb(0.25, 0.25, 0.75), + Vec2::new(50.0, 50.0), + )); } diff --git a/tests/window/resizing.rs b/tests/window/resizing.rs index 3cb6d4913a294..2d6254e3a4aa8 100644 --- a/tests/window/resizing.rs +++ b/tests/window/resizing.rs @@ -150,12 +150,8 @@ fn setup_2d(mut commands: Commands) { }, ..default() }); - commands.spawn(SpriteBundle { - sprite: Sprite { - color: Color::srgb(0.25, 0.25, 0.75), - custom_size: Some(Vec2::new(50.0, 50.0)), - ..default() - }, - ..default() - }); + commands.spawn(Sprite::from_color( + Color::srgb(0.25, 0.25, 0.75), + Vec2::new(50.0, 50.0), + )); } From 2cbeab7aa923b9621baea4f292128f1113d6b3b9 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Mon, 30 Sep 2024 13:43:35 -0700 Subject: [PATCH 03/18] Revert "migrate engine bundles" This reverts commit a52c0e811390ebf8d390df1197f4a2cb47b6fb3c. --- crates/bevy_ecs/src/bundle.rs | 4 +- .../bevy_sprite/src/texture_atlas_builder.rs | 5 +- examples/2d/bloom_2d.rs | 11 ++-- examples/2d/move_sprite.rs | 7 ++- examples/2d/pixel_grid_snap.rs | 23 ++++++-- examples/2d/rotation.rs | 33 ++++++++--- examples/2d/sprite.rs | 7 ++- examples/2d/sprite_animation.rs | 31 +++++----- examples/2d/sprite_flipping.rs | 15 +++-- examples/2d/sprite_sheet.rs | 15 ++--- examples/2d/sprite_slice.rs | 11 ++-- examples/2d/sprite_tile.rs | 5 +- examples/2d/text2d.rs | 26 ++++++--- examples/2d/texture_atlas.rs | 39 +++++++------ examples/2d/transparency_2d.rs | 27 +++++---- examples/animation/color_animation.rs | 20 +++++-- examples/asset/alter_sprite.rs | 17 ++++-- examples/asset/asset_settings.rs | 43 +++++++------- examples/asset/custom_asset_reader.rs | 5 +- examples/asset/embedded_asset.rs | 5 +- examples/asset/extra_source.rs | 5 +- examples/audio/spatial_audio_2d.rs | 26 ++++++--- examples/ecs/hierarchy.rs | 31 +++++----- examples/ecs/parallel_query.rs | 7 ++- examples/ecs/removal_detection.rs | 5 +- examples/games/breakout.rs | 58 ++++++++++++------- examples/games/contributors.rs | 15 +++-- examples/games/desk_toy.rs | 5 +- .../movement/physics_in_fixed_timestep.rs | 7 ++- examples/picking/sprite_picking.rs | 45 ++++++++------ .../shader/compute_shader_game_of_life.rs | 11 ++-- examples/state/computed_states.rs | 5 +- examples/state/custom_transitions.rs | 5 +- examples/state/states.rs | 5 +- examples/state/sub_states.rs | 5 +- examples/stress_tests/bevymark.rs | 8 +-- .../stress_tests/many_animated_sprites.rs | 19 +++--- examples/stress_tests/many_sprites.rs | 19 +++--- examples/time/virtual_time.rs | 26 +++++---- examples/tools/gamepad_viewer.rs | 38 ++++++++---- examples/window/transparent_window.rs | 5 +- tests/window/minimising.rs | 12 ++-- tests/window/resizing.rs | 12 ++-- 43 files changed, 459 insertions(+), 264 deletions(-) diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 932baed73a380..bbe114a2fee87 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -80,8 +80,8 @@ use core::{any::TypeId, ptr::NonNull}; /// Additionally, [Tuples](`tuple`) of bundles are also [`Bundle`] (with up to 15 bundles). /// These bundles contain the items of the 'inner' bundles. /// This is a convenient shorthand which is primarily used when spawning entities. -/// For example, spawning an entity using the bundle `(SpatialBundle {...}, PlayerMarker)` -/// will spawn an entity with components required for transform and visibility, and the `PlayerMarker` component. +/// For example, spawning an entity using the bundle `(SpriteBundle {...}, PlayerMarker)` +/// will spawn an entity with components required for a 2d sprite, and the `PlayerMarker` component. /// /// [`unit`], otherwise known as [`()`](`unit`), is a [`Bundle`] containing no components (since it /// can also be considered as the empty tuple). diff --git a/crates/bevy_sprite/src/texture_atlas_builder.rs b/crates/bevy_sprite/src/texture_atlas_builder.rs index db652b8163709..8aad5216c02aa 100644 --- a/crates/bevy_sprite/src/texture_atlas_builder.rs +++ b/crates/bevy_sprite/src/texture_atlas_builder.rs @@ -188,7 +188,10 @@ impl<'a> TextureAtlasBuilder<'a> { /// let texture = textures.add(texture); /// let layout = layouts.add(atlas_layout); /// // Spawn your sprite - /// commands.spawn(Sprite::from_atlas_image(texture, TextureAtlas::from(layout))); + /// commands.spawn(( + /// SpriteBundle { texture, ..Default::default() }, + /// TextureAtlas::from(layout), + /// )); /// } /// ``` /// diff --git a/examples/2d/bloom_2d.rs b/examples/2d/bloom_2d.rs index 0c967e864dfe3..a603ced251bf9 100644 --- a/examples/2d/bloom_2d.rs +++ b/examples/2d/bloom_2d.rs @@ -36,10 +36,13 @@ fn setup( )); // Sprite - commands.spawn(Sprite { - image: asset_server.load("branding/bevy_bird_dark.png"), - color: Color::srgb(5.0, 5.0, 5.0), // 4. Put something bright in a dark environment to see the effect - custom_size: Some(Vec2::splat(160.0)), + commands.spawn(SpriteBundle { + texture: asset_server.load("branding/bevy_bird_dark.png"), + sprite: Sprite { + color: Color::srgb(5.0, 5.0, 5.0), // 4. Put something bright in a dark environment to see the effect + custom_size: Some(Vec2::splat(160.0)), + ..default() + }, ..default() }); diff --git a/examples/2d/move_sprite.rs b/examples/2d/move_sprite.rs index 8247798c90823..6f0efa53cbe76 100644 --- a/examples/2d/move_sprite.rs +++ b/examples/2d/move_sprite.rs @@ -19,8 +19,11 @@ enum Direction { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); commands.spawn(( - Sprite::from_image(asset_server.load("branding/icon.png")), - Transform::from_xyz(100., 0., 0.), + SpriteBundle { + texture: asset_server.load("branding/icon.png"), + transform: Transform::from_xyz(100., 0., 0.), + ..default() + }, Direction::Up, )); } diff --git a/examples/2d/pixel_grid_snap.rs b/examples/2d/pixel_grid_snap.rs index de1bf9904d3f5..95c7079dddea4 100644 --- a/examples/2d/pixel_grid_snap.rs +++ b/examples/2d/pixel_grid_snap.rs @@ -53,16 +53,22 @@ struct Rotate; fn setup_sprite(mut commands: Commands, asset_server: Res) { // the sample sprite that will be rendered to the pixel-perfect canvas commands.spawn(( - Sprite::from_image(asset_server.load("pixel/bevy_pixel_dark.png")), - Transform::from_xyz(-40., 20., 2.), + SpriteBundle { + texture: asset_server.load("pixel/bevy_pixel_dark.png"), + transform: Transform::from_xyz(-40., 20., 2.), + ..default() + }, Rotate, PIXEL_PERFECT_LAYERS, )); // the sample sprite that will be rendered to the high-res "outer world" commands.spawn(( - Sprite::from_image(asset_server.load("pixel/bevy_pixel_light.png")), - Transform::from_xyz(-40., -20., 2.), + SpriteBundle { + texture: asset_server.load("pixel/bevy_pixel_light.png"), + transform: Transform::from_xyz(-40., -20., 2.), + ..default() + }, Rotate, HIGH_RES_LAYERS, )); @@ -132,7 +138,14 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { )); // spawn the canvas - commands.spawn((Sprite::from_image(image_handle), Canvas, HIGH_RES_LAYERS)); + commands.spawn(( + SpriteBundle { + texture: image_handle, + ..default() + }, + Canvas, + HIGH_RES_LAYERS, + )); // the "outer" camera renders whatever is on `HIGH_RES_LAYERS` to the screen. // here, the canvas and one of the sample sprites will be rendered by this camera diff --git a/examples/2d/rotation.rs b/examples/2d/rotation.rs index 031b9479fcc3b..43227ed5027be 100644 --- a/examples/2d/rotation.rs +++ b/examples/2d/rotation.rs @@ -62,7 +62,10 @@ fn setup(mut commands: Commands, asset_server: Res) { // player controlled ship commands.spawn(( - Sprite::from_image(ship_handle), + SpriteBundle { + texture: ship_handle, + ..default() + }, Player { movement_speed: 500.0, // meters per second rotation_speed: f32::to_radians(360.0), // degrees per second @@ -71,27 +74,39 @@ fn setup(mut commands: Commands, asset_server: Res) { // enemy that snaps to face the player spawns on the bottom and left commands.spawn(( - Sprite::from_image(enemy_a_handle.clone()), - Transform::from_xyz(0.0 - horizontal_margin, 0.0, 0.0), + SpriteBundle { + texture: enemy_a_handle.clone(), + transform: Transform::from_xyz(0.0 - horizontal_margin, 0.0, 0.0), + ..default() + }, SnapToPlayer, )); commands.spawn(( - Sprite::from_image(enemy_a_handle), - Transform::from_xyz(0.0, 0.0 - vertical_margin, 0.0), + SpriteBundle { + texture: enemy_a_handle, + transform: Transform::from_xyz(0.0, 0.0 - vertical_margin, 0.0), + ..default() + }, SnapToPlayer, )); // enemy that rotates to face the player enemy spawns on the top and right commands.spawn(( - Sprite::from_image(enemy_b_handle.clone()), - Transform::from_xyz(0.0 + horizontal_margin, 0.0, 0.0), + SpriteBundle { + texture: enemy_b_handle.clone(), + transform: Transform::from_xyz(0.0 + horizontal_margin, 0.0, 0.0), + ..default() + }, RotateToPlayer { rotation_speed: f32::to_radians(45.0), // degrees per second }, )); commands.spawn(( - Sprite::from_image(enemy_b_handle), - Transform::from_xyz(0.0, 0.0 + vertical_margin, 0.0), + SpriteBundle { + texture: enemy_b_handle, + transform: Transform::from_xyz(0.0, 0.0 + vertical_margin, 0.0), + ..default() + }, RotateToPlayer { rotation_speed: f32::to_radians(90.0), // degrees per second }, diff --git a/examples/2d/sprite.rs b/examples/2d/sprite.rs index 64b42c484ba41..5fdf172a5be19 100644 --- a/examples/2d/sprite.rs +++ b/examples/2d/sprite.rs @@ -11,7 +11,8 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); - commands.spawn(Sprite::from_image( - asset_server.load("branding/bevy_bird_dark.png"), - )); + commands.spawn(SpriteBundle { + texture: asset_server.load("branding/bevy_bird_dark.png"), + ..default() + }); } diff --git a/examples/2d/sprite_animation.rs b/examples/2d/sprite_animation.rs index 9c9c6d6f66c01..74d3a2bf43e8f 100644 --- a/examples/2d/sprite_animation.rs +++ b/examples/2d/sprite_animation.rs @@ -104,15 +104,16 @@ fn setup( // create the first (left-hand) sprite commands.spawn(( - Sprite { - image: texture.clone(), - atlas: Some(TextureAtlas { - layout: texture_atlas_layout.clone(), - index: animation_config_1.first_sprite_index, - }), + SpriteBundle { + transform: Transform::from_scale(Vec3::splat(6.0)) + .with_translation(Vec3::new(-50.0, 0.0, 0.0)), + texture: texture.clone(), ..default() }, - Transform::from_scale(Vec3::splat(6.0)).with_translation(Vec3::new(-50.0, 0.0, 0.0)), + TextureAtlas { + layout: texture_atlas_layout.clone(), + index: animation_config_1.first_sprite_index, + }, LeftSprite, animation_config_1, )); @@ -122,13 +123,15 @@ fn setup( // create the second (right-hand) sprite commands.spawn(( - Sprite { - image: texture.clone(), - atlas: Some(TextureAtlas { - layout: texture_atlas_layout.clone(), - index: animation_config_2.first_sprite_index, - }), - ..Default::default() + SpriteBundle { + transform: Transform::from_scale(Vec3::splat(6.0)) + .with_translation(Vec3::new(50.0, 0.0, 0.0)), + texture: texture.clone(), + ..default() + }, + TextureAtlas { + layout: texture_atlas_layout.clone(), + index: animation_config_2.first_sprite_index, }, RightSprite, animation_config_2, diff --git a/examples/2d/sprite_flipping.rs b/examples/2d/sprite_flipping.rs index ca169574a1dff..33ac914995a99 100644 --- a/examples/2d/sprite_flipping.rs +++ b/examples/2d/sprite_flipping.rs @@ -11,12 +11,15 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); - commands.spawn(Sprite { - image: asset_server.load("branding/bevy_bird_dark.png"), - // Flip the logo to the left - flip_x: true, - // And don't flip it upside-down ( the default ) - flip_y: false, + commands.spawn(SpriteBundle { + texture: asset_server.load("branding/bevy_bird_dark.png"), + sprite: Sprite { + // Flip the logo to the left + flip_x: true, + // And don't flip it upside-down ( the default ) + flip_y: false, + ..default() + }, ..default() }); } diff --git a/examples/2d/sprite_sheet.rs b/examples/2d/sprite_sheet.rs index b1f1051d9a0b2..c1c55914b78f3 100644 --- a/examples/2d/sprite_sheet.rs +++ b/examples/2d/sprite_sheet.rs @@ -48,14 +48,15 @@ fn setup( let animation_indices = AnimationIndices { first: 1, last: 6 }; commands.spawn(Camera2dBundle::default()); commands.spawn(( - Sprite::from_atlas_image( + SpriteBundle { + transform: Transform::from_scale(Vec3::splat(6.0)), texture, - TextureAtlas { - layout: texture_atlas_layout, - index: animation_indices.first, - }, - ), - Transform::from_scale(Vec3::splat(6.0)), + ..default() + }, + TextureAtlas { + layout: texture_atlas_layout, + index: animation_indices.first, + }, animation_indices, AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)), )); diff --git a/examples/2d/sprite_slice.rs b/examples/2d/sprite_slice.rs index cd4dd312f1c93..9782710326995 100644 --- a/examples/2d/sprite_slice.rs +++ b/examples/2d/sprite_slice.rs @@ -73,14 +73,15 @@ fn spawn_sprites( for (label, text_style, size, scale_mode) in cases { position.x += 0.5 * size.x; - let mut cmd = commands.spawn(( - Sprite { - image: texture_handle.clone(), + let mut cmd = commands.spawn(SpriteBundle { + transform: Transform::from_translation(position), + texture: texture_handle.clone(), + sprite: Sprite { custom_size: Some(size), ..default() }, - Transform::from_translation(position), - )); + ..default() + }); if let Some(scale_mode) = scale_mode { cmd = cmd.insert(scale_mode); } diff --git a/examples/2d/sprite_tile.rs b/examples/2d/sprite_tile.rs index 7c2515acd8d47..c9b0fe5ecb811 100644 --- a/examples/2d/sprite_tile.rs +++ b/examples/2d/sprite_tile.rs @@ -27,7 +27,10 @@ fn setup(mut commands: Commands, asset_server: Res) { speed: 50.0, }); commands.spawn(( - Sprite::from_image(asset_server.load("branding/icon.png")), + SpriteBundle { + texture: asset_server.load("branding/icon.png"), + ..default() + }, ImageScaleMode::Tiled { tile_x: true, tile_y: true, diff --git a/examples/2d/text2d.rs b/examples/2d/text2d.rs index ad9ddcd401a48..f69abc3bed3c3 100644 --- a/examples/2d/text2d.rs +++ b/examples/2d/text2d.rs @@ -79,10 +79,15 @@ fn setup(mut commands: Commands, asset_server: Res) { let box_size = Vec2::new(300.0, 200.0); let box_position = Vec2::new(0.0, -250.0); commands - .spawn(( - Sprite::from_color(Color::srgb(0.25, 0.25, 0.75), box_size), - Transform::from_translation(box_position.extend(0.0)), - )) + .spawn(SpriteBundle { + sprite: Sprite { + color: Color::srgb(0.25, 0.25, 0.75), + custom_size: Some(Vec2::new(box_size.x, box_size.y)), + ..default() + }, + transform: Transform::from_translation(box_position.extend(0.0)), + ..default() + }) .with_children(|builder| { builder.spawn(Text2dBundle { text: Text { @@ -105,10 +110,15 @@ fn setup(mut commands: Commands, asset_server: Res) { let other_box_size = Vec2::new(300.0, 200.0); let other_box_position = Vec2::new(320.0, -250.0); commands - .spawn(( - Sprite::from_color(Color::srgb(0.20, 0.3, 0.70), other_box_size), - Transform::from_translation(other_box_position.extend(0.0)), - )) + .spawn(SpriteBundle { + sprite: Sprite { + color: Color::srgb(0.20, 0.3, 0.70), + custom_size: Some(Vec2::new(other_box_size.x, other_box_size.y)), + ..default() + }, + transform: Transform::from_translation(other_box_position.extend(0.0)), + ..default() + }) .with_children(|builder| { builder.spawn(Text2dBundle { text: Text { diff --git a/examples/2d/texture_atlas.rs b/examples/2d/texture_atlas.rs index 6f8efd557559e..0bb8615f4aa42 100644 --- a/examples/2d/texture_atlas.rs +++ b/examples/2d/texture_atlas.rs @@ -97,24 +97,26 @@ fn setup( // padded textures are to the right, unpadded to the left // draw unpadded texture atlas - commands.spawn(( - Sprite::from_image(linear_texture.clone()), - Transform { + commands.spawn(SpriteBundle { + texture: linear_texture.clone(), + transform: Transform { translation: Vec3::new(-250.0, -130.0, 0.0), scale: Vec3::splat(0.8), ..default() }, - )); + ..default() + }); // draw padded texture atlas - commands.spawn(( - Sprite::from_image(linear_padded_texture.clone()), - Transform { + commands.spawn(SpriteBundle { + texture: linear_padded_texture.clone(), + transform: Transform { translation: Vec3::new(250.0, -130.0, 0.0), scale: Vec3::splat(0.8), ..default() }, - )); + ..default() + }); let font = asset_server.load("fonts/FiraSans-Bold.ttf"); @@ -239,18 +241,19 @@ fn create_sprite_from_atlas( texture: Handle, ) { commands.spawn(( - Transform { - translation: Vec3::new(translation.0, translation.1, translation.2), - scale: Vec3::splat(3.0), + SpriteBundle { + transform: Transform { + translation: Vec3::new(translation.0, translation.1, translation.2), + scale: Vec3::splat(3.0), + ..default() + }, + texture, ..default() }, - Sprite::from_atlas_image( - texture, - TextureAtlas { - layout: atlas_handle, - index: sprite_index, - }, - ), + TextureAtlas { + layout: atlas_handle, + index: sprite_index, + }, )); } diff --git a/examples/2d/transparency_2d.rs b/examples/2d/transparency_2d.rs index 5ce9da7c9da70..069b73098e688 100644 --- a/examples/2d/transparency_2d.rs +++ b/examples/2d/transparency_2d.rs @@ -15,22 +15,27 @@ fn setup(mut commands: Commands, asset_server: Res) { let sprite_handle = asset_server.load("branding/icon.png"); - commands.spawn(Sprite::from_image(sprite_handle.clone())); - commands.spawn(( - Sprite { - image: sprite_handle.clone(), + commands.spawn(SpriteBundle { + texture: sprite_handle.clone(), + ..default() + }); + commands.spawn(SpriteBundle { + sprite: Sprite { // Alpha channel of the color controls transparency. color: Color::srgba(0.0, 0.0, 1.0, 0.7), ..default() }, - Transform::from_xyz(100.0, 0.0, 0.0), - )); - commands.spawn(( - Sprite { - image: sprite_handle, + texture: sprite_handle.clone(), + transform: Transform::from_xyz(100.0, 0.0, 0.0), + ..default() + }); + commands.spawn(SpriteBundle { + sprite: Sprite { color: Color::srgba(0.0, 1.0, 0.0, 0.3), ..default() }, - Transform::from_xyz(200.0, 0.0, 0.0), - )); + texture: sprite_handle, + transform: Transform::from_xyz(200.0, 0.0, 0.0), + ..default() + }); } diff --git a/examples/animation/color_animation.rs b/examples/animation/color_animation.rs index eb1f8e06bb055..e1b364e39b5e8 100644 --- a/examples/animation/color_animation.rs +++ b/examples/animation/color_animation.rs @@ -70,16 +70,28 @@ fn setup(mut commands: Commands) { fn spawn_curve_sprite(commands: &mut Commands, y: f32, points: [T; 4]) { commands.spawn(( - Sprite::sized(Vec2::new(75., 75.)), - Transform::from_xyz(0., y, 0.), + SpriteBundle { + transform: Transform::from_xyz(0., y, 0.), + sprite: Sprite { + custom_size: Some(Vec2::new(75., 75.)), + ..Default::default() + }, + ..Default::default() + }, Curve(CubicBezier::new([points]).to_curve().unwrap()), )); } fn spawn_mixed_sprite(commands: &mut Commands, y: f32, colors: [T; 4]) { commands.spawn(( - Transform::from_xyz(0., y, 0.), - Sprite::sized(Vec2::new(75., 75.)), + SpriteBundle { + transform: Transform::from_xyz(0., y, 0.), + sprite: Sprite { + custom_size: Some(Vec2::new(75., 75.)), + ..Default::default() + }, + ..Default::default() + }, Mixed(colors), )); } diff --git a/examples/asset/alter_sprite.rs b/examples/asset/alter_sprite.rs index e9f02ec01c223..7725ad36eb8f2 100644 --- a/examples/asset/alter_sprite.rs +++ b/examples/asset/alter_sprite.rs @@ -75,16 +75,23 @@ fn setup(mut commands: Commands, asset_server: Res) { // This marker component ensures we can easily find either of the Birds by using With and // Without query filters. Left, - Sprite::from_image(texture_left), - Transform::from_xyz(-200.0, 0.0, 0.0), + SpriteBundle { + texture: texture_left, + transform: Transform::from_xyz(-200.0, 0.0, 0.0), + ..default() + }, bird_left, )); commands.spawn(( Name::new("Bird Right"), - // In contrast to the above, here we rely on the default `RenderAssetUsages` loader setting - Sprite::from_image(asset_server.load(bird_right.get_texture_path())), - Transform::from_xyz(200.0, 0.0, 0.0), + SpriteBundle { + // In contrast to the above, here we rely on the default `RenderAssetUsages` loader + // setting. + texture: asset_server.load(bird_right.get_texture_path()), + transform: Transform::from_xyz(200.0, 0.0, 0.0), + ..default() + }, bird_right, )); } diff --git a/examples/asset/asset_settings.rs b/examples/asset/asset_settings.rs index 5462d3b5eedf4..d45393b3afa51 100644 --- a/examples/asset/asset_settings.rs +++ b/examples/asset/asset_settings.rs @@ -23,14 +23,15 @@ fn setup(mut commands: Commands, asset_server: Res) { // If you are using a very small image and rendering it larger like seen here, the default linear filtering will result in a blurry image. // Useful note: The default sampler specified by the ImagePlugin is *not* the same as the default implementation of sampler. This is why // everything uses linear by default but if you look at the default of sampler, it uses nearest. - commands.spawn(( - Sprite { - image: asset_server.load("bevy_pixel_dark.png"), + commands.spawn(SpriteBundle { + texture: asset_server.load("bevy_pixel_dark.png"), + sprite: Sprite { custom_size: Some(Vec2 { x: 160.0, y: 120.0 }), ..Default::default() }, - Transform::from_xyz(-100.0, 0.0, 0.0), - )); + transform: Transform::from_xyz(-100.0, 0.0, 0.0), + ..Default::default() + }); // When a .meta file is added with the same name as the asset and a '.meta' extension // you can (and must) specify all fields of the asset loader's settings for that @@ -41,14 +42,15 @@ fn setup(mut commands: Commands, asset_server: Res) { // A good reference when filling this out is to check out [ImageLoaderSettings::default()] // and follow to the default implementation of each fields type. // https://docs.rs/bevy/latest/bevy/render/texture/struct.ImageLoaderSettings.html# - commands.spawn(( - Sprite { - image: asset_server.load("bevy_pixel_dark_with_meta.png"), + commands.spawn(SpriteBundle { + texture: asset_server.load("bevy_pixel_dark_with_meta.png"), + sprite: Sprite { custom_size: Some(Vec2 { x: 160.0, y: 120.0 }), ..Default::default() }, - Transform::from_xyz(100.0, 0.0, 0.0), - )); + transform: Transform::from_xyz(100.0, 0.0, 0.0), + ..Default::default() + }); // Another option is to use the AssetServers load_with_settings function. // With this you can specify the same settings upon loading your asset with a @@ -60,19 +62,20 @@ fn setup(mut commands: Commands, asset_server: Res) { // settings changes from any loads after the first of the same asset will be ignored. // This is why this one loads a differently named copy of the asset instead of using // same one as without a .meta file. - commands.spawn(( - Sprite { - image: asset_server.load_with_settings( - "bevy_pixel_dark_with_settings.png", - |settings: &mut ImageLoaderSettings| { - settings.sampler = ImageSampler::nearest(); - }, - ), + commands.spawn(SpriteBundle { + texture: asset_server.load_with_settings( + "bevy_pixel_dark_with_settings.png", + |settings: &mut ImageLoaderSettings| { + settings.sampler = ImageSampler::nearest(); + }, + ), + sprite: Sprite { custom_size: Some(Vec2 { x: 160.0, y: 120.0 }), ..Default::default() }, - Transform::from_xyz(0.0, 150.0, 0.0), - )); + transform: Transform::from_xyz(0.0, 150.0, 0.0), + ..Default::default() + }); commands.spawn(Camera2dBundle::default()); } diff --git a/examples/asset/custom_asset_reader.rs b/examples/asset/custom_asset_reader.rs index 05073c6639f83..7ee270c2c8f32 100644 --- a/examples/asset/custom_asset_reader.rs +++ b/examples/asset/custom_asset_reader.rs @@ -61,5 +61,8 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); - commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); + commands.spawn(SpriteBundle { + texture: asset_server.load("branding/icon.png"), + ..default() + }); } diff --git a/examples/asset/embedded_asset.rs b/examples/asset/embedded_asset.rs index 3dd0134f228ca..b60ec636b3750 100644 --- a/examples/asset/embedded_asset.rs +++ b/examples/asset/embedded_asset.rs @@ -48,5 +48,8 @@ fn setup(mut commands: Commands, asset_server: Res) { "embedded://embedded_asset/files/bevy_pixel_light.png".into() ); - commands.spawn(Sprite::from_image(asset_server.load(asset_path))); + commands.spawn(SpriteBundle { + texture: asset_server.load(asset_path), + ..default() + }); } diff --git a/examples/asset/extra_source.rs b/examples/asset/extra_source.rs index b8f339a90a5e0..4e0463e4b1c7d 100644 --- a/examples/asset/extra_source.rs +++ b/examples/asset/extra_source.rs @@ -42,5 +42,8 @@ fn setup(mut commands: Commands, asset_server: Res) { // path. assert_eq!(asset_path, "example_files://bevy_pixel_light.png".into()); - commands.spawn(Sprite::from_image(asset_server.load(asset_path))); + commands.spawn(SpriteBundle { + texture: asset_server.load(asset_path), + ..default() + }); } diff --git a/examples/audio/spatial_audio_2d.rs b/examples/audio/spatial_audio_2d.rs index 6c4f302921338..72c0b77daa328 100644 --- a/examples/audio/spatial_audio_2d.rs +++ b/examples/audio/spatial_audio_2d.rs @@ -52,16 +52,26 @@ fn setup( .spawn((SpatialBundle::default(), listener.clone())) .with_children(|parent| { // left ear - parent.spawn(( - Sprite::from_color(RED, Vec2::splat(20.0)), - Transform::from_xyz(-gap / 2.0, 0.0, 0.0), - )); + parent.spawn(SpriteBundle { + sprite: Sprite { + color: RED.into(), + custom_size: Some(Vec2::splat(20.0)), + ..default() + }, + transform: Transform::from_xyz(-gap / 2.0, 0.0, 0.0), + ..default() + }); // right ear - parent.spawn(( - Sprite::from_color(LIME, Vec2::splat(20.0)), - Transform::from_xyz(gap / 2.0, 0.0, 0.0), - )); + parent.spawn(SpriteBundle { + sprite: Sprite { + color: LIME.into(), + custom_size: Some(Vec2::splat(20.0)), + ..default() + }, + transform: Transform::from_xyz(gap / 2.0, 0.0, 0.0), + ..default() + }); }); // example instructions diff --git a/examples/ecs/hierarchy.rs b/examples/ecs/hierarchy.rs index b95945e15b43f..e3356d28e36f5 100644 --- a/examples/ecs/hierarchy.rs +++ b/examples/ecs/hierarchy.rs @@ -18,21 +18,23 @@ fn setup(mut commands: Commands, asset_server: Res) { // Spawn a root entity with no parent let parent = commands - .spawn(( - Sprite::from_image(texture.clone()), - Transform::from_scale(Vec3::splat(0.75)), - )) + .spawn(SpriteBundle { + transform: Transform::from_scale(Vec3::splat(0.75)), + texture: texture.clone(), + ..default() + }) // With that entity as a parent, run a lambda that spawns its children .with_children(|parent| { // parent is a ChildBuilder, which has a similar API to Commands - parent.spawn(( - Transform::from_xyz(250.0, 0.0, 0.0).with_scale(Vec3::splat(0.75)), - Sprite { - image: texture.clone(), + parent.spawn(SpriteBundle { + transform: Transform::from_xyz(250.0, 0.0, 0.0).with_scale(Vec3::splat(0.75)), + texture: texture.clone(), + sprite: Sprite { color: BLUE.into(), ..default() }, - )); + ..default() + }); }) // Store parent entity for next sections .id(); @@ -40,14 +42,15 @@ fn setup(mut commands: Commands, asset_server: Res) { // Another way is to use the add_child function to add children after the parent // entity has already been spawned. let child = commands - .spawn(( - Sprite { - image: texture, + .spawn(SpriteBundle { + transform: Transform::from_xyz(0.0, 250.0, 0.0).with_scale(Vec3::splat(0.75)), + texture, + sprite: Sprite { color: LIME.into(), ..default() }, - Transform::from_xyz(0.0, 250.0, 0.0).with_scale(Vec3::splat(0.75)), - )) + ..default() + }) .id(); // Add child to the parent. diff --git a/examples/ecs/parallel_query.rs b/examples/ecs/parallel_query.rs index 0ab683582c462..adf7f243da83c 100644 --- a/examples/ecs/parallel_query.rs +++ b/examples/ecs/parallel_query.rs @@ -16,8 +16,11 @@ fn spawn_system(mut commands: Commands, asset_server: Res) { let mut rng = ChaCha8Rng::seed_from_u64(19878367467713); for _ in 0..128 { commands.spawn(( - Sprite::from_image(texture.clone()), - Transform::from_scale(Vec3::splat(0.1)), + SpriteBundle { + texture: texture.clone(), + transform: Transform::from_scale(Vec3::splat(0.1)), + ..default() + }, Velocity(20.0 * Vec2::new(rng.gen::() - 0.5, rng.gen::() - 0.5)), )); } diff --git a/examples/ecs/removal_detection.rs b/examples/ecs/removal_detection.rs index a1085866546ed..462c5d6cbd98f 100644 --- a/examples/ecs/removal_detection.rs +++ b/examples/ecs/removal_detection.rs @@ -29,7 +29,10 @@ struct MyComponent; fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); commands.spawn(( - Sprite::from_image(asset_server.load("branding/icon.png")), + SpriteBundle { + texture: asset_server.load("branding/icon.png"), + ..default() + }, // Add the `Component`. MyComponent, )); diff --git a/examples/games/breakout.rs b/examples/games/breakout.rs index ff4bde4de1527..f6f690b1e4c9f 100644 --- a/examples/games/breakout.rs +++ b/examples/games/breakout.rs @@ -107,8 +107,7 @@ struct CollisionSound(Handle); struct WallBundle { // You can nest bundles inside of other bundles like this // Allowing you to compose their functionality - sprite: Sprite, - transform: Transform, + sprite_bundle: SpriteBundle, collider: Collider, } @@ -155,15 +154,21 @@ impl WallBundle { // making our code easier to read and less prone to bugs when we change the logic fn new(location: WallLocation) -> WallBundle { WallBundle { - sprite: Sprite::from_color(WALL_COLOR, Vec2::ONE), - transform: Transform { - // We need to convert our Vec2 into a Vec3, by giving it a z-coordinate - // This is used to determine the order of our sprites - translation: location.position().extend(0.0), - // The z-scale of 2D objects must always be 1.0, - // or their ordering will be affected in surprising ways. - // See https://github.com/bevyengine/bevy/issues/4149 - scale: location.size().extend(1.0), + sprite_bundle: SpriteBundle { + transform: Transform { + // We need to convert our Vec2 into a Vec3, by giving it a z-coordinate + // This is used to determine the order of our sprites + translation: location.position().extend(0.0), + // The z-scale of 2D objects must always be 1.0, + // or their ordering will be affected in surprising ways. + // See https://github.com/bevyengine/bevy/issues/4149 + scale: location.size().extend(1.0), + ..default() + }, + sprite: Sprite { + color: WALL_COLOR, + ..default() + }, ..default() }, collider: Collider, @@ -196,10 +201,16 @@ fn setup( let paddle_y = BOTTOM_WALL + GAP_BETWEEN_PADDLE_AND_FLOOR; commands.spawn(( - Sprite::from_color(PADDLE_COLOR, Vec2::ONE), - Transform { - translation: Vec3::new(0.0, paddle_y, 0.0), - scale: PADDLE_SIZE.extend(1.0), + SpriteBundle { + transform: Transform { + translation: Vec3::new(0.0, paddle_y, 0.0), + scale: PADDLE_SIZE.extend(1.0), + ..default() + }, + sprite: Sprite { + color: PADDLE_COLOR, + ..default() + }, ..default() }, Paddle, @@ -287,13 +298,16 @@ fn setup( // brick commands.spawn(( - Sprite { - color: BRICK_COLOR, - ..default() - }, - Transform { - translation: brick_position.extend(0.0), - scale: Vec3::new(BRICK_SIZE.x, BRICK_SIZE.y, 1.0), + SpriteBundle { + sprite: Sprite { + color: BRICK_COLOR, + ..default() + }, + transform: Transform { + translation: brick_position.extend(0.0), + scale: Vec3::new(BRICK_SIZE.x, BRICK_SIZE.y, 1.0), + ..default() + }, ..default() }, Brick, diff --git a/examples/games/contributors.rs b/examples/games/contributors.rs index cceaad9489e12..067f0bdf58b20 100644 --- a/examples/games/contributors.rs +++ b/examples/games/contributors.rs @@ -106,14 +106,17 @@ fn setup_contributor_selection(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); commands.spawn(( Name::new("Player"), - Sprite::from_image(asset_server.load("branding/icon.png")), - Transform::from_scale(Vec3::splat(0.3)), + SpriteBundle { + texture: asset_server.load("branding/icon.png"), + transform: Transform::from_scale(Vec3::splat(0.3)), + ..default() + }, AccumulatedInput::default(), Velocity::default(), PhysicalTranslation::default(), diff --git a/examples/picking/sprite_picking.rs b/examples/picking/sprite_picking.rs index 8ee5ea0fde159..67abff50b5cf9 100644 --- a/examples/picking/sprite_picking.rs +++ b/examples/picking/sprite_picking.rs @@ -32,7 +32,7 @@ fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); let len = 128.0; - let sprite_size = Vec2::splat(len / 2.0); + let sprite_size = Some(Vec2::splat(len / 2.0)); commands .spawn(SpatialBundle::default()) @@ -57,29 +57,35 @@ fn setup(mut commands: Commands, asset_server: Res) { // spawn black square behind sprite to show anchor point commands - .spawn(( - Sprite::from_color(Color::BLACK, sprite_size), - Transform::from_xyz(i * len - len, j * len - len, -1.0), - )) + .spawn(SpriteBundle { + sprite: Sprite { + custom_size: sprite_size, + color: Color::BLACK, + ..default() + }, + transform: Transform::from_xyz(i * len - len, j * len - len, -1.0), + ..default() + }) .observe(recolor_on::>(Color::srgb(0.0, 1.0, 1.0))) .observe(recolor_on::>(Color::BLACK)) .observe(recolor_on::>(Color::srgb(1.0, 1.0, 0.0))) .observe(recolor_on::>(Color::srgb(0.0, 1.0, 1.0))); commands - .spawn(( - Sprite { - image: asset_server.load("branding/bevy_bird_dark.png"), - custom_size: Some(sprite_size), + .spawn(SpriteBundle { + sprite: Sprite { + custom_size: sprite_size, color: Color::srgb(1.0, 0.0, 0.0), anchor: anchor.to_owned(), ..default() }, + texture: asset_server.load("branding/bevy_bird_dark.png"), // 3x3 grid of anchor examples by changing transform - Transform::from_xyz(i * len - len, j * len - len, 0.0) + transform: Transform::from_xyz(i * len - len, j * len - len, 0.0) .with_scale(Vec3::splat(1.0 + (i - 1.0) * 0.2)) .with_rotation(Quat::from_rotation_z((j - 1.0) * 0.2)), - )) + ..default() + }) .observe(recolor_on::>(Color::srgb(0.0, 1.0, 0.0))) .observe(recolor_on::>(Color::srgb(1.0, 0.0, 0.0))) .observe(recolor_on::>(Color::srgb(0.0, 0.0, 1.0))) @@ -125,14 +131,15 @@ fn setup_atlas( let animation_indices = AnimationIndices { first: 1, last: 6 }; commands .spawn(( - Sprite::from_atlas_image( - texture_handle, - TextureAtlas { - layout: texture_atlas_layout_handle, - index: animation_indices.first, - }, - ), - Transform::from_xyz(300.0, 0.0, 0.0).with_scale(Vec3::splat(6.0)), + TextureAtlas { + layout: texture_atlas_layout_handle, + index: animation_indices.first, + }, + SpriteBundle { + texture: texture_handle, + transform: Transform::from_xyz(300.0, 0.0, 0.0).with_scale(Vec3::splat(6.0)), + ..default() + }, animation_indices, AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)), )) diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index 7f4381376a694..09c072d1a5c86 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -67,14 +67,15 @@ fn setup(mut commands: Commands, mut images: ResMut>) { let image0 = images.add(image.clone()); let image1 = images.add(image); - commands.spawn(( - Sprite { - image: image0.clone(), + commands.spawn(SpriteBundle { + sprite: Sprite { custom_size: Some(Vec2::new(SIZE.0 as f32, SIZE.1 as f32)), ..default() }, - Transform::from_scale(Vec3::splat(DISPLAY_FACTOR as f32)), - )); + texture: image0.clone(), + transform: Transform::from_scale(Vec3::splat(DISPLAY_FACTOR as f32)), + ..default() + }); commands.spawn(Camera2dBundle::default()); commands.insert_resource(GameOfLifeImages { diff --git a/examples/state/computed_states.rs b/examples/state/computed_states.rs index 167e2e88d4607..47dd555ab2736 100644 --- a/examples/state/computed_states.rs +++ b/examples/state/computed_states.rs @@ -423,7 +423,10 @@ mod ui { pub fn setup_game(mut commands: Commands, asset_server: Res) { commands.spawn(( StateScoped(InGame), - Sprite::from_image(asset_server.load("branding/icon.png")), + SpriteBundle { + texture: asset_server.load("branding/icon.png"), + ..default() + }, )); } diff --git a/examples/state/custom_transitions.rs b/examples/state/custom_transitions.rs index 4cab6c8968593..9baa0a76c6155 100644 --- a/examples/state/custom_transitions.rs +++ b/examples/state/custom_transitions.rs @@ -223,7 +223,10 @@ fn setup(mut commands: Commands) { } fn setup_game(mut commands: Commands, asset_server: Res) { - commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); + commands.spawn(SpriteBundle { + texture: asset_server.load("branding/icon.png"), + ..default() + }); info!("Setup game"); } diff --git a/examples/state/states.rs b/examples/state/states.rs index d6e13dfc8d756..f5a19ea4578fb 100644 --- a/examples/state/states.rs +++ b/examples/state/states.rs @@ -120,7 +120,10 @@ fn cleanup_menu(mut commands: Commands, menu_data: Res) { } fn setup_game(mut commands: Commands, asset_server: Res) { - commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); + commands.spawn(SpriteBundle { + texture: asset_server.load("branding/icon.png"), + ..default() + }); } const SPEED: f32 = 100.0; diff --git a/examples/state/sub_states.rs b/examples/state/sub_states.rs index 00b8783a90ffe..849bae29214a1 100644 --- a/examples/state/sub_states.rs +++ b/examples/state/sub_states.rs @@ -198,7 +198,10 @@ mod ui { } pub fn setup_game(mut commands: Commands, asset_server: Res) { - commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); + commands.spawn(SpriteBundle { + texture: asset_server.load("branding/icon.png"), + ..default() + }); } pub fn setup_paused_screen(mut commands: Commands) { diff --git a/examples/stress_tests/bevymark.rs b/examples/stress_tests/bevymark.rs index 6a6a7d5b72522..e0959cc930eb1 100644 --- a/examples/stress_tests/bevymark.rs +++ b/examples/stress_tests/bevymark.rs @@ -433,16 +433,16 @@ fn spawn_birds( color }; ( - Sprite { - image: bird_resources + SpriteBundle { + texture: bird_resources .textures .choose(&mut bird_resources.material_rng) .unwrap() .clone(), - color, + transform, + sprite: Sprite { color, ..default() }, ..default() }, - transform, Bird { velocity }, ) }) diff --git a/examples/stress_tests/many_animated_sprites.rs b/examples/stress_tests/many_animated_sprites.rs index 71f8c646e110d..17be46f0ac572 100644 --- a/examples/stress_tests/many_animated_sprites.rs +++ b/examples/stress_tests/many_animated_sprites.rs @@ -82,16 +82,19 @@ fn setup( timer.set_elapsed(Duration::from_secs_f32(rng.gen::())); commands.spawn(( - Sprite { - image: texture_handle.clone(), - custom_size: Some(tile_size), + SpriteBundle { + texture: texture_handle.clone(), + transform: Transform { + translation, + rotation, + scale, + }, + sprite: Sprite { + custom_size: Some(tile_size), + ..default() + }, ..default() }, - Transform { - translation, - rotation, - scale, - }, TextureAtlas::from(texture_atlas_handle.clone()), AnimationTimer(timer), )); diff --git a/examples/stress_tests/many_sprites.rs b/examples/stress_tests/many_sprites.rs index daef33f661262..fb08d2942f4b1 100644 --- a/examples/stress_tests/many_sprites.rs +++ b/examples/stress_tests/many_sprites.rs @@ -81,9 +81,14 @@ fn setup(mut commands: Commands, assets: Res, color_tint: Res()); let scale = Vec3::splat(rng.gen::() * 2.0); - sprites.push(( - Sprite { - image: sprite_handle.clone(), + sprites.push(SpriteBundle { + texture: sprite_handle.clone(), + transform: Transform { + translation, + rotation, + scale, + }, + sprite: Sprite { custom_size: Some(tile_size), color: if color_tint.0 { COLORS[rng.gen_range(0..3)] @@ -92,12 +97,8 @@ fn setup(mut commands: Commands, assets: Res, color_tint: Res, mut time: ResMu // the sprite moving based on real time commands.spawn(( - Sprite::from_image(texture_handle.clone()), - Transform::from_scale(sprite_scale), + SpriteBundle { + texture: texture_handle.clone(), + transform: Transform::from_scale(sprite_scale), + ..default() + }, RealTime, )); // the sprite moving based on virtual time commands.spawn(( - Sprite { - image: texture_handle, - color: virtual_color, - ..Default::default() - }, - Transform { - scale: sprite_scale, - translation: Vec3::new(0., -160., 0.), + SpriteBundle { + texture: texture_handle, + sprite: Sprite { + color: virtual_color, + ..default() + }, + transform: Transform { + scale: sprite_scale, + translation: Vec3::new(0., -160., 0.), + ..default() + }, ..default() }, VirtualTime, diff --git a/examples/tools/gamepad_viewer.rs b/examples/tools/gamepad_viewer.rs index dad7aa01f9044..7be76fe50a6ad 100644 --- a/examples/tools/gamepad_viewer.rs +++ b/examples/tools/gamepad_viewer.rs @@ -281,20 +281,34 @@ fn setup_sticks( }) .with_children(|parent| { // full extent - parent.spawn(Sprite::from_color( - DEAD_COLOR, - Vec2::splat(STICK_BOUNDS_SIZE * 2.), - )); + parent.spawn(SpriteBundle { + sprite: Sprite { + custom_size: Some(Vec2::splat(STICK_BOUNDS_SIZE * 2.)), + color: DEAD_COLOR, + ..default() + }, + ..default() + }); // live zone - parent.spawn(( - Sprite::from_color(LIVE_COLOR, Vec2::splat(live_size)), - Transform::from_xyz(live_mid, live_mid, 2.), - )); + parent.spawn(SpriteBundle { + transform: Transform::from_xyz(live_mid, live_mid, 2.), + sprite: Sprite { + custom_size: Some(Vec2::new(live_size, live_size)), + color: LIVE_COLOR, + ..default() + }, + ..default() + }); // dead zone - parent.spawn(( - Sprite::from_color(DEAD_COLOR, Vec2::splat(dead_size)), - Transform::from_xyz(dead_mid, dead_mid, 3.), - )); + parent.spawn(SpriteBundle { + transform: Transform::from_xyz(dead_mid, dead_mid, 3.), + sprite: Sprite { + custom_size: Some(Vec2::new(dead_size, dead_size)), + color: DEAD_COLOR, + ..default() + }, + ..default() + }); // text let style = TextStyle { font_size: 13., diff --git a/examples/window/transparent_window.rs b/examples/window/transparent_window.rs index 236b8a147f399..f95408ff52fe0 100644 --- a/examples/window/transparent_window.rs +++ b/examples/window/transparent_window.rs @@ -30,5 +30,8 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); - commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); + commands.spawn(SpriteBundle { + texture: asset_server.load("branding/icon.png"), + ..default() + }); } diff --git a/tests/window/minimising.rs b/tests/window/minimising.rs index 3a637896f1ad8..12d0826573eed 100644 --- a/tests/window/minimising.rs +++ b/tests/window/minimising.rs @@ -74,8 +74,12 @@ fn setup_2d(mut commands: Commands) { }, ..default() }); - commands.spawn(Sprite::from_color( - Color::srgb(0.25, 0.25, 0.75), - Vec2::new(50.0, 50.0), - )); + commands.spawn(SpriteBundle { + sprite: Sprite { + color: Color::srgb(0.25, 0.25, 0.75), + custom_size: Some(Vec2::new(50.0, 50.0)), + ..default() + }, + ..default() + }); } diff --git a/tests/window/resizing.rs b/tests/window/resizing.rs index 2d6254e3a4aa8..3cb6d4913a294 100644 --- a/tests/window/resizing.rs +++ b/tests/window/resizing.rs @@ -150,8 +150,12 @@ fn setup_2d(mut commands: Commands) { }, ..default() }); - commands.spawn(Sprite::from_color( - Color::srgb(0.25, 0.25, 0.75), - Vec2::new(50.0, 50.0), - )); + commands.spawn(SpriteBundle { + sprite: Sprite { + color: Color::srgb(0.25, 0.25, 0.75), + custom_size: Some(Vec2::new(50.0, 50.0)), + ..default() + }, + ..default() + }); } From 6e799ab7e84fca2fffb6d5def7222922d75c93a9 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Mon, 30 Sep 2024 13:43:36 -0700 Subject: [PATCH 04/18] Revert "migrate bevy_sprite" This reverts commit f4a628a1dfde71e155bb7fabd3d7717f51ef8b65. --- crates/bevy_sprite/src/bundle.rs | 5 - crates/bevy_sprite/src/lib.rs | 16 +- crates/bevy_sprite/src/picking_backend.rs | 174 +++++++++--------- crates/bevy_sprite/src/render/mod.rs | 14 +- crates/bevy_sprite/src/sprite.rs | 40 +--- .../src/texture_slice/computed_slices.rs | 37 +++- 6 files changed, 139 insertions(+), 147 deletions(-) diff --git a/crates/bevy_sprite/src/bundle.rs b/crates/bevy_sprite/src/bundle.rs index f0c433de08485..ca962c40b1b2c 100644 --- a/crates/bevy_sprite/src/bundle.rs +++ b/crates/bevy_sprite/src/bundle.rs @@ -1,4 +1,3 @@ -#![expect(deprecated)] use crate::Sprite; use bevy_asset::Handle; use bevy_ecs::bundle::Bundle; @@ -16,10 +15,6 @@ use bevy_transform::components::{GlobalTransform, Transform}; /// - [`ImageScaleMode`](crate::ImageScaleMode) to enable either slicing or tiling of the texture /// - [`TextureAtlas`](crate::TextureAtlas) to draw a specific section of the texture #[derive(Bundle, Clone, Debug, Default)] -#[deprecated( - since = "0.15.0", - note = "Use the `Sprite` component instead. Inserting it will now also insert `Transform` and `Visibility` automatically." -)] pub struct SpriteBundle { /// Specifies the rendering properties of the sprite, such as color tint and flip. pub sprite: Sprite, diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index 2d798f9234368..a411cef4afc1f 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -25,7 +25,6 @@ mod texture_slice; /// The sprite prelude. /// /// This includes the most common types in this crate, re-exported for your convenience. -#[expect(deprecated)] pub mod prelude { #[doc(hidden)] pub use crate::{ @@ -189,9 +188,9 @@ pub fn calculate_bounds_2d( atlases: Res>, meshes_without_aabb: Query<(Entity, &Mesh2dHandle), (Without, Without)>, sprites_to_recalculate_aabb: Query< - (Entity, &Sprite), + (Entity, &Sprite, &Handle, Option<&TextureAtlas>), ( - Or<(Without, Changed)>, + Or<(Without, Changed, Changed)>, Without, ), >, @@ -203,13 +202,13 @@ pub fn calculate_bounds_2d( } } } - for (entity, sprite) in &sprites_to_recalculate_aabb { + for (entity, sprite, texture_handle, atlas) in &sprites_to_recalculate_aabb { if let Some(size) = sprite .custom_size .or_else(|| sprite.rect.map(|rect| rect.size())) - .or_else(|| match &sprite.atlas { + .or_else(|| match atlas { // We default to the texture size for regular sprites - None => images.get(&sprite.image).map(Image::size_f32), + None => images.get(texture_handle).map(Image::size_f32), // We default to the drawn rect for atlas sprites Some(atlas) => atlas .texture_rect(&atlases) @@ -263,7 +262,10 @@ mod test { app.add_systems(Update, calculate_bounds_2d); // Add entities - let entity = app.world_mut().spawn(Sprite::from_image(image_handle)).id(); + let entity = app + .world_mut() + .spawn((Sprite::default(), image_handle)) + .id(); // Verify that the entity does not have an AABB assert!(!app diff --git a/crates/bevy_sprite/src/picking_backend.rs b/crates/bevy_sprite/src/picking_backend.rs index 6e981ffb1d8ad..baf06d1a143b4 100644 --- a/crates/bevy_sprite/src/picking_backend.rs +++ b/crates/bevy_sprite/src/picking_backend.rs @@ -29,20 +29,25 @@ pub fn sprite_picking( primary_window: Query>, images: Res>, texture_atlas_layout: Res>, - sprite_query: Query<( - Entity, - &Sprite, - &GlobalTransform, - Option<&Pickable>, - &ViewVisibility, - )>, + sprite_query: Query< + ( + Entity, + Option<&Sprite>, + Option<&TextureAtlas>, + Option<&Handle>, + &GlobalTransform, + Option<&Pickable>, + &ViewVisibility, + ), + Or<(With, With)>, + >, mut output: EventWriter, ) { let mut sorted_sprites: Vec<_> = sprite_query .iter() - .filter(|x| !x.2.affine().is_nan()) + .filter(|x| !x.4.affine().is_nan()) .collect(); - sorted_sprites.sort_by_key(|x| Reverse(FloatOrd(x.2.translation().z))); + sorted_sprites.sort_by_key(|x| Reverse(FloatOrd(x.4.translation().z))); let primary_window = primary_window.get_single().ok(); @@ -75,83 +80,88 @@ pub fn sprite_picking( .iter() .copied() .filter(|(.., visibility)| visibility.get()) - .filter_map(|(entity, sprite, sprite_transform, pickable, ..)| { - if blocked { - return None; - } - - // Hit box in sprite coordinate system - let Some(extents) = sprite - .custom_size - .or_else(|| { - sprite.atlas.as_ref().and_then(|atlas| { + .filter_map( + |(entity, sprite, atlas, image, sprite_transform, pickable, ..)| { + if blocked { + return None; + } + + // Hit box in sprite coordinate system + let (extents, anchor) = if let Some((sprite, atlas)) = sprite.zip(atlas) { + let extents = sprite.custom_size.or_else(|| { texture_atlas_layout .get(&atlas.layout) .map(|f| f.textures[atlas.index].size().as_vec2()) - }) + })?; + let anchor = sprite.anchor.as_vec(); + (extents, anchor) + } else if let Some((sprite, image)) = sprite.zip(image) { + let extents = sprite + .custom_size + .or_else(|| images.get(image).map(|f| f.size().as_vec2()))?; + let anchor = sprite.anchor.as_vec(); + (extents, anchor) + } else { + return None; + }; + + let center = -anchor * extents; + let rect = Rect::from_center_half_size(center, extents / 2.0); + + // Transform cursor line segment to sprite coordinate system + let world_to_sprite = sprite_transform.affine().inverse(); + let cursor_start_sprite = + world_to_sprite.transform_point3(cursor_ray_world.origin); + let cursor_end_sprite = world_to_sprite.transform_point3(cursor_ray_end); + + // Find where the cursor segment intersects the plane Z=0 (which is the sprite's + // plane in sprite-local space). It may not intersect if, for example, we're + // viewing the sprite side-on + if cursor_start_sprite.z == cursor_end_sprite.z { + // Cursor ray is parallel to the sprite and misses it + return None; + } + let lerp_factor = + f32::inverse_lerp(cursor_start_sprite.z, cursor_end_sprite.z, 0.0); + if !(0.0..=1.0).contains(&lerp_factor) { + // Lerp factor is out of range, meaning that while an infinite line cast by + // the cursor would intersect the sprite, the sprite is not between the + // camera's near and far planes + return None; + } + // Otherwise we can interpolate the xy of the start and end positions by the + // lerp factor to get the cursor position in sprite space! + let cursor_pos_sprite = cursor_start_sprite + .lerp(cursor_end_sprite, lerp_factor) + .xy(); + + let is_cursor_in_sprite = rect.contains(cursor_pos_sprite); + + blocked = is_cursor_in_sprite + && pickable.map(|p| p.should_block_lower) != Some(false); + + is_cursor_in_sprite.then(|| { + let hit_pos_world = + sprite_transform.transform_point(cursor_pos_sprite.extend(0.0)); + // Transform point from world to camera space to get the Z distance + let hit_pos_cam = cam_transform + .affine() + .inverse() + .transform_point3(hit_pos_world); + // HitData requires a depth as calculated from the camera's near clipping plane + let depth = -cam_ortho.near - hit_pos_cam.z; + ( + entity, + HitData::new( + cam_entity, + depth, + Some(hit_pos_world), + Some(*sprite_transform.back()), + ), + ) }) - .or_else(|| images.get(&sprite.image).map(|f| f.size().as_vec2())) - else { - return None; - }; - - let anchor = sprite.anchor.as_vec(); - - let center = -anchor * extents; - let rect = Rect::from_center_half_size(center, extents / 2.0); - - // Transform cursor line segment to sprite coordinate system - let world_to_sprite = sprite_transform.affine().inverse(); - let cursor_start_sprite = world_to_sprite.transform_point3(cursor_ray_world.origin); - let cursor_end_sprite = world_to_sprite.transform_point3(cursor_ray_end); - - // Find where the cursor segment intersects the plane Z=0 (which is the sprite's - // plane in sprite-local space). It may not intersect if, for example, we're - // viewing the sprite side-on - if cursor_start_sprite.z == cursor_end_sprite.z { - // Cursor ray is parallel to the sprite and misses it - return None; - } - let lerp_factor = - f32::inverse_lerp(cursor_start_sprite.z, cursor_end_sprite.z, 0.0); - if !(0.0..=1.0).contains(&lerp_factor) { - // Lerp factor is out of range, meaning that while an infinite line cast by - // the cursor would intersect the sprite, the sprite is not between the - // camera's near and far planes - return None; - } - // Otherwise we can interpolate the xy of the start and end positions by the - // lerp factor to get the cursor position in sprite space! - let cursor_pos_sprite = cursor_start_sprite - .lerp(cursor_end_sprite, lerp_factor) - .xy(); - - let is_cursor_in_sprite = rect.contains(cursor_pos_sprite); - - blocked = - is_cursor_in_sprite && pickable.map(|p| p.should_block_lower) != Some(false); - - is_cursor_in_sprite.then(|| { - let hit_pos_world = - sprite_transform.transform_point(cursor_pos_sprite.extend(0.0)); - // Transform point from world to camera space to get the Z distance - let hit_pos_cam = cam_transform - .affine() - .inverse() - .transform_point3(hit_pos_world); - // HitData requires a depth as calculated from the camera's near clipping plane - let depth = -cam_ortho.near - hit_pos_cam.z; - ( - entity, - HitData::new( - cam_entity, - depth, - Some(hit_pos_world), - Some(*sprite_transform.back()), - ), - ) - }) - }) + }, + ) .collect(); let order = camera.order as f32; diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 26eaafbf7c4a3..1efa866ec7e64 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -375,12 +375,14 @@ pub fn extract_sprites( &ViewVisibility, &Sprite, &GlobalTransform, + &Handle, + Option<&TextureAtlas>, Option<&ComputedTextureSlices>, )>, >, ) { extracted_sprites.sprites.clear(); - for (entity, view_visibility, sprite, transform, slices) in sprite_query.iter() { + for (entity, view_visibility, sprite, transform, handle, sheet, slices) in sprite_query.iter() { if !view_visibility.get() { continue; } @@ -388,14 +390,12 @@ pub fn extract_sprites( if let Some(slices) = slices { extracted_sprites.sprites.extend( slices - .extract_sprites(transform, entity, sprite, &sprite.image) + .extract_sprites(transform, entity, sprite, handle) .map(|e| (commands.spawn_empty().id(), e)), ); } else { - let atlas_rect = sprite - .atlas - .as_ref() - .and_then(|s| s.texture_rect(&texture_atlases).map(|r| r.as_rect())); + let atlas_rect = + sheet.and_then(|s| s.texture_rect(&texture_atlases).map(|r| r.as_rect())); let rect = match (atlas_rect, sprite.rect) { (None, None) => None, (None, Some(sprite_rect)) => Some(sprite_rect), @@ -419,7 +419,7 @@ pub fn extract_sprites( custom_size: sprite.custom_size, flip_x: sprite.flip_x, flip_y: sprite.flip_y, - image_handle_id: sprite.image.id(), + image_handle_id: handle.id(), anchor: sprite.anchor.as_vec(), original_entity: None, }, diff --git a/crates/bevy_sprite/src/sprite.rs b/crates/bevy_sprite/src/sprite.rs index 97e6f6f835988..13e37ee56cadd 100644 --- a/crates/bevy_sprite/src/sprite.rs +++ b/crates/bevy_sprite/src/sprite.rs @@ -1,22 +1,16 @@ -use bevy_asset::Handle; use bevy_color::Color; use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_math::{Rect, Vec2}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; -use bevy_render::{texture::Image, view::Visibility}; -use bevy_transform::components::Transform; -use crate::{TextureAtlas, TextureSlicer}; +use crate::TextureSlicer; -/// Describes a sprite to be rendered to a 2D camera +/// Specifies the rendering properties of a sprite. +/// +/// This is commonly used as a component within [`SpriteBundle`](crate::bundle::SpriteBundle). #[derive(Component, Debug, Default, Clone, Reflect)] -#[require(Transform, Visibility)] #[reflect(Component, Default, Debug)] pub struct Sprite { - /// The image used to render the sprite - pub image: Handle, - /// The (optional) texture atlas used to render the sprite - pub atlas: Option, /// The sprite's color tint pub color: Color, /// Flip the sprite along the `X` axis @@ -44,32 +38,6 @@ impl Sprite { ..Default::default() } } - - /// Create a sprite from an image - pub fn from_image(image: Handle) -> Self { - Self { - image, - ..Default::default() - } - } - - /// Create a sprite from an image, with an associated texture atlas - pub fn from_atlas_image(image: Handle, atlas: TextureAtlas) -> Self { - Self { - image, - atlas: Some(atlas), - ..Default::default() - } - } - - /// Create a sprite from a solid color - pub fn from_color(color: impl Into, size: Vec2) -> Self { - Self { - color: color.into(), - custom_size: Some(size), - ..Default::default() - } - } } /// Controls how the image is altered when scaled. diff --git a/crates/bevy_sprite/src/texture_slice/computed_slices.rs b/crates/bevy_sprite/src/texture_slice/computed_slices.rs index 626e3bc42c233..c49987e1bb93a 100644 --- a/crates/bevy_sprite/src/texture_slice/computed_slices.rs +++ b/crates/bevy_sprite/src/texture_slice/computed_slices.rs @@ -139,7 +139,13 @@ pub(crate) fn compute_slices_on_asset_event( mut events: EventReader>, images: Res>, atlas_layouts: Res>, - sprites: Query<(Entity, &ImageScaleMode, &Sprite)>, + sprites: Query<( + Entity, + &ImageScaleMode, + &Sprite, + &Handle, + Option<&TextureAtlas>, + )>, ) { // We store the asset ids of added/modified image assets let added_handles: HashSet<_> = events @@ -153,16 +159,16 @@ pub(crate) fn compute_slices_on_asset_event( return; } // We recompute the sprite slices for sprite entities with a matching asset handle id - for (entity, scale_mode, sprite) in &sprites { - if !added_handles.contains(&sprite.image.id()) { + for (entity, scale_mode, sprite, image_handle, atlas) in &sprites { + if !added_handles.contains(&image_handle.id()) { continue; } if let Some(slices) = compute_sprite_slices( sprite, scale_mode, - &sprite.image, + image_handle, &images, - sprite.atlas.as_ref(), + atlas, &atlas_layouts, ) { commands.entity(entity).insert(slices); @@ -177,17 +183,28 @@ pub(crate) fn compute_slices_on_sprite_change( images: Res>, atlas_layouts: Res>, changed_sprites: Query< - (Entity, &ImageScaleMode, &Sprite), - Or<(Changed, Changed)>, + ( + Entity, + &ImageScaleMode, + &Sprite, + &Handle, + Option<&TextureAtlas>, + ), + Or<( + Changed, + Changed>, + Changed, + Changed, + )>, >, ) { - for (entity, scale_mode, sprite) in &changed_sprites { + for (entity, scale_mode, sprite, image_handle, atlas) in &changed_sprites { if let Some(slices) = compute_sprite_slices( sprite, scale_mode, - &sprite.image, + image_handle, &images, - sprite.atlas.as_ref(), + atlas, &atlas_layouts, ) { commands.entity(entity).insert(slices); From fdb3277d228d312fecf9cdf339df32ae5fd1bc68 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Fri, 27 Sep 2024 22:38:54 -0700 Subject: [PATCH 05/18] migrate bevy_sprite --- crates/bevy_sprite/src/bundle.rs | 5 + crates/bevy_sprite/src/lib.rs | 16 +- crates/bevy_sprite/src/picking_backend.rs | 155 +++++++++--------- crates/bevy_sprite/src/render/mod.rs | 15 +- crates/bevy_sprite/src/sprite.rs | 40 ++++- .../src/texture_slice/computed_slices.rs | 37 ++--- 6 files changed, 140 insertions(+), 128 deletions(-) diff --git a/crates/bevy_sprite/src/bundle.rs b/crates/bevy_sprite/src/bundle.rs index df9e99e622dee..de5d6d2cd58e3 100644 --- a/crates/bevy_sprite/src/bundle.rs +++ b/crates/bevy_sprite/src/bundle.rs @@ -1,3 +1,4 @@ +#![expect(deprecated)] use crate::Sprite; use bevy_asset::Handle; use bevy_ecs::bundle::Bundle; @@ -16,6 +17,10 @@ use bevy_transform::components::{GlobalTransform, Transform}; /// - [`ImageScaleMode`](crate::ImageScaleMode) to enable either slicing or tiling of the texture /// - [`TextureAtlas`](crate::TextureAtlas) to draw a specific section of the texture #[derive(Bundle, Clone, Debug, Default)] +#[deprecated( + since = "0.15.0", + note = "Use the `Sprite` component instead. Inserting it will now also insert `Transform` and `Visibility` automatically." +)] pub struct SpriteBundle { /// Specifies the rendering properties of the sprite, such as color tint and flip. pub sprite: Sprite, diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index a5d645e001873..9de407dca8525 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -25,6 +25,7 @@ mod texture_slice; /// The sprite prelude. /// /// This includes the most common types in this crate, re-exported for your convenience. +#[expect(deprecated)] pub mod prelude { #[doc(hidden)] pub use crate::{ @@ -188,9 +189,9 @@ pub fn calculate_bounds_2d( atlases: Res>, meshes_without_aabb: Query<(Entity, &Mesh2dHandle), (Without, Without)>, sprites_to_recalculate_aabb: Query< - (Entity, &Sprite, &Handle, Option<&TextureAtlas>), + (Entity, &Sprite), ( - Or<(Without, Changed, Changed)>, + Or<(Without, Changed)>, Without, ), >, @@ -202,13 +203,13 @@ pub fn calculate_bounds_2d( } } } - for (entity, sprite, texture_handle, atlas) in &sprites_to_recalculate_aabb { + for (entity, sprite) in &sprites_to_recalculate_aabb { if let Some(size) = sprite .custom_size .or_else(|| sprite.rect.map(|rect| rect.size())) - .or_else(|| match atlas { + .or_else(|| match &sprite.atlas { // We default to the texture size for regular sprites - None => images.get(texture_handle).map(Image::size_f32), + None => images.get(&sprite.image).map(Image::size_f32), // We default to the drawn rect for atlas sprites Some(atlas) => atlas .texture_rect(&atlases) @@ -262,10 +263,7 @@ mod test { app.add_systems(Update, calculate_bounds_2d); // Add entities - let entity = app - .world_mut() - .spawn((Sprite::default(), image_handle)) - .id(); + let entity = app.world_mut().spawn(Sprite::from_image(image_handle)).id(); // Verify that the entity does not have an AABB assert!(!app diff --git a/crates/bevy_sprite/src/picking_backend.rs b/crates/bevy_sprite/src/picking_backend.rs index 7199d7875dc6b..66ee7d6b11610 100644 --- a/crates/bevy_sprite/src/picking_backend.rs +++ b/crates/bevy_sprite/src/picking_backend.rs @@ -32,8 +32,6 @@ pub fn sprite_picking( sprite_query: Query<( Entity, &Sprite, - Option<&TextureAtlas>, - &Handle, &GlobalTransform, Option<&Pickable>, &ViewVisibility, @@ -42,9 +40,9 @@ pub fn sprite_picking( ) { let mut sorted_sprites: Vec<_> = sprite_query .iter() - .filter(|x| !x.4.affine().is_nan()) + .filter(|x| !x.2.affine().is_nan()) .collect(); - sorted_sprites.sort_by_key(|x| Reverse(FloatOrd(x.4.translation().z))); + sorted_sprites.sort_by_key(|x| Reverse(FloatOrd(x.2.translation().z))); let primary_window = primary_window.get_single().ok(); @@ -77,82 +75,79 @@ pub fn sprite_picking( .iter() .copied() .filter(|(.., visibility)| visibility.get()) - .filter_map( - |(entity, sprite, atlas, image, sprite_transform, pickable, ..)| { - if blocked { - return None; - } - - // Hit box in sprite coordinate system - let extents = match (sprite.custom_size, atlas) { - (Some(custom_size), _) => custom_size, - (None, None) => images.get(image)?.size().as_vec2(), - (None, Some(atlas)) => texture_atlas_layout - .get(&atlas.layout) - .and_then(|layout| layout.textures.get(atlas.index)) - // Dropped atlas layouts and indexes out of bounds are rendered as a sprite - .map_or(images.get(image)?.size().as_vec2(), |rect| { - rect.size().as_vec2() - }), - }; - let anchor = sprite.anchor.as_vec(); - let center = -anchor * extents; - let rect = Rect::from_center_half_size(center, extents / 2.0); - - // Transform cursor line segment to sprite coordinate system - let world_to_sprite = sprite_transform.affine().inverse(); - let cursor_start_sprite = - world_to_sprite.transform_point3(cursor_ray_world.origin); - let cursor_end_sprite = world_to_sprite.transform_point3(cursor_ray_end); - - // Find where the cursor segment intersects the plane Z=0 (which is the sprite's - // plane in sprite-local space). It may not intersect if, for example, we're - // viewing the sprite side-on - if cursor_start_sprite.z == cursor_end_sprite.z { - // Cursor ray is parallel to the sprite and misses it - return None; - } - let lerp_factor = - f32::inverse_lerp(cursor_start_sprite.z, cursor_end_sprite.z, 0.0); - if !(0.0..=1.0).contains(&lerp_factor) { - // Lerp factor is out of range, meaning that while an infinite line cast by - // the cursor would intersect the sprite, the sprite is not between the - // camera's near and far planes - return None; - } - // Otherwise we can interpolate the xy of the start and end positions by the - // lerp factor to get the cursor position in sprite space! - let cursor_pos_sprite = cursor_start_sprite - .lerp(cursor_end_sprite, lerp_factor) - .xy(); - - let is_cursor_in_sprite = rect.contains(cursor_pos_sprite); - - blocked = is_cursor_in_sprite - && pickable.map(|p| p.should_block_lower) != Some(false); - - is_cursor_in_sprite.then(|| { - let hit_pos_world = - sprite_transform.transform_point(cursor_pos_sprite.extend(0.0)); - // Transform point from world to camera space to get the Z distance - let hit_pos_cam = cam_transform - .affine() - .inverse() - .transform_point3(hit_pos_world); - // HitData requires a depth as calculated from the camera's near clipping plane - let depth = -cam_ortho.near - hit_pos_cam.z; - ( - entity, - HitData::new( - cam_entity, - depth, - Some(hit_pos_world), - Some(*sprite_transform.back()), - ), - ) - }) - }, - ) + .filter_map(|(entity, sprite, sprite_transform, pickable, ..)| { + if blocked { + return None; + } + + // Hit box in sprite coordinate system + let extents = match (sprite.custom_size, &sprite.atlas) { + (Some(custom_size), _) => custom_size, + (None, None) => images.get(&sprite.image)?.size().as_vec2(), + (None, Some(atlas)) => texture_atlas_layout + .get(&atlas.layout) + .and_then(|layout| layout.textures.get(atlas.index)) + // Dropped atlas layouts and indexes out of bounds are rendered as a sprite + .map_or(images.get(&sprite.image)?.size().as_vec2(), |rect| { + rect.size().as_vec2() + }), + }; + let anchor = sprite.anchor.as_vec(); + let center = -anchor * extents; + let rect = Rect::from_center_half_size(center, extents / 2.0); + + // Transform cursor line segment to sprite coordinate system + let world_to_sprite = sprite_transform.affine().inverse(); + let cursor_start_sprite = world_to_sprite.transform_point3(cursor_ray_world.origin); + let cursor_end_sprite = world_to_sprite.transform_point3(cursor_ray_end); + + // Find where the cursor segment intersects the plane Z=0 (which is the sprite's + // plane in sprite-local space). It may not intersect if, for example, we're + // viewing the sprite side-on + if cursor_start_sprite.z == cursor_end_sprite.z { + // Cursor ray is parallel to the sprite and misses it + return None; + } + let lerp_factor = + f32::inverse_lerp(cursor_start_sprite.z, cursor_end_sprite.z, 0.0); + if !(0.0..=1.0).contains(&lerp_factor) { + // Lerp factor is out of range, meaning that while an infinite line cast by + // the cursor would intersect the sprite, the sprite is not between the + // camera's near and far planes + return None; + } + // Otherwise we can interpolate the xy of the start and end positions by the + // lerp factor to get the cursor position in sprite space! + let cursor_pos_sprite = cursor_start_sprite + .lerp(cursor_end_sprite, lerp_factor) + .xy(); + + let is_cursor_in_sprite = rect.contains(cursor_pos_sprite); + + blocked = + is_cursor_in_sprite && pickable.map(|p| p.should_block_lower) != Some(false); + + is_cursor_in_sprite.then(|| { + let hit_pos_world = + sprite_transform.transform_point(cursor_pos_sprite.extend(0.0)); + // Transform point from world to camera space to get the Z distance + let hit_pos_cam = cam_transform + .affine() + .inverse() + .transform_point3(hit_pos_world); + // HitData requires a depth as calculated from the camera's near clipping plane + let depth = -cam_ortho.near - hit_pos_cam.z; + ( + entity, + HitData::new( + cam_entity, + depth, + Some(hit_pos_world), + Some(*sprite_transform.back()), + ), + ) + }) + }) .collect(); let order = camera.order as f32; diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 63b025d5a6747..4402f30eec589 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -377,15 +377,12 @@ pub fn extract_sprites( &ViewVisibility, &Sprite, &GlobalTransform, - &Handle, - Option<&TextureAtlas>, Option<&ComputedTextureSlices>, )>, >, ) { extracted_sprites.sprites.clear(); - for (original_entity, entity, view_visibility, sprite, transform, handle, sheet, slices) in - sprite_query.iter() + for (original_entity, entity, view_visibility, sprite, transform, slices) in sprite_query.iter() { if !view_visibility.get() { continue; @@ -394,12 +391,14 @@ pub fn extract_sprites( if let Some(slices) = slices { extracted_sprites.sprites.extend( slices - .extract_sprites(transform, original_entity, sprite, handle) + .extract_sprites(transform, original_entity, sprite, &sprite.image) .map(|e| (commands.spawn(TemporaryRenderEntity).id(), e)), ); } else { - let atlas_rect = - sheet.and_then(|s| s.texture_rect(&texture_atlases).map(|r| r.as_rect())); + let atlas_rect = sprite + .atlas + .as_ref() + .and_then(|s| s.texture_rect(&texture_atlases).map(|r| r.as_rect())); let rect = match (atlas_rect, sprite.rect) { (None, None) => None, (None, Some(sprite_rect)) => Some(sprite_rect), @@ -423,7 +422,7 @@ pub fn extract_sprites( custom_size: sprite.custom_size, flip_x: sprite.flip_x, flip_y: sprite.flip_y, - image_handle_id: handle.id(), + image_handle_id: sprite.image.id(), anchor: sprite.anchor.as_vec(), original_entity: Some(original_entity), }, diff --git a/crates/bevy_sprite/src/sprite.rs b/crates/bevy_sprite/src/sprite.rs index 13e37ee56cadd..97e6f6f835988 100644 --- a/crates/bevy_sprite/src/sprite.rs +++ b/crates/bevy_sprite/src/sprite.rs @@ -1,16 +1,22 @@ +use bevy_asset::Handle; use bevy_color::Color; use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_math::{Rect, Vec2}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::{texture::Image, view::Visibility}; +use bevy_transform::components::Transform; -use crate::TextureSlicer; +use crate::{TextureAtlas, TextureSlicer}; -/// Specifies the rendering properties of a sprite. -/// -/// This is commonly used as a component within [`SpriteBundle`](crate::bundle::SpriteBundle). +/// Describes a sprite to be rendered to a 2D camera #[derive(Component, Debug, Default, Clone, Reflect)] +#[require(Transform, Visibility)] #[reflect(Component, Default, Debug)] pub struct Sprite { + /// The image used to render the sprite + pub image: Handle, + /// The (optional) texture atlas used to render the sprite + pub atlas: Option, /// The sprite's color tint pub color: Color, /// Flip the sprite along the `X` axis @@ -38,6 +44,32 @@ impl Sprite { ..Default::default() } } + + /// Create a sprite from an image + pub fn from_image(image: Handle) -> Self { + Self { + image, + ..Default::default() + } + } + + /// Create a sprite from an image, with an associated texture atlas + pub fn from_atlas_image(image: Handle, atlas: TextureAtlas) -> Self { + Self { + image, + atlas: Some(atlas), + ..Default::default() + } + } + + /// Create a sprite from a solid color + pub fn from_color(color: impl Into, size: Vec2) -> Self { + Self { + color: color.into(), + custom_size: Some(size), + ..Default::default() + } + } } /// Controls how the image is altered when scaled. diff --git a/crates/bevy_sprite/src/texture_slice/computed_slices.rs b/crates/bevy_sprite/src/texture_slice/computed_slices.rs index c49987e1bb93a..626e3bc42c233 100644 --- a/crates/bevy_sprite/src/texture_slice/computed_slices.rs +++ b/crates/bevy_sprite/src/texture_slice/computed_slices.rs @@ -139,13 +139,7 @@ pub(crate) fn compute_slices_on_asset_event( mut events: EventReader>, images: Res>, atlas_layouts: Res>, - sprites: Query<( - Entity, - &ImageScaleMode, - &Sprite, - &Handle, - Option<&TextureAtlas>, - )>, + sprites: Query<(Entity, &ImageScaleMode, &Sprite)>, ) { // We store the asset ids of added/modified image assets let added_handles: HashSet<_> = events @@ -159,16 +153,16 @@ pub(crate) fn compute_slices_on_asset_event( return; } // We recompute the sprite slices for sprite entities with a matching asset handle id - for (entity, scale_mode, sprite, image_handle, atlas) in &sprites { - if !added_handles.contains(&image_handle.id()) { + for (entity, scale_mode, sprite) in &sprites { + if !added_handles.contains(&sprite.image.id()) { continue; } if let Some(slices) = compute_sprite_slices( sprite, scale_mode, - image_handle, + &sprite.image, &images, - atlas, + sprite.atlas.as_ref(), &atlas_layouts, ) { commands.entity(entity).insert(slices); @@ -183,28 +177,17 @@ pub(crate) fn compute_slices_on_sprite_change( images: Res>, atlas_layouts: Res>, changed_sprites: Query< - ( - Entity, - &ImageScaleMode, - &Sprite, - &Handle, - Option<&TextureAtlas>, - ), - Or<( - Changed, - Changed>, - Changed, - Changed, - )>, + (Entity, &ImageScaleMode, &Sprite), + Or<(Changed, Changed)>, >, ) { - for (entity, scale_mode, sprite, image_handle, atlas) in &changed_sprites { + for (entity, scale_mode, sprite) in &changed_sprites { if let Some(slices) = compute_sprite_slices( sprite, scale_mode, - image_handle, + &sprite.image, &images, - atlas, + sprite.atlas.as_ref(), &atlas_layouts, ) { commands.entity(entity).insert(slices); From cf7138059c238e9402d7baea90a0d0731d3529ab Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Fri, 27 Sep 2024 22:39:00 -0700 Subject: [PATCH 06/18] migrate engine bundles --- crates/bevy_ecs/src/bundle.rs | 4 +- .../bevy_sprite/src/texture_atlas_builder.rs | 5 +- examples/2d/bloom_2d.rs | 11 ++-- examples/2d/move_sprite.rs | 7 +-- examples/2d/pixel_grid_snap.rs | 23 ++------ examples/2d/rotation.rs | 33 +++-------- examples/2d/sprite.rs | 7 +-- examples/2d/sprite_animation.rs | 31 +++++----- examples/2d/sprite_flipping.rs | 15 ++--- examples/2d/sprite_sheet.rs | 15 +++-- examples/2d/sprite_slice.rs | 11 ++-- examples/2d/sprite_tile.rs | 5 +- examples/2d/text2d.rs | 26 +++------ examples/2d/texture_atlas.rs | 33 +++++------ examples/2d/transparency_2d.rs | 27 ++++----- examples/animation/color_animation.rs | 20 ++----- examples/asset/alter_sprite.rs | 17 ++---- examples/asset/asset_settings.rs | 43 +++++++------- examples/asset/custom_asset_reader.rs | 5 +- examples/asset/embedded_asset.rs | 5 +- examples/asset/extra_source.rs | 5 +- examples/audio/spatial_audio_2d.rs | 26 +++------ examples/ecs/hierarchy.rs | 31 +++++----- examples/ecs/parallel_query.rs | 7 +-- examples/ecs/removal_detection.rs | 5 +- examples/games/breakout.rs | 58 +++++++------------ examples/games/contributors.rs | 15 ++--- examples/games/desk_toy.rs | 5 +- .../movement/physics_in_fixed_timestep.rs | 7 +-- examples/picking/sprite_picking.rs | 45 ++++++-------- .../shader/compute_shader_game_of_life.rs | 11 ++-- examples/state/computed_states.rs | 5 +- examples/state/custom_transitions.rs | 5 +- examples/state/states.rs | 5 +- examples/state/sub_states.rs | 5 +- examples/stress_tests/bevymark.rs | 8 +-- .../stress_tests/many_animated_sprites.rs | 19 +++--- examples/stress_tests/many_sprites.rs | 19 +++--- examples/time/virtual_time.rs | 26 ++++----- examples/tools/gamepad_viewer.rs | 38 ++++-------- examples/window/transparent_window.rs | 5 +- tests/window/minimising.rs | 12 ++-- tests/window/resizing.rs | 12 ++-- 43 files changed, 261 insertions(+), 456 deletions(-) diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 7a23ac3f11e06..de5b8bd25ec7b 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -80,8 +80,8 @@ use core::{any::TypeId, ptr::NonNull}; /// Additionally, [Tuples](`tuple`) of bundles are also [`Bundle`] (with up to 15 bundles). /// These bundles contain the items of the 'inner' bundles. /// This is a convenient shorthand which is primarily used when spawning entities. -/// For example, spawning an entity using the bundle `(SpriteBundle {...}, PlayerMarker)` -/// will spawn an entity with components required for a 2d sprite, and the `PlayerMarker` component. +/// For example, spawning an entity using the bundle `(SpatialBundle {...}, PlayerMarker)` +/// will spawn an entity with components required for transform and visibility, and the `PlayerMarker` component. /// /// [`unit`], otherwise known as [`()`](`unit`), is a [`Bundle`] containing no components (since it /// can also be considered as the empty tuple). diff --git a/crates/bevy_sprite/src/texture_atlas_builder.rs b/crates/bevy_sprite/src/texture_atlas_builder.rs index ddeea0723804f..a15658d8e3ecc 100644 --- a/crates/bevy_sprite/src/texture_atlas_builder.rs +++ b/crates/bevy_sprite/src/texture_atlas_builder.rs @@ -190,10 +190,7 @@ impl<'a> TextureAtlasBuilder<'a> { /// let texture = textures.add(texture); /// let layout = layouts.add(atlas_layout); /// // Spawn your sprite - /// commands.spawn(( - /// SpriteBundle { texture, ..Default::default() }, - /// TextureAtlas::from(layout), - /// )); + /// commands.spawn(Sprite::from_atlas_image(texture, TextureAtlas::from(layout))); /// } /// ``` /// diff --git a/examples/2d/bloom_2d.rs b/examples/2d/bloom_2d.rs index a603ced251bf9..0c967e864dfe3 100644 --- a/examples/2d/bloom_2d.rs +++ b/examples/2d/bloom_2d.rs @@ -36,13 +36,10 @@ fn setup( )); // Sprite - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/bevy_bird_dark.png"), - sprite: Sprite { - color: Color::srgb(5.0, 5.0, 5.0), // 4. Put something bright in a dark environment to see the effect - custom_size: Some(Vec2::splat(160.0)), - ..default() - }, + commands.spawn(Sprite { + image: asset_server.load("branding/bevy_bird_dark.png"), + color: Color::srgb(5.0, 5.0, 5.0), // 4. Put something bright in a dark environment to see the effect + custom_size: Some(Vec2::splat(160.0)), ..default() }); diff --git a/examples/2d/move_sprite.rs b/examples/2d/move_sprite.rs index 6f0efa53cbe76..8247798c90823 100644 --- a/examples/2d/move_sprite.rs +++ b/examples/2d/move_sprite.rs @@ -19,11 +19,8 @@ enum Direction { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); commands.spawn(( - SpriteBundle { - texture: asset_server.load("branding/icon.png"), - transform: Transform::from_xyz(100., 0., 0.), - ..default() - }, + Sprite::from_image(asset_server.load("branding/icon.png")), + Transform::from_xyz(100., 0., 0.), Direction::Up, )); } diff --git a/examples/2d/pixel_grid_snap.rs b/examples/2d/pixel_grid_snap.rs index 95c7079dddea4..de1bf9904d3f5 100644 --- a/examples/2d/pixel_grid_snap.rs +++ b/examples/2d/pixel_grid_snap.rs @@ -53,22 +53,16 @@ struct Rotate; fn setup_sprite(mut commands: Commands, asset_server: Res) { // the sample sprite that will be rendered to the pixel-perfect canvas commands.spawn(( - SpriteBundle { - texture: asset_server.load("pixel/bevy_pixel_dark.png"), - transform: Transform::from_xyz(-40., 20., 2.), - ..default() - }, + Sprite::from_image(asset_server.load("pixel/bevy_pixel_dark.png")), + Transform::from_xyz(-40., 20., 2.), Rotate, PIXEL_PERFECT_LAYERS, )); // the sample sprite that will be rendered to the high-res "outer world" commands.spawn(( - SpriteBundle { - texture: asset_server.load("pixel/bevy_pixel_light.png"), - transform: Transform::from_xyz(-40., -20., 2.), - ..default() - }, + Sprite::from_image(asset_server.load("pixel/bevy_pixel_light.png")), + Transform::from_xyz(-40., -20., 2.), Rotate, HIGH_RES_LAYERS, )); @@ -138,14 +132,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { )); // spawn the canvas - commands.spawn(( - SpriteBundle { - texture: image_handle, - ..default() - }, - Canvas, - HIGH_RES_LAYERS, - )); + commands.spawn((Sprite::from_image(image_handle), Canvas, HIGH_RES_LAYERS)); // the "outer" camera renders whatever is on `HIGH_RES_LAYERS` to the screen. // here, the canvas and one of the sample sprites will be rendered by this camera diff --git a/examples/2d/rotation.rs b/examples/2d/rotation.rs index 43227ed5027be..031b9479fcc3b 100644 --- a/examples/2d/rotation.rs +++ b/examples/2d/rotation.rs @@ -62,10 +62,7 @@ fn setup(mut commands: Commands, asset_server: Res) { // player controlled ship commands.spawn(( - SpriteBundle { - texture: ship_handle, - ..default() - }, + Sprite::from_image(ship_handle), Player { movement_speed: 500.0, // meters per second rotation_speed: f32::to_radians(360.0), // degrees per second @@ -74,39 +71,27 @@ fn setup(mut commands: Commands, asset_server: Res) { // enemy that snaps to face the player spawns on the bottom and left commands.spawn(( - SpriteBundle { - texture: enemy_a_handle.clone(), - transform: Transform::from_xyz(0.0 - horizontal_margin, 0.0, 0.0), - ..default() - }, + Sprite::from_image(enemy_a_handle.clone()), + Transform::from_xyz(0.0 - horizontal_margin, 0.0, 0.0), SnapToPlayer, )); commands.spawn(( - SpriteBundle { - texture: enemy_a_handle, - transform: Transform::from_xyz(0.0, 0.0 - vertical_margin, 0.0), - ..default() - }, + Sprite::from_image(enemy_a_handle), + Transform::from_xyz(0.0, 0.0 - vertical_margin, 0.0), SnapToPlayer, )); // enemy that rotates to face the player enemy spawns on the top and right commands.spawn(( - SpriteBundle { - texture: enemy_b_handle.clone(), - transform: Transform::from_xyz(0.0 + horizontal_margin, 0.0, 0.0), - ..default() - }, + Sprite::from_image(enemy_b_handle.clone()), + Transform::from_xyz(0.0 + horizontal_margin, 0.0, 0.0), RotateToPlayer { rotation_speed: f32::to_radians(45.0), // degrees per second }, )); commands.spawn(( - SpriteBundle { - texture: enemy_b_handle, - transform: Transform::from_xyz(0.0, 0.0 + vertical_margin, 0.0), - ..default() - }, + Sprite::from_image(enemy_b_handle), + Transform::from_xyz(0.0, 0.0 + vertical_margin, 0.0), RotateToPlayer { rotation_speed: f32::to_radians(90.0), // degrees per second }, diff --git a/examples/2d/sprite.rs b/examples/2d/sprite.rs index 5fdf172a5be19..64b42c484ba41 100644 --- a/examples/2d/sprite.rs +++ b/examples/2d/sprite.rs @@ -11,8 +11,7 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/bevy_bird_dark.png"), - ..default() - }); + commands.spawn(Sprite::from_image( + asset_server.load("branding/bevy_bird_dark.png"), + )); } diff --git a/examples/2d/sprite_animation.rs b/examples/2d/sprite_animation.rs index 74d3a2bf43e8f..9c9c6d6f66c01 100644 --- a/examples/2d/sprite_animation.rs +++ b/examples/2d/sprite_animation.rs @@ -104,16 +104,15 @@ fn setup( // create the first (left-hand) sprite commands.spawn(( - SpriteBundle { - transform: Transform::from_scale(Vec3::splat(6.0)) - .with_translation(Vec3::new(-50.0, 0.0, 0.0)), - texture: texture.clone(), + Sprite { + image: texture.clone(), + atlas: Some(TextureAtlas { + layout: texture_atlas_layout.clone(), + index: animation_config_1.first_sprite_index, + }), ..default() }, - TextureAtlas { - layout: texture_atlas_layout.clone(), - index: animation_config_1.first_sprite_index, - }, + Transform::from_scale(Vec3::splat(6.0)).with_translation(Vec3::new(-50.0, 0.0, 0.0)), LeftSprite, animation_config_1, )); @@ -123,15 +122,13 @@ fn setup( // create the second (right-hand) sprite commands.spawn(( - SpriteBundle { - transform: Transform::from_scale(Vec3::splat(6.0)) - .with_translation(Vec3::new(50.0, 0.0, 0.0)), - texture: texture.clone(), - ..default() - }, - TextureAtlas { - layout: texture_atlas_layout.clone(), - index: animation_config_2.first_sprite_index, + Sprite { + image: texture.clone(), + atlas: Some(TextureAtlas { + layout: texture_atlas_layout.clone(), + index: animation_config_2.first_sprite_index, + }), + ..Default::default() }, RightSprite, animation_config_2, diff --git a/examples/2d/sprite_flipping.rs b/examples/2d/sprite_flipping.rs index 33ac914995a99..ca169574a1dff 100644 --- a/examples/2d/sprite_flipping.rs +++ b/examples/2d/sprite_flipping.rs @@ -11,15 +11,12 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/bevy_bird_dark.png"), - sprite: Sprite { - // Flip the logo to the left - flip_x: true, - // And don't flip it upside-down ( the default ) - flip_y: false, - ..default() - }, + commands.spawn(Sprite { + image: asset_server.load("branding/bevy_bird_dark.png"), + // Flip the logo to the left + flip_x: true, + // And don't flip it upside-down ( the default ) + flip_y: false, ..default() }); } diff --git a/examples/2d/sprite_sheet.rs b/examples/2d/sprite_sheet.rs index c1c55914b78f3..b1f1051d9a0b2 100644 --- a/examples/2d/sprite_sheet.rs +++ b/examples/2d/sprite_sheet.rs @@ -48,15 +48,14 @@ fn setup( let animation_indices = AnimationIndices { first: 1, last: 6 }; commands.spawn(Camera2dBundle::default()); commands.spawn(( - SpriteBundle { - transform: Transform::from_scale(Vec3::splat(6.0)), + Sprite::from_atlas_image( texture, - ..default() - }, - TextureAtlas { - layout: texture_atlas_layout, - index: animation_indices.first, - }, + TextureAtlas { + layout: texture_atlas_layout, + index: animation_indices.first, + }, + ), + Transform::from_scale(Vec3::splat(6.0)), animation_indices, AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)), )); diff --git a/examples/2d/sprite_slice.rs b/examples/2d/sprite_slice.rs index 9782710326995..cd4dd312f1c93 100644 --- a/examples/2d/sprite_slice.rs +++ b/examples/2d/sprite_slice.rs @@ -73,15 +73,14 @@ fn spawn_sprites( for (label, text_style, size, scale_mode) in cases { position.x += 0.5 * size.x; - let mut cmd = commands.spawn(SpriteBundle { - transform: Transform::from_translation(position), - texture: texture_handle.clone(), - sprite: Sprite { + let mut cmd = commands.spawn(( + Sprite { + image: texture_handle.clone(), custom_size: Some(size), ..default() }, - ..default() - }); + Transform::from_translation(position), + )); if let Some(scale_mode) = scale_mode { cmd = cmd.insert(scale_mode); } diff --git a/examples/2d/sprite_tile.rs b/examples/2d/sprite_tile.rs index c9b0fe5ecb811..7c2515acd8d47 100644 --- a/examples/2d/sprite_tile.rs +++ b/examples/2d/sprite_tile.rs @@ -27,10 +27,7 @@ fn setup(mut commands: Commands, asset_server: Res) { speed: 50.0, }); commands.spawn(( - SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }, + Sprite::from_image(asset_server.load("branding/icon.png")), ImageScaleMode::Tiled { tile_x: true, tile_y: true, diff --git a/examples/2d/text2d.rs b/examples/2d/text2d.rs index f69abc3bed3c3..ad9ddcd401a48 100644 --- a/examples/2d/text2d.rs +++ b/examples/2d/text2d.rs @@ -79,15 +79,10 @@ fn setup(mut commands: Commands, asset_server: Res) { let box_size = Vec2::new(300.0, 200.0); let box_position = Vec2::new(0.0, -250.0); commands - .spawn(SpriteBundle { - sprite: Sprite { - color: Color::srgb(0.25, 0.25, 0.75), - custom_size: Some(Vec2::new(box_size.x, box_size.y)), - ..default() - }, - transform: Transform::from_translation(box_position.extend(0.0)), - ..default() - }) + .spawn(( + Sprite::from_color(Color::srgb(0.25, 0.25, 0.75), box_size), + Transform::from_translation(box_position.extend(0.0)), + )) .with_children(|builder| { builder.spawn(Text2dBundle { text: Text { @@ -110,15 +105,10 @@ fn setup(mut commands: Commands, asset_server: Res) { let other_box_size = Vec2::new(300.0, 200.0); let other_box_position = Vec2::new(320.0, -250.0); commands - .spawn(SpriteBundle { - sprite: Sprite { - color: Color::srgb(0.20, 0.3, 0.70), - custom_size: Some(Vec2::new(other_box_size.x, other_box_size.y)), - ..default() - }, - transform: Transform::from_translation(other_box_position.extend(0.0)), - ..default() - }) + .spawn(( + Sprite::from_color(Color::srgb(0.20, 0.3, 0.70), other_box_size), + Transform::from_translation(other_box_position.extend(0.0)), + )) .with_children(|builder| { builder.spawn(Text2dBundle { text: Text { diff --git a/examples/2d/texture_atlas.rs b/examples/2d/texture_atlas.rs index d910f0ae9d230..0049f78f3fc96 100644 --- a/examples/2d/texture_atlas.rs +++ b/examples/2d/texture_atlas.rs @@ -99,26 +99,24 @@ fn setup( // padded textures are to the right, unpadded to the left // draw unpadded texture atlas - commands.spawn(SpriteBundle { - texture: linear_texture.clone(), - transform: Transform { + commands.spawn(( + Sprite::from_image(linear_texture.clone()), + Transform { translation: Vec3::new(-250.0, -130.0, 0.0), scale: Vec3::splat(0.8), ..default() }, - ..default() - }); + )); // draw padded texture atlas - commands.spawn(SpriteBundle { - texture: linear_padded_texture.clone(), - transform: Transform { + commands.spawn(( + Sprite::from_image(linear_padded_texture.clone()), + Transform { translation: Vec3::new(250.0, -130.0, 0.0), scale: Vec3::splat(0.8), ..default() }, - ..default() - }); + )); let font = asset_server.load("fonts/FiraSans-Bold.ttf"); @@ -260,16 +258,15 @@ fn create_sprite_from_atlas( vendor_handle: &Handle, ) { commands.spawn(( - SpriteBundle { - transform: Transform { - translation: Vec3::new(translation.0, translation.1, translation.2), - scale: Vec3::splat(3.0), - ..default() - }, - texture: atlas_texture, + Transform { + translation: Vec3::new(translation.0, translation.1, translation.2), + scale: Vec3::splat(3.0), ..default() }, - atlas_sources.handle(atlas_handle, vendor_handle).unwrap(), + Sprite::from_atlas_image( + atlas_texture, + atlas_sources.handle(atlas_handle, vendor_handle).unwrap(), + ), )); } diff --git a/examples/2d/transparency_2d.rs b/examples/2d/transparency_2d.rs index 069b73098e688..5ce9da7c9da70 100644 --- a/examples/2d/transparency_2d.rs +++ b/examples/2d/transparency_2d.rs @@ -15,27 +15,22 @@ fn setup(mut commands: Commands, asset_server: Res) { let sprite_handle = asset_server.load("branding/icon.png"); - commands.spawn(SpriteBundle { - texture: sprite_handle.clone(), - ..default() - }); - commands.spawn(SpriteBundle { - sprite: Sprite { + commands.spawn(Sprite::from_image(sprite_handle.clone())); + commands.spawn(( + Sprite { + image: sprite_handle.clone(), // Alpha channel of the color controls transparency. color: Color::srgba(0.0, 0.0, 1.0, 0.7), ..default() }, - texture: sprite_handle.clone(), - transform: Transform::from_xyz(100.0, 0.0, 0.0), - ..default() - }); - commands.spawn(SpriteBundle { - sprite: Sprite { + Transform::from_xyz(100.0, 0.0, 0.0), + )); + commands.spawn(( + Sprite { + image: sprite_handle, color: Color::srgba(0.0, 1.0, 0.0, 0.3), ..default() }, - texture: sprite_handle, - transform: Transform::from_xyz(200.0, 0.0, 0.0), - ..default() - }); + Transform::from_xyz(200.0, 0.0, 0.0), + )); } diff --git a/examples/animation/color_animation.rs b/examples/animation/color_animation.rs index e1b364e39b5e8..eb1f8e06bb055 100644 --- a/examples/animation/color_animation.rs +++ b/examples/animation/color_animation.rs @@ -70,28 +70,16 @@ fn setup(mut commands: Commands) { fn spawn_curve_sprite(commands: &mut Commands, y: f32, points: [T; 4]) { commands.spawn(( - SpriteBundle { - transform: Transform::from_xyz(0., y, 0.), - sprite: Sprite { - custom_size: Some(Vec2::new(75., 75.)), - ..Default::default() - }, - ..Default::default() - }, + Sprite::sized(Vec2::new(75., 75.)), + Transform::from_xyz(0., y, 0.), Curve(CubicBezier::new([points]).to_curve().unwrap()), )); } fn spawn_mixed_sprite(commands: &mut Commands, y: f32, colors: [T; 4]) { commands.spawn(( - SpriteBundle { - transform: Transform::from_xyz(0., y, 0.), - sprite: Sprite { - custom_size: Some(Vec2::new(75., 75.)), - ..Default::default() - }, - ..Default::default() - }, + Transform::from_xyz(0., y, 0.), + Sprite::sized(Vec2::new(75., 75.)), Mixed(colors), )); } diff --git a/examples/asset/alter_sprite.rs b/examples/asset/alter_sprite.rs index 7725ad36eb8f2..e9f02ec01c223 100644 --- a/examples/asset/alter_sprite.rs +++ b/examples/asset/alter_sprite.rs @@ -75,23 +75,16 @@ fn setup(mut commands: Commands, asset_server: Res) { // This marker component ensures we can easily find either of the Birds by using With and // Without query filters. Left, - SpriteBundle { - texture: texture_left, - transform: Transform::from_xyz(-200.0, 0.0, 0.0), - ..default() - }, + Sprite::from_image(texture_left), + Transform::from_xyz(-200.0, 0.0, 0.0), bird_left, )); commands.spawn(( Name::new("Bird Right"), - SpriteBundle { - // In contrast to the above, here we rely on the default `RenderAssetUsages` loader - // setting. - texture: asset_server.load(bird_right.get_texture_path()), - transform: Transform::from_xyz(200.0, 0.0, 0.0), - ..default() - }, + // In contrast to the above, here we rely on the default `RenderAssetUsages` loader setting + Sprite::from_image(asset_server.load(bird_right.get_texture_path())), + Transform::from_xyz(200.0, 0.0, 0.0), bird_right, )); } diff --git a/examples/asset/asset_settings.rs b/examples/asset/asset_settings.rs index d45393b3afa51..5462d3b5eedf4 100644 --- a/examples/asset/asset_settings.rs +++ b/examples/asset/asset_settings.rs @@ -23,15 +23,14 @@ fn setup(mut commands: Commands, asset_server: Res) { // If you are using a very small image and rendering it larger like seen here, the default linear filtering will result in a blurry image. // Useful note: The default sampler specified by the ImagePlugin is *not* the same as the default implementation of sampler. This is why // everything uses linear by default but if you look at the default of sampler, it uses nearest. - commands.spawn(SpriteBundle { - texture: asset_server.load("bevy_pixel_dark.png"), - sprite: Sprite { + commands.spawn(( + Sprite { + image: asset_server.load("bevy_pixel_dark.png"), custom_size: Some(Vec2 { x: 160.0, y: 120.0 }), ..Default::default() }, - transform: Transform::from_xyz(-100.0, 0.0, 0.0), - ..Default::default() - }); + Transform::from_xyz(-100.0, 0.0, 0.0), + )); // When a .meta file is added with the same name as the asset and a '.meta' extension // you can (and must) specify all fields of the asset loader's settings for that @@ -42,15 +41,14 @@ fn setup(mut commands: Commands, asset_server: Res) { // A good reference when filling this out is to check out [ImageLoaderSettings::default()] // and follow to the default implementation of each fields type. // https://docs.rs/bevy/latest/bevy/render/texture/struct.ImageLoaderSettings.html# - commands.spawn(SpriteBundle { - texture: asset_server.load("bevy_pixel_dark_with_meta.png"), - sprite: Sprite { + commands.spawn(( + Sprite { + image: asset_server.load("bevy_pixel_dark_with_meta.png"), custom_size: Some(Vec2 { x: 160.0, y: 120.0 }), ..Default::default() }, - transform: Transform::from_xyz(100.0, 0.0, 0.0), - ..Default::default() - }); + Transform::from_xyz(100.0, 0.0, 0.0), + )); // Another option is to use the AssetServers load_with_settings function. // With this you can specify the same settings upon loading your asset with a @@ -62,20 +60,19 @@ fn setup(mut commands: Commands, asset_server: Res) { // settings changes from any loads after the first of the same asset will be ignored. // This is why this one loads a differently named copy of the asset instead of using // same one as without a .meta file. - commands.spawn(SpriteBundle { - texture: asset_server.load_with_settings( - "bevy_pixel_dark_with_settings.png", - |settings: &mut ImageLoaderSettings| { - settings.sampler = ImageSampler::nearest(); - }, - ), - sprite: Sprite { + commands.spawn(( + Sprite { + image: asset_server.load_with_settings( + "bevy_pixel_dark_with_settings.png", + |settings: &mut ImageLoaderSettings| { + settings.sampler = ImageSampler::nearest(); + }, + ), custom_size: Some(Vec2 { x: 160.0, y: 120.0 }), ..Default::default() }, - transform: Transform::from_xyz(0.0, 150.0, 0.0), - ..Default::default() - }); + Transform::from_xyz(0.0, 150.0, 0.0), + )); commands.spawn(Camera2dBundle::default()); } diff --git a/examples/asset/custom_asset_reader.rs b/examples/asset/custom_asset_reader.rs index 7ee270c2c8f32..05073c6639f83 100644 --- a/examples/asset/custom_asset_reader.rs +++ b/examples/asset/custom_asset_reader.rs @@ -61,8 +61,5 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); } diff --git a/examples/asset/embedded_asset.rs b/examples/asset/embedded_asset.rs index b60ec636b3750..3dd0134f228ca 100644 --- a/examples/asset/embedded_asset.rs +++ b/examples/asset/embedded_asset.rs @@ -48,8 +48,5 @@ fn setup(mut commands: Commands, asset_server: Res) { "embedded://embedded_asset/files/bevy_pixel_light.png".into() ); - commands.spawn(SpriteBundle { - texture: asset_server.load(asset_path), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load(asset_path))); } diff --git a/examples/asset/extra_source.rs b/examples/asset/extra_source.rs index 4e0463e4b1c7d..b8f339a90a5e0 100644 --- a/examples/asset/extra_source.rs +++ b/examples/asset/extra_source.rs @@ -42,8 +42,5 @@ fn setup(mut commands: Commands, asset_server: Res) { // path. assert_eq!(asset_path, "example_files://bevy_pixel_light.png".into()); - commands.spawn(SpriteBundle { - texture: asset_server.load(asset_path), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load(asset_path))); } diff --git a/examples/audio/spatial_audio_2d.rs b/examples/audio/spatial_audio_2d.rs index 72c0b77daa328..6c4f302921338 100644 --- a/examples/audio/spatial_audio_2d.rs +++ b/examples/audio/spatial_audio_2d.rs @@ -52,26 +52,16 @@ fn setup( .spawn((SpatialBundle::default(), listener.clone())) .with_children(|parent| { // left ear - parent.spawn(SpriteBundle { - sprite: Sprite { - color: RED.into(), - custom_size: Some(Vec2::splat(20.0)), - ..default() - }, - transform: Transform::from_xyz(-gap / 2.0, 0.0, 0.0), - ..default() - }); + parent.spawn(( + Sprite::from_color(RED, Vec2::splat(20.0)), + Transform::from_xyz(-gap / 2.0, 0.0, 0.0), + )); // right ear - parent.spawn(SpriteBundle { - sprite: Sprite { - color: LIME.into(), - custom_size: Some(Vec2::splat(20.0)), - ..default() - }, - transform: Transform::from_xyz(gap / 2.0, 0.0, 0.0), - ..default() - }); + parent.spawn(( + Sprite::from_color(LIME, Vec2::splat(20.0)), + Transform::from_xyz(gap / 2.0, 0.0, 0.0), + )); }); // example instructions diff --git a/examples/ecs/hierarchy.rs b/examples/ecs/hierarchy.rs index e3356d28e36f5..b95945e15b43f 100644 --- a/examples/ecs/hierarchy.rs +++ b/examples/ecs/hierarchy.rs @@ -18,23 +18,21 @@ fn setup(mut commands: Commands, asset_server: Res) { // Spawn a root entity with no parent let parent = commands - .spawn(SpriteBundle { - transform: Transform::from_scale(Vec3::splat(0.75)), - texture: texture.clone(), - ..default() - }) + .spawn(( + Sprite::from_image(texture.clone()), + Transform::from_scale(Vec3::splat(0.75)), + )) // With that entity as a parent, run a lambda that spawns its children .with_children(|parent| { // parent is a ChildBuilder, which has a similar API to Commands - parent.spawn(SpriteBundle { - transform: Transform::from_xyz(250.0, 0.0, 0.0).with_scale(Vec3::splat(0.75)), - texture: texture.clone(), - sprite: Sprite { + parent.spawn(( + Transform::from_xyz(250.0, 0.0, 0.0).with_scale(Vec3::splat(0.75)), + Sprite { + image: texture.clone(), color: BLUE.into(), ..default() }, - ..default() - }); + )); }) // Store parent entity for next sections .id(); @@ -42,15 +40,14 @@ fn setup(mut commands: Commands, asset_server: Res) { // Another way is to use the add_child function to add children after the parent // entity has already been spawned. let child = commands - .spawn(SpriteBundle { - transform: Transform::from_xyz(0.0, 250.0, 0.0).with_scale(Vec3::splat(0.75)), - texture, - sprite: Sprite { + .spawn(( + Sprite { + image: texture, color: LIME.into(), ..default() }, - ..default() - }) + Transform::from_xyz(0.0, 250.0, 0.0).with_scale(Vec3::splat(0.75)), + )) .id(); // Add child to the parent. diff --git a/examples/ecs/parallel_query.rs b/examples/ecs/parallel_query.rs index adf7f243da83c..0ab683582c462 100644 --- a/examples/ecs/parallel_query.rs +++ b/examples/ecs/parallel_query.rs @@ -16,11 +16,8 @@ fn spawn_system(mut commands: Commands, asset_server: Res) { let mut rng = ChaCha8Rng::seed_from_u64(19878367467713); for _ in 0..128 { commands.spawn(( - SpriteBundle { - texture: texture.clone(), - transform: Transform::from_scale(Vec3::splat(0.1)), - ..default() - }, + Sprite::from_image(texture.clone()), + Transform::from_scale(Vec3::splat(0.1)), Velocity(20.0 * Vec2::new(rng.gen::() - 0.5, rng.gen::() - 0.5)), )); } diff --git a/examples/ecs/removal_detection.rs b/examples/ecs/removal_detection.rs index 462c5d6cbd98f..a1085866546ed 100644 --- a/examples/ecs/removal_detection.rs +++ b/examples/ecs/removal_detection.rs @@ -29,10 +29,7 @@ struct MyComponent; fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); commands.spawn(( - SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }, + Sprite::from_image(asset_server.load("branding/icon.png")), // Add the `Component`. MyComponent, )); diff --git a/examples/games/breakout.rs b/examples/games/breakout.rs index f6f690b1e4c9f..ff4bde4de1527 100644 --- a/examples/games/breakout.rs +++ b/examples/games/breakout.rs @@ -107,7 +107,8 @@ struct CollisionSound(Handle); struct WallBundle { // You can nest bundles inside of other bundles like this // Allowing you to compose their functionality - sprite_bundle: SpriteBundle, + sprite: Sprite, + transform: Transform, collider: Collider, } @@ -154,21 +155,15 @@ impl WallBundle { // making our code easier to read and less prone to bugs when we change the logic fn new(location: WallLocation) -> WallBundle { WallBundle { - sprite_bundle: SpriteBundle { - transform: Transform { - // We need to convert our Vec2 into a Vec3, by giving it a z-coordinate - // This is used to determine the order of our sprites - translation: location.position().extend(0.0), - // The z-scale of 2D objects must always be 1.0, - // or their ordering will be affected in surprising ways. - // See https://github.com/bevyengine/bevy/issues/4149 - scale: location.size().extend(1.0), - ..default() - }, - sprite: Sprite { - color: WALL_COLOR, - ..default() - }, + sprite: Sprite::from_color(WALL_COLOR, Vec2::ONE), + transform: Transform { + // We need to convert our Vec2 into a Vec3, by giving it a z-coordinate + // This is used to determine the order of our sprites + translation: location.position().extend(0.0), + // The z-scale of 2D objects must always be 1.0, + // or their ordering will be affected in surprising ways. + // See https://github.com/bevyengine/bevy/issues/4149 + scale: location.size().extend(1.0), ..default() }, collider: Collider, @@ -201,16 +196,10 @@ fn setup( let paddle_y = BOTTOM_WALL + GAP_BETWEEN_PADDLE_AND_FLOOR; commands.spawn(( - SpriteBundle { - transform: Transform { - translation: Vec3::new(0.0, paddle_y, 0.0), - scale: PADDLE_SIZE.extend(1.0), - ..default() - }, - sprite: Sprite { - color: PADDLE_COLOR, - ..default() - }, + Sprite::from_color(PADDLE_COLOR, Vec2::ONE), + Transform { + translation: Vec3::new(0.0, paddle_y, 0.0), + scale: PADDLE_SIZE.extend(1.0), ..default() }, Paddle, @@ -298,16 +287,13 @@ fn setup( // brick commands.spawn(( - SpriteBundle { - sprite: Sprite { - color: BRICK_COLOR, - ..default() - }, - transform: Transform { - translation: brick_position.extend(0.0), - scale: Vec3::new(BRICK_SIZE.x, BRICK_SIZE.y, 1.0), - ..default() - }, + Sprite { + color: BRICK_COLOR, + ..default() + }, + Transform { + translation: brick_position.extend(0.0), + scale: Vec3::new(BRICK_SIZE.x, BRICK_SIZE.y, 1.0), ..default() }, Brick, diff --git a/examples/games/contributors.rs b/examples/games/contributors.rs index 067f0bdf58b20..cceaad9489e12 100644 --- a/examples/games/contributors.rs +++ b/examples/games/contributors.rs @@ -106,17 +106,14 @@ fn setup_contributor_selection(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); commands.spawn(( Name::new("Player"), - SpriteBundle { - texture: asset_server.load("branding/icon.png"), - transform: Transform::from_scale(Vec3::splat(0.3)), - ..default() - }, + Sprite::from_image(asset_server.load("branding/icon.png")), + Transform::from_scale(Vec3::splat(0.3)), AccumulatedInput::default(), Velocity::default(), PhysicalTranslation::default(), diff --git a/examples/picking/sprite_picking.rs b/examples/picking/sprite_picking.rs index 67abff50b5cf9..8ee5ea0fde159 100644 --- a/examples/picking/sprite_picking.rs +++ b/examples/picking/sprite_picking.rs @@ -32,7 +32,7 @@ fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); let len = 128.0; - let sprite_size = Some(Vec2::splat(len / 2.0)); + let sprite_size = Vec2::splat(len / 2.0); commands .spawn(SpatialBundle::default()) @@ -57,35 +57,29 @@ fn setup(mut commands: Commands, asset_server: Res) { // spawn black square behind sprite to show anchor point commands - .spawn(SpriteBundle { - sprite: Sprite { - custom_size: sprite_size, - color: Color::BLACK, - ..default() - }, - transform: Transform::from_xyz(i * len - len, j * len - len, -1.0), - ..default() - }) + .spawn(( + Sprite::from_color(Color::BLACK, sprite_size), + Transform::from_xyz(i * len - len, j * len - len, -1.0), + )) .observe(recolor_on::>(Color::srgb(0.0, 1.0, 1.0))) .observe(recolor_on::>(Color::BLACK)) .observe(recolor_on::>(Color::srgb(1.0, 1.0, 0.0))) .observe(recolor_on::>(Color::srgb(0.0, 1.0, 1.0))); commands - .spawn(SpriteBundle { - sprite: Sprite { - custom_size: sprite_size, + .spawn(( + Sprite { + image: asset_server.load("branding/bevy_bird_dark.png"), + custom_size: Some(sprite_size), color: Color::srgb(1.0, 0.0, 0.0), anchor: anchor.to_owned(), ..default() }, - texture: asset_server.load("branding/bevy_bird_dark.png"), // 3x3 grid of anchor examples by changing transform - transform: Transform::from_xyz(i * len - len, j * len - len, 0.0) + Transform::from_xyz(i * len - len, j * len - len, 0.0) .with_scale(Vec3::splat(1.0 + (i - 1.0) * 0.2)) .with_rotation(Quat::from_rotation_z((j - 1.0) * 0.2)), - ..default() - }) + )) .observe(recolor_on::>(Color::srgb(0.0, 1.0, 0.0))) .observe(recolor_on::>(Color::srgb(1.0, 0.0, 0.0))) .observe(recolor_on::>(Color::srgb(0.0, 0.0, 1.0))) @@ -131,15 +125,14 @@ fn setup_atlas( let animation_indices = AnimationIndices { first: 1, last: 6 }; commands .spawn(( - TextureAtlas { - layout: texture_atlas_layout_handle, - index: animation_indices.first, - }, - SpriteBundle { - texture: texture_handle, - transform: Transform::from_xyz(300.0, 0.0, 0.0).with_scale(Vec3::splat(6.0)), - ..default() - }, + Sprite::from_atlas_image( + texture_handle, + TextureAtlas { + layout: texture_atlas_layout_handle, + index: animation_indices.first, + }, + ), + Transform::from_xyz(300.0, 0.0, 0.0).with_scale(Vec3::splat(6.0)), animation_indices, AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)), )) diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index 09c072d1a5c86..7f4381376a694 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -67,15 +67,14 @@ fn setup(mut commands: Commands, mut images: ResMut>) { let image0 = images.add(image.clone()); let image1 = images.add(image); - commands.spawn(SpriteBundle { - sprite: Sprite { + commands.spawn(( + Sprite { + image: image0.clone(), custom_size: Some(Vec2::new(SIZE.0 as f32, SIZE.1 as f32)), ..default() }, - texture: image0.clone(), - transform: Transform::from_scale(Vec3::splat(DISPLAY_FACTOR as f32)), - ..default() - }); + Transform::from_scale(Vec3::splat(DISPLAY_FACTOR as f32)), + )); commands.spawn(Camera2dBundle::default()); commands.insert_resource(GameOfLifeImages { diff --git a/examples/state/computed_states.rs b/examples/state/computed_states.rs index 47dd555ab2736..167e2e88d4607 100644 --- a/examples/state/computed_states.rs +++ b/examples/state/computed_states.rs @@ -423,10 +423,7 @@ mod ui { pub fn setup_game(mut commands: Commands, asset_server: Res) { commands.spawn(( StateScoped(InGame), - SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }, + Sprite::from_image(asset_server.load("branding/icon.png")), )); } diff --git a/examples/state/custom_transitions.rs b/examples/state/custom_transitions.rs index 9baa0a76c6155..4cab6c8968593 100644 --- a/examples/state/custom_transitions.rs +++ b/examples/state/custom_transitions.rs @@ -223,10 +223,7 @@ fn setup(mut commands: Commands) { } fn setup_game(mut commands: Commands, asset_server: Res) { - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); info!("Setup game"); } diff --git a/examples/state/states.rs b/examples/state/states.rs index f5a19ea4578fb..d6e13dfc8d756 100644 --- a/examples/state/states.rs +++ b/examples/state/states.rs @@ -120,10 +120,7 @@ fn cleanup_menu(mut commands: Commands, menu_data: Res) { } fn setup_game(mut commands: Commands, asset_server: Res) { - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); } const SPEED: f32 = 100.0; diff --git a/examples/state/sub_states.rs b/examples/state/sub_states.rs index 849bae29214a1..00b8783a90ffe 100644 --- a/examples/state/sub_states.rs +++ b/examples/state/sub_states.rs @@ -198,10 +198,7 @@ mod ui { } pub fn setup_game(mut commands: Commands, asset_server: Res) { - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); } pub fn setup_paused_screen(mut commands: Commands) { diff --git a/examples/stress_tests/bevymark.rs b/examples/stress_tests/bevymark.rs index 97c9115fcf4df..42d2e8929f66f 100644 --- a/examples/stress_tests/bevymark.rs +++ b/examples/stress_tests/bevymark.rs @@ -435,16 +435,16 @@ fn spawn_birds( color }; ( - SpriteBundle { - texture: bird_resources + Sprite { + image: bird_resources .textures .choose(&mut bird_resources.material_rng) .unwrap() .clone(), - transform, - sprite: Sprite { color, ..default() }, + color, ..default() }, + transform, Bird { velocity }, ) }) diff --git a/examples/stress_tests/many_animated_sprites.rs b/examples/stress_tests/many_animated_sprites.rs index 17be46f0ac572..71f8c646e110d 100644 --- a/examples/stress_tests/many_animated_sprites.rs +++ b/examples/stress_tests/many_animated_sprites.rs @@ -82,19 +82,16 @@ fn setup( timer.set_elapsed(Duration::from_secs_f32(rng.gen::())); commands.spawn(( - SpriteBundle { - texture: texture_handle.clone(), - transform: Transform { - translation, - rotation, - scale, - }, - sprite: Sprite { - custom_size: Some(tile_size), - ..default() - }, + Sprite { + image: texture_handle.clone(), + custom_size: Some(tile_size), ..default() }, + Transform { + translation, + rotation, + scale, + }, TextureAtlas::from(texture_atlas_handle.clone()), AnimationTimer(timer), )); diff --git a/examples/stress_tests/many_sprites.rs b/examples/stress_tests/many_sprites.rs index fb08d2942f4b1..daef33f661262 100644 --- a/examples/stress_tests/many_sprites.rs +++ b/examples/stress_tests/many_sprites.rs @@ -81,14 +81,9 @@ fn setup(mut commands: Commands, assets: Res, color_tint: Res()); let scale = Vec3::splat(rng.gen::() * 2.0); - sprites.push(SpriteBundle { - texture: sprite_handle.clone(), - transform: Transform { - translation, - rotation, - scale, - }, - sprite: Sprite { + sprites.push(( + Sprite { + image: sprite_handle.clone(), custom_size: Some(tile_size), color: if color_tint.0 { COLORS[rng.gen_range(0..3)] @@ -97,8 +92,12 @@ fn setup(mut commands: Commands, assets: Res, color_tint: Res, mut time: ResMu // the sprite moving based on real time commands.spawn(( - SpriteBundle { - texture: texture_handle.clone(), - transform: Transform::from_scale(sprite_scale), - ..default() - }, + Sprite::from_image(texture_handle.clone()), + Transform::from_scale(sprite_scale), RealTime, )); // the sprite moving based on virtual time commands.spawn(( - SpriteBundle { - texture: texture_handle, - sprite: Sprite { - color: virtual_color, - ..default() - }, - transform: Transform { - scale: sprite_scale, - translation: Vec3::new(0., -160., 0.), - ..default() - }, + Sprite { + image: texture_handle, + color: virtual_color, + ..Default::default() + }, + Transform { + scale: sprite_scale, + translation: Vec3::new(0., -160., 0.), ..default() }, VirtualTime, diff --git a/examples/tools/gamepad_viewer.rs b/examples/tools/gamepad_viewer.rs index 2689195565b14..2696a3721a0a6 100644 --- a/examples/tools/gamepad_viewer.rs +++ b/examples/tools/gamepad_viewer.rs @@ -283,34 +283,20 @@ fn setup_sticks( }) .with_children(|parent| { // full extent - parent.spawn(SpriteBundle { - sprite: Sprite { - custom_size: Some(Vec2::splat(STICK_BOUNDS_SIZE * 2.)), - color: DEAD_COLOR, - ..default() - }, - ..default() - }); + parent.spawn(Sprite::from_color( + DEAD_COLOR, + Vec2::splat(STICK_BOUNDS_SIZE * 2.), + )); // live zone - parent.spawn(SpriteBundle { - transform: Transform::from_xyz(live_mid, live_mid, 2.), - sprite: Sprite { - custom_size: Some(Vec2::new(live_size, live_size)), - color: LIVE_COLOR, - ..default() - }, - ..default() - }); + parent.spawn(( + Sprite::from_color(LIVE_COLOR, Vec2::splat(live_size)), + Transform::from_xyz(live_mid, live_mid, 2.), + )); // dead zone - parent.spawn(SpriteBundle { - transform: Transform::from_xyz(dead_mid, dead_mid, 3.), - sprite: Sprite { - custom_size: Some(Vec2::new(dead_size, dead_size)), - color: DEAD_COLOR, - ..default() - }, - ..default() - }); + parent.spawn(( + Sprite::from_color(DEAD_COLOR, Vec2::splat(dead_size)), + Transform::from_xyz(dead_mid, dead_mid, 3.), + )); // text let style = TextStyle { font_size: 13., diff --git a/examples/window/transparent_window.rs b/examples/window/transparent_window.rs index f95408ff52fe0..236b8a147f399 100644 --- a/examples/window/transparent_window.rs +++ b/examples/window/transparent_window.rs @@ -30,8 +30,5 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/icon.png"), - ..default() - }); + commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png"))); } diff --git a/tests/window/minimising.rs b/tests/window/minimising.rs index 4517d6cf4d979..13dc2f6752c5c 100644 --- a/tests/window/minimising.rs +++ b/tests/window/minimising.rs @@ -73,12 +73,8 @@ fn setup_2d(mut commands: Commands) { }, ..default() }); - commands.spawn(SpriteBundle { - sprite: Sprite { - color: Color::srgb(0.25, 0.25, 0.75), - custom_size: Some(Vec2::new(50.0, 50.0)), - ..default() - }, - ..default() - }); + commands.spawn(Sprite::from_color( + Color::srgb(0.25, 0.25, 0.75), + Vec2::new(50.0, 50.0), + )); } diff --git a/tests/window/resizing.rs b/tests/window/resizing.rs index df4b1f78f56b2..1009695bc44e1 100644 --- a/tests/window/resizing.rs +++ b/tests/window/resizing.rs @@ -149,12 +149,8 @@ fn setup_2d(mut commands: Commands) { }, ..default() }); - commands.spawn(SpriteBundle { - sprite: Sprite { - color: Color::srgb(0.25, 0.25, 0.75), - custom_size: Some(Vec2::new(50.0, 50.0)), - ..default() - }, - ..default() - }); + commands.spawn(Sprite::from_color( + Color::srgb(0.25, 0.25, 0.75), + Vec2::new(50.0, 50.0), + )); } From b4e09ce5c359fd925e3dc002461935aa09948ad3 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Sun, 6 Oct 2024 17:21:25 -0700 Subject: [PATCH 07/18] sync to render world --- crates/bevy_sprite/src/sprite.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_sprite/src/sprite.rs b/crates/bevy_sprite/src/sprite.rs index 97e6f6f835988..8b32488ec27c2 100644 --- a/crates/bevy_sprite/src/sprite.rs +++ b/crates/bevy_sprite/src/sprite.rs @@ -3,14 +3,14 @@ use bevy_color::Color; use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_math::{Rect, Vec2}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; -use bevy_render::{texture::Image, view::Visibility}; +use bevy_render::{texture::Image, view::Visibility, world_sync::SyncToRenderWorld}; use bevy_transform::components::Transform; use crate::{TextureAtlas, TextureSlicer}; /// Describes a sprite to be rendered to a 2D camera #[derive(Component, Debug, Default, Clone, Reflect)] -#[require(Transform, Visibility)] +#[require(Transform, Visibility, SyncToRenderWorld)] #[reflect(Component, Default, Debug)] pub struct Sprite { /// The image used to render the sprite From eefae1c1703388700f6e1993404f894fc105f8cd Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Sun, 6 Oct 2024 17:58:00 -0700 Subject: [PATCH 08/18] fix ci --- crates/bevy_sprite/src/picking_backend.rs | 2 +- crates/bevy_sprite/src/render/mod.rs | 6 +++--- crates/bevy_sprite/src/sprite.rs | 6 +++--- examples/ecs/fallible_params.rs | 24 +++++++++-------------- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/crates/bevy_sprite/src/picking_backend.rs b/crates/bevy_sprite/src/picking_backend.rs index 66ee7d6b11610..7b970545c45e9 100644 --- a/crates/bevy_sprite/src/picking_backend.rs +++ b/crates/bevy_sprite/src/picking_backend.rs @@ -4,7 +4,7 @@ use core::cmp::Reverse; -use crate::{Sprite, TextureAtlas, TextureAtlasLayout}; +use crate::{Sprite, TextureAtlasLayout}; use bevy_app::prelude::*; use bevy_asset::prelude::*; use bevy_ecs::prelude::*; diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 4402f30eec589..9201c65077197 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -1,10 +1,10 @@ use core::ops::Range; use crate::{ - texture_atlas::{TextureAtlas, TextureAtlasLayout}, - ComputedTextureSlices, Sprite, WithSprite, SPRITE_SHADER_HANDLE, + texture_atlas::TextureAtlasLayout, ComputedTextureSlices, Sprite, WithSprite, + SPRITE_SHADER_HANDLE, }; -use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; +use bevy_asset::{AssetEvent, AssetId, Assets}; use bevy_color::{ColorToComponents, LinearRgba}; use bevy_core_pipeline::{ core_2d::{Transparent2d, CORE_2D_DEPTH_FORMAT}, diff --git a/crates/bevy_sprite/src/sprite.rs b/crates/bevy_sprite/src/sprite.rs index 8b32488ec27c2..c64b1b0d836d4 100644 --- a/crates/bevy_sprite/src/sprite.rs +++ b/crates/bevy_sprite/src/sprite.rs @@ -27,9 +27,9 @@ pub struct Sprite { /// of the sprite's image pub custom_size: Option, /// An optional rectangle representing the region of the sprite's image to render, instead of rendering - /// the full image. This is an easy one-off alternative to using a [`TextureAtlas`](crate::TextureAtlas). + /// the full image. This is an easy one-off alternative to using a [`TextureAtlas`]. /// - /// When used with a [`TextureAtlas`](crate::TextureAtlas), the rect + /// When used with a [`TextureAtlas`], the rect /// is offset by the atlas's minimal (top-left) corner position. pub rect: Option, /// [`Anchor`] point of the sprite in the world @@ -90,7 +90,7 @@ pub enum ImageScaleMode { }, } -/// How a sprite is positioned relative to its [`Transform`](bevy_transform::components::Transform). +/// How a sprite is positioned relative to its [`Transform`]. /// It defaults to `Anchor::Center`. #[derive(Component, Debug, Clone, Copy, PartialEq, Default, Reflect)] #[reflect(Component, Default, Debug, PartialEq)] diff --git a/examples/ecs/fallible_params.rs b/examples/ecs/fallible_params.rs index 6100154c2828e..fda8583e7fa19 100644 --- a/examples/ecs/fallible_params.rs +++ b/examples/ecs/fallible_params.rs @@ -68,15 +68,12 @@ fn setup(mut commands: Commands, asset_server: Res) { rotation_speed: 2.0, min_follow_radius: 50.0, }, - SpriteBundle { - sprite: Sprite { - color: bevy::color::palettes::tailwind::BLUE_800.into(), - ..default() - }, - transform: Transform::from_translation(Vec3::ZERO), - texture, - ..default() + Sprite { + image: texture, + color: bevy::color::palettes::tailwind::BLUE_800.into(), + ..Default::default() }, + Transform::from_translation(Vec3::ZERO), )); } @@ -99,15 +96,12 @@ fn user_input( rotation: rng.gen_range(0.0..std::f32::consts::TAU), rotation_speed: rng.gen_range(0.5..1.5), }, - SpriteBundle { - sprite: Sprite { - color: bevy::color::palettes::tailwind::RED_800.into(), - ..default() - }, - transform: Transform::from_translation(Vec3::ZERO), - texture, + Sprite { + image: texture, + color: bevy::color::palettes::tailwind::RED_800.into(), ..default() }, + Transform::from_translation(Vec3::ZERO), )); } From bc2085c3fdcb592f5beb0d6475c3d7dcac3be4f7 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:37:11 -0700 Subject: [PATCH 09/18] Update bundle.rs --- crates/bevy_ecs/src/bundle.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index f23f6a7322c5b..8dacc98a911d7 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -77,8 +77,6 @@ use core::{any::TypeId, ptr::NonNull}; /// Additionally, [Tuples](`tuple`) of bundles are also [`Bundle`] (with up to 15 bundles). /// These bundles contain the items of the 'inner' bundles. /// This is a convenient shorthand which is primarily used when spawning entities. -/// For example, spawning an entity using the bundle `(SpatialBundle {...}, PlayerMarker)` -/// will spawn an entity with components required for transform and visibility, and the `PlayerMarker` component. /// /// [`unit`], otherwise known as [`()`](`unit`), is a [`Bundle`] containing no components (since it /// can also be considered as the empty tuple). From 5a74e6f1e053cabc1eb4d5681fabadd534950a00 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:45:42 -0700 Subject: [PATCH 10/18] fix more bundles when will it end why does every example use sprites --- examples/2d/cpu_draw.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/2d/cpu_draw.rs b/examples/2d/cpu_draw.rs index 0fa6e81a62bcf..8e9982dbee6f0 100644 --- a/examples/2d/cpu_draw.rs +++ b/examples/2d/cpu_draw.rs @@ -78,11 +78,7 @@ fn setup(mut commands: Commands, mut images: ResMut>) { let handle = images.add(image); // create a sprite entity using our image - commands.spawn(SpriteBundle { - texture: handle.clone(), - ..Default::default() - }); - + commands.spawn(Sprite::from_image(handle.clone())); commands.insert_resource(MyProcGenImage(handle)); } From 8961f0096adf9b2699ce2b2563821a45b78524e8 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:20:19 -0700 Subject: [PATCH 11/18] more bundles it never ends --- examples/animation/easing_functions.rs | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/examples/animation/easing_functions.rs b/examples/animation/easing_functions.rs index 73899a3f1783b..82566cf200d8b 100644 --- a/examples/animation/easing_functions.rs +++ b/examples/animation/easing_functions.rs @@ -79,24 +79,14 @@ fn setup(mut commands: Commands) { SelectedEaseFunction(*function, color), )) .with_children(|p| { - p.spawn(SpriteBundle { - sprite: Sprite { - custom_size: Some(Vec2::new(5.0, 5.0)), - color, - ..default() - }, - transform: Transform::from_xyz(110.0, 15.0, 0.0), - ..default() - }); - p.spawn(SpriteBundle { - sprite: Sprite { - custom_size: Some(Vec2::new(4.0, 4.0)), - color, - ..default() - }, - transform: Transform::from_xyz(0.0, 0.0, 0.0), - ..default() - }); + p.spawn(( + Sprite::from_color(color, Vec2::splat(5.0)), + Transform::from_xyz(110.0, 15.0, 0.0), + )); + p.spawn(( + Sprite::from_color(color, Vec2::splat(4.0)), + Transform::from_xyz(0.0, 0.0, 0.0), + )); }); } } From c37ac2d7ef212e2b61caf30ee7587c6f061e4007 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:43:41 -0700 Subject: [PATCH 12/18] fix asset_decompression --- crates/bevy_sprite/src/sprite.rs | 6 ++++++ examples/asset/asset_decompression.rs | 19 +++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/crates/bevy_sprite/src/sprite.rs b/crates/bevy_sprite/src/sprite.rs index c64b1b0d836d4..99939bbacf391 100644 --- a/crates/bevy_sprite/src/sprite.rs +++ b/crates/bevy_sprite/src/sprite.rs @@ -72,6 +72,12 @@ impl Sprite { } } +impl From> for Sprite { + fn from(image: Handle) -> Self { + Self::from_image(image) + } +} + /// Controls how the image is altered when scaled. #[derive(Component, Debug, Clone, Reflect)] #[reflect(Component, Debug)] diff --git a/examples/asset/asset_decompression.rs b/examples/asset/asset_decompression.rs index 0be3d1d5cb178..a849d2f56276a 100644 --- a/examples/asset/asset_decompression.rs +++ b/examples/asset/asset_decompression.rs @@ -99,25 +99,20 @@ fn main() { .init_asset::() .init_asset_loader::() .add_systems(Startup, setup) - .add_systems(Update, decompress::) + .add_systems(Update, decompress::) .run(); } fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2d); - commands.spawn(( - Compressed:: { - compressed: asset_server.load("data/compressed_image.png.gz"), - ..default() - }, - Sprite::default(), - Transform::default(), - Visibility::default(), - )); + commands.spawn((Compressed:: { + compressed: asset_server.load("data/compressed_image.png.gz"), + ..default() + },)); } -fn decompress( +fn decompress>, A: Asset>( mut commands: Commands, asset_server: Res, mut compressed_assets: ResMut>, @@ -133,6 +128,6 @@ fn decompress( commands .entity(entity) .remove::>() - .insert(asset_server.add(uncompressed)); + .insert(T::from(asset_server.add(uncompressed))); } } From b01fc54f2097e4ed3d0beaef9d11b46c9a4eb075 Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:49:18 -0700 Subject: [PATCH 13/18] cleanup --- crates/bevy_sprite/src/render/mod.rs | 2 +- .../src/texture_slice/computed_slices.rs | 27 ++++--------------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 9201c65077197..6b5ca34dac118 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -391,7 +391,7 @@ pub fn extract_sprites( if let Some(slices) = slices { extracted_sprites.sprites.extend( slices - .extract_sprites(transform, original_entity, sprite, &sprite.image) + .extract_sprites(transform, original_entity, sprite) .map(|e| (commands.spawn(TemporaryRenderEntity).id(), e)), ); } else { diff --git a/crates/bevy_sprite/src/texture_slice/computed_slices.rs b/crates/bevy_sprite/src/texture_slice/computed_slices.rs index 626e3bc42c233..b9931a883c3be 100644 --- a/crates/bevy_sprite/src/texture_slice/computed_slices.rs +++ b/crates/bevy_sprite/src/texture_slice/computed_slices.rs @@ -29,7 +29,6 @@ impl ComputedTextureSlices { transform: &'a GlobalTransform, original_entity: Entity, sprite: &'a Sprite, - handle: &'a Handle, ) -> impl ExactSizeIterator + 'a { let mut flip = Vec2::ONE; let [mut flip_x, mut flip_y] = [false; 2]; @@ -52,7 +51,7 @@ impl ComputedTextureSlices { custom_size: Some(slice.draw_size), flip_x, flip_y, - image_handle_id: handle.id(), + image_handle_id: sprite.image.id(), anchor: Self::redepend_anchor_from_sprite_to_slice(sprite, slice), } }) @@ -88,12 +87,10 @@ impl ComputedTextureSlices { fn compute_sprite_slices( sprite: &Sprite, scale_mode: &ImageScaleMode, - image_handle: &Handle, images: &Assets, - atlas: Option<&TextureAtlas>, atlas_layouts: &Assets, ) -> Option { - let (image_size, texture_rect) = match atlas { + let (image_size, texture_rect) = match &sprite.atlas { Some(a) => { let layout = atlas_layouts.get(&a.layout)?; ( @@ -102,7 +99,7 @@ fn compute_sprite_slices( ) } None => { - let image = images.get(image_handle)?; + let image = images.get(&sprite.image)?; let size = Vec2::new( image.texture_descriptor.size.width as f32, image.texture_descriptor.size.height as f32, @@ -157,14 +154,7 @@ pub(crate) fn compute_slices_on_asset_event( if !added_handles.contains(&sprite.image.id()) { continue; } - if let Some(slices) = compute_sprite_slices( - sprite, - scale_mode, - &sprite.image, - &images, - sprite.atlas.as_ref(), - &atlas_layouts, - ) { + if let Some(slices) = compute_sprite_slices(sprite, scale_mode, &images, &atlas_layouts) { commands.entity(entity).insert(slices); } } @@ -182,14 +172,7 @@ pub(crate) fn compute_slices_on_sprite_change( >, ) { for (entity, scale_mode, sprite) in &changed_sprites { - if let Some(slices) = compute_sprite_slices( - sprite, - scale_mode, - &sprite.image, - &images, - sprite.atlas.as_ref(), - &atlas_layouts, - ) { + if let Some(slices) = compute_sprite_slices(sprite, scale_mode, &images, &atlas_layouts) { commands.entity(entity).insert(slices); } } From 1d9b438cf7b6811982e49196b7d57a0cb6f9d74b Mon Sep 17 00:00:00 2001 From: Emerson Coskey <56370779+ecoskey@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:49:24 -0700 Subject: [PATCH 14/18] fix sprite_sheet --- examples/2d/sprite_sheet.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/2d/sprite_sheet.rs b/examples/2d/sprite_sheet.rs index 9ce038c218473..f0ea2dcf04740 100644 --- a/examples/2d/sprite_sheet.rs +++ b/examples/2d/sprite_sheet.rs @@ -22,16 +22,19 @@ struct AnimationTimer(Timer); fn animate_sprite( time: Res