diff --git a/crates/bevy_sprite/src/bundle.rs b/crates/bevy_sprite/src/bundle.rs index e6c48ecea036e..83b86a5c854e1 100644 --- a/crates/bevy_sprite/src/bundle.rs +++ b/crates/bevy_sprite/src/bundle.rs @@ -1,4 +1,4 @@ -use crate::{texture_atlas::TextureAtlas, ImageScaleMode, Sprite}; +use crate::{Sprite, TextureAtlas}; use bevy_asset::Handle; use bevy_ecs::bundle::Bundle; use bevy_render::{ @@ -8,12 +8,16 @@ use bevy_render::{ use bevy_transform::components::{GlobalTransform, Transform}; /// A [`Bundle`] of components for drawing a single sprite from an image. +/// +/// # Extra behaviours +/// +/// You may add the following components to enable additional behaviours +/// - [`ImageScaleMode`](crate::ImageScaleMode) to enable either slicing or tiling of the texture +/// - [`TextureAtlas`] to draw specific sections of a sprite sheet, (See [`SpriteSheetBundle`]) #[derive(Bundle, Clone, Default)] pub struct SpriteBundle { /// Specifies the rendering properties of the sprite, such as color tint and flip. pub sprite: Sprite, - /// Controls how the image is altered when scaled. - pub scale_mode: ImageScaleMode, /// The local transform of the sprite, relative to its parent. pub transform: Transform, /// The absolute transform of the sprite. This should generally not be written to directly. @@ -41,8 +45,6 @@ pub struct SpriteBundle { pub struct SpriteSheetBundle { /// Specifies the rendering properties of the sprite, such as color tint and flip. pub sprite: Sprite, - /// Controls how the image is altered when scaled. - pub scale_mode: ImageScaleMode, /// The local transform of the sprite, relative to its parent. pub transform: Transform, /// The absolute transform of the sprite. This should generally not be written to directly. diff --git a/crates/bevy_sprite/src/sprite.rs b/crates/bevy_sprite/src/sprite.rs index c62d6da82d4f3..9c164d9a2c202 100644 --- a/crates/bevy_sprite/src/sprite.rs +++ b/crates/bevy_sprite/src/sprite.rs @@ -32,12 +32,9 @@ pub struct Sprite { } /// Controls how the image is altered when scaled. -#[derive(Component, Debug, Default, Clone, Reflect)] -#[reflect(Component, Default)] +#[derive(Component, Debug, Clone, Reflect)] +#[reflect(Component)] pub enum ImageScaleMode { - /// The entire texture stretches when its dimensions change. This is the default option. - #[default] - Stretched, /// The texture will be cut in 9 slices, keeping the texture in proportions on resize Sliced(TextureSlicer), /// The texture will be repeated if stretched beyond `stretched_value` diff --git a/crates/bevy_sprite/src/texture_slice/computed_slices.rs b/crates/bevy_sprite/src/texture_slice/computed_slices.rs index 4e17fd0f2d4e7..cc5954c3ccb1d 100644 --- a/crates/bevy_sprite/src/texture_slice/computed_slices.rs +++ b/crates/bevy_sprite/src/texture_slice/computed_slices.rs @@ -8,7 +8,7 @@ use bevy_render::texture::Image; use bevy_transform::prelude::*; use bevy_utils::HashSet; -/// Component storing texture slices for sprite entities with a tiled or sliced [`ImageScaleMode`] +/// Component storing texture slices for sprite entities with a [`ImageScaleMode`] /// /// This component is automatically inserted and updated #[derive(Debug, Clone, Component)] @@ -62,9 +62,7 @@ impl ComputedTextureSlices { /// Generates sprite slices for a `sprite` given a `scale_mode`. The slices /// will be computed according to the `image_handle` dimensions or the sprite rect. /// -/// Returns `None` if either: -/// - The scale mode is [`ImageScaleMode::Stretched`] -/// - The image asset is not loaded +/// Returns `None` if the image asset is not loaded #[must_use] fn compute_sprite_slices( sprite: &Sprite, @@ -72,9 +70,6 @@ fn compute_sprite_slices( image_handle: &Handle, images: &Assets, ) -> Option { - if let ImageScaleMode::Stretched = scale_mode { - return None; - } let image_size = images.get(image_handle).map(|i| { Vec2::new( i.texture_descriptor.size.width as f32, @@ -82,7 +77,6 @@ fn compute_sprite_slices( ) })?; let slices = match scale_mode { - ImageScaleMode::Stretched => unreachable!(), ImageScaleMode::Sliced(slicer) => slicer.compute_slices( sprite.rect.unwrap_or(Rect { min: Vec2::ZERO, @@ -110,7 +104,7 @@ fn compute_sprite_slices( } /// System reacting to added or modified [`Image`] handles, and recompute sprite slices -/// on matching sprite entities +/// on matching sprite entities with a [`ImageScaleMode`] component pub(crate) fn compute_slices_on_asset_event( mut commands: Commands, mut events: EventReader>, @@ -140,6 +134,7 @@ pub(crate) fn compute_slices_on_asset_event( } /// System reacting to changes on relevant sprite bundle components to compute the sprite slices +/// on matching sprite entities with a [`ImageScaleMode`] component pub(crate) fn compute_slices_on_sprite_change( mut commands: Commands, images: Res>, diff --git a/crates/bevy_ui/src/node_bundles.rs b/crates/bevy_ui/src/node_bundles.rs index 639142e18118d..964fd61c514ca 100644 --- a/crates/bevy_ui/src/node_bundles.rs +++ b/crates/bevy_ui/src/node_bundles.rs @@ -13,7 +13,7 @@ use bevy_render::{ prelude::Color, view::{InheritedVisibility, ViewVisibility, Visibility}, }; -use bevy_sprite::{ImageScaleMode, TextureAtlas}; +use bevy_sprite::TextureAtlas; #[cfg(feature = "bevy_text")] use bevy_text::{BreakLineOn, JustifyText, Text, TextLayoutInfo, TextSection, TextStyle}; use bevy_transform::prelude::{GlobalTransform, Transform}; @@ -76,6 +76,11 @@ impl Default for NodeBundle { } /// A UI node that is an image +/// +/// # Extra behaviours +/// +/// You may add the following components to enable additional behaviours +/// - [`ImageScaleMode`](bevy_sprite::ImageScaleMode) to enable either slicing or tiling of the texture #[derive(Bundle, Debug, Default)] pub struct ImageBundle { /// Describes the logical size of the node @@ -95,8 +100,6 @@ pub struct ImageBundle { /// /// This component is set automatically pub image_size: UiImageSize, - /// Controls how the image is altered when scaled. - pub scale_mode: ImageScaleMode, /// Whether this node should block interaction with lower nodes pub focus_policy: FocusPolicy, /// The transform of the node @@ -288,6 +291,11 @@ where } /// A UI node that is a button +/// +/// # Extra behaviours +/// +/// You may add the following components to enable additional behaviours +/// - [`ImageScaleMode`](bevy_sprite::ImageScaleMode) to enable either slicing or tiling of the texture #[derive(Bundle, Clone, Debug)] pub struct ButtonBundle { /// Describes the logical size of the node @@ -309,8 +317,6 @@ pub struct ButtonBundle { pub border_color: BorderColor, /// The image of the node pub image: UiImage, - /// Controls how the image is altered when scaled. - pub scale_mode: ImageScaleMode, /// The transform of the node /// /// This component is automatically managed by the UI layout system. @@ -347,7 +353,6 @@ impl Default for ButtonBundle { inherited_visibility: Default::default(), view_visibility: Default::default(), z_index: Default::default(), - scale_mode: ImageScaleMode::default(), } } } diff --git a/crates/bevy_ui/src/texture_slice.rs b/crates/bevy_ui/src/texture_slice.rs index 25aa791be278c..2c77f43499cf8 100644 --- a/crates/bevy_ui/src/texture_slice.rs +++ b/crates/bevy_ui/src/texture_slice.rs @@ -77,9 +77,7 @@ impl ComputedTextureSlices { /// Generates sprite slices for a `sprite` given a `scale_mode`. The slices /// will be computed according to the `image_handle` dimensions or the sprite rect. /// -/// Returns `None` if either: -/// - The scale mode is [`ImageScaleMode::Stretched`] -/// - The image asset is not loaded +/// Returns `None` if the image asset is not loaded #[must_use] fn compute_texture_slices( draw_area: Vec2, @@ -87,9 +85,6 @@ fn compute_texture_slices( image_handle: &UiImage, images: &Assets, ) -> Option { - if let ImageScaleMode::Stretched = scale_mode { - return None; - } let image_size = images.get(&image_handle.texture).map(|i| { Vec2::new( i.texture_descriptor.size.width as f32, @@ -101,7 +96,6 @@ fn compute_texture_slices( max: image_size, }; let slices = match scale_mode { - ImageScaleMode::Stretched => unreachable!(), ImageScaleMode::Sliced(slicer) => slicer.compute_slices(texture_rect, Some(draw_area)), ImageScaleMode::Tiled { tile_x, @@ -120,7 +114,7 @@ fn compute_texture_slices( } /// System reacting to added or modified [`Image`] handles, and recompute sprite slices -/// on matching sprite entities +/// on matching sprite entities with a [`ImageScaleMode`] component pub(crate) fn compute_slices_on_asset_event( mut commands: Commands, mut events: EventReader>, @@ -157,6 +151,7 @@ pub(crate) fn compute_slices_on_asset_event( } /// System reacting to changes on relevant sprite bundle components to compute the sprite slices +/// on matching sprite entities with a [`ImageScaleMode`] component pub(crate) fn compute_slices_on_image_change( mut commands: Commands, images: Res>, diff --git a/examples/2d/sprite_slice.rs b/examples/2d/sprite_slice.rs index 34fcfbcb2bdc9..c2cf40130b091 100644 --- a/examples/2d/sprite_slice.rs +++ b/examples/2d/sprite_slice.rs @@ -25,89 +25,85 @@ fn spawn_sprites( ) { let cases = [ // Reference sprite - ( - "Original texture", - style.clone(), - Vec2::splat(100.0), - ImageScaleMode::default(), - ), + ("Original texture", style.clone(), Vec2::splat(100.0), None), // Scaled regular sprite ( "Stretched texture", style.clone(), Vec2::new(100.0, 200.0), - ImageScaleMode::default(), + None, ), // Stretched Scaled sliced sprite ( "Stretched and sliced", style.clone(), Vec2::new(100.0, 200.0), - ImageScaleMode::Sliced(TextureSlicer { + Some(ImageScaleMode::Sliced(TextureSlicer { border: BorderRect::square(slice_border), center_scale_mode: SliceScaleMode::Stretch, ..default() - }), + })), ), // Scaled sliced sprite ( "Sliced and Tiled", style.clone(), Vec2::new(100.0, 200.0), - ImageScaleMode::Sliced(TextureSlicer { + Some(ImageScaleMode::Sliced(TextureSlicer { border: BorderRect::square(slice_border), center_scale_mode: SliceScaleMode::Tile { stretch_value: 0.5 }, sides_scale_mode: SliceScaleMode::Tile { stretch_value: 0.2 }, ..default() - }), + })), ), // Scaled sliced sprite horizontally ( "Sliced and Tiled", style.clone(), Vec2::new(300.0, 200.0), - ImageScaleMode::Sliced(TextureSlicer { + Some(ImageScaleMode::Sliced(TextureSlicer { border: BorderRect::square(slice_border), center_scale_mode: SliceScaleMode::Tile { stretch_value: 0.2 }, sides_scale_mode: SliceScaleMode::Tile { stretch_value: 0.3 }, ..default() - }), + })), ), // Scaled sliced sprite horizontally with max scale ( "Sliced and Tiled with corner constraint", style, Vec2::new(300.0, 200.0), - ImageScaleMode::Sliced(TextureSlicer { + Some(ImageScaleMode::Sliced(TextureSlicer { border: BorderRect::square(slice_border), center_scale_mode: SliceScaleMode::Tile { stretch_value: 0.1 }, sides_scale_mode: SliceScaleMode::Tile { stretch_value: 0.2 }, max_corner_scale: 0.2, - }), + })), ), ]; for (label, text_style, size, scale_mode) in cases { position.x += 0.5 * size.x; - commands - .spawn(SpriteBundle { - transform: Transform::from_translation(position), - texture: texture_handle.clone(), - sprite: Sprite { - custom_size: Some(size), - ..default() - }, - scale_mode, + let mut cmd = commands.spawn(SpriteBundle { + transform: Transform::from_translation(position), + texture: texture_handle.clone(), + sprite: Sprite { + custom_size: Some(size), + ..default() + }, + ..default() + }); + if let Some(scale_mode) = scale_mode { + cmd.insert(scale_mode); + } + cmd.with_children(|builder| { + builder.spawn(Text2dBundle { + text: Text::from_section(label, text_style).with_justify(JustifyText::Center), + transform: Transform::from_xyz(0., -0.5 * size.y - 10., 0.0), + text_anchor: bevy::sprite::Anchor::TopCenter, ..default() - }) - .with_children(|builder| { - builder.spawn(Text2dBundle { - text: Text::from_section(label, text_style).with_justify(JustifyText::Center), - transform: Transform::from_xyz(0., -0.5 * size.y - 10., 0.0), - text_anchor: bevy::sprite::Anchor::TopCenter, - ..default() - }); }); + }); position.x += 0.5 * size.x + gap; } } diff --git a/examples/2d/sprite_tile.rs b/examples/2d/sprite_tile.rs index 016517505e955..c9b0fe5ecb811 100644 --- a/examples/2d/sprite_tile.rs +++ b/examples/2d/sprite_tile.rs @@ -26,15 +26,17 @@ fn setup(mut commands: Commands, asset_server: Res) { current: 128.0, speed: 50.0, }); - commands.spawn(SpriteBundle { - texture: asset_server.load("branding/icon.png"), - scale_mode: ImageScaleMode::Tiled { + commands.spawn(( + SpriteBundle { + texture: asset_server.load("branding/icon.png"), + ..default() + }, + ImageScaleMode::Tiled { tile_x: true, tile_y: true, stretch_value: 0.5, // The image will tile every 128px }, - ..default() - }); + )); } fn animate(mut sprites: Query<&mut Sprite>, mut state: ResMut, time: Res