bevy/examples/2d/sprite_tile.rs

49 lines
1.2 KiB
Rust
Raw Normal View History

Sprite slicing and tiling (#10588) > Replaces #5213 # Objective Implement sprite tiling and [9 slice scaling](https://en.wikipedia.org/wiki/9-slice_scaling) for `bevy_sprite`. Allowing slice scaling and texture tiling. Basic scaling vs 9 slice scaling: ![Traditional_scaling_vs_9-slice_scaling](https://user-images.githubusercontent.com/26703856/177335801-27f6fa27-c569-4ce6-b0e6-4f54e8f4e80a.svg) Slicing example: <img width="481" alt="Screenshot 2022-07-05 at 15 05 49" src="https://user-images.githubusercontent.com/26703856/177336112-9e961af0-c0af-4197-aec9-430c1170a79d.png"> Tiling example: <img width="1329" alt="Screenshot 2023-11-16 at 13 53 32" src="https://github.com/bevyengine/bevy/assets/26703856/14db39b7-d9e0-4bc3-ba0e-b1f2db39ae8f"> # Solution - `SpriteBundlue` now has a `scale_mode` component storing a `SpriteScaleMode` enum with three variants: - `Stretched` (default) - `Tiled` to have sprites tile horizontally and/or vertically - `Sliced` allowing 9 slicing the texture and optionally tile some sections with a `Textureslicer`. - `bevy_sprite` has two extra systems to compute a `ComputedTextureSlices` if necessary,: - One system react to changes on `Sprite`, `Handle<Image>` or `SpriteScaleMode` - The other listens to `AssetEvent<Image>` to compute slices on sprites when the texture is ready or changed - I updated the `bevy_sprite` extraction stage to extract potentially multiple textures instead of one, depending on the presence of `ComputedTextureSlices` - I added two examples showcasing the slicing and tiling feature. The addition of `ComputedTextureSlices` as a cache is to avoid querying the image data, to retrieve its dimensions, every frame in a extract or prepare stage. Also it reacts to changes so we can have stuff like this (tiling example): https://github.com/bevyengine/bevy/assets/26703856/a349a9f3-33c3-471f-8ef4-a0e5dfce3b01 # Related - [ ] Once #5103 or #10099 is merged I can enable tiling and slicing for texture sheets as ui # To discuss There is an other option, to consider slice/tiling as part of the asset, using the new asset preprocessing but I have no clue on how to do it. Also, instead of retrieving the Image dimensions, we could use the same system as the sprite sheet and have the user give the image dimensions directly (grid). But I think it's less user friendly --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: ickshonpe <david.curthoys@googlemail.com> Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
2024-01-15 15:40:06 +00:00
//! Displays a single [`Sprite`] tiled in a grid, with a scaling animation
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, animate)
.run();
}
#[derive(Resource)]
struct AnimationState {
min: f32,
max: f32,
current: f32,
speed: f32,
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
Sprite slicing and tiling (#10588) > Replaces #5213 # Objective Implement sprite tiling and [9 slice scaling](https://en.wikipedia.org/wiki/9-slice_scaling) for `bevy_sprite`. Allowing slice scaling and texture tiling. Basic scaling vs 9 slice scaling: ![Traditional_scaling_vs_9-slice_scaling](https://user-images.githubusercontent.com/26703856/177335801-27f6fa27-c569-4ce6-b0e6-4f54e8f4e80a.svg) Slicing example: <img width="481" alt="Screenshot 2022-07-05 at 15 05 49" src="https://user-images.githubusercontent.com/26703856/177336112-9e961af0-c0af-4197-aec9-430c1170a79d.png"> Tiling example: <img width="1329" alt="Screenshot 2023-11-16 at 13 53 32" src="https://github.com/bevyengine/bevy/assets/26703856/14db39b7-d9e0-4bc3-ba0e-b1f2db39ae8f"> # Solution - `SpriteBundlue` now has a `scale_mode` component storing a `SpriteScaleMode` enum with three variants: - `Stretched` (default) - `Tiled` to have sprites tile horizontally and/or vertically - `Sliced` allowing 9 slicing the texture and optionally tile some sections with a `Textureslicer`. - `bevy_sprite` has two extra systems to compute a `ComputedTextureSlices` if necessary,: - One system react to changes on `Sprite`, `Handle<Image>` or `SpriteScaleMode` - The other listens to `AssetEvent<Image>` to compute slices on sprites when the texture is ready or changed - I updated the `bevy_sprite` extraction stage to extract potentially multiple textures instead of one, depending on the presence of `ComputedTextureSlices` - I added two examples showcasing the slicing and tiling feature. The addition of `ComputedTextureSlices` as a cache is to avoid querying the image data, to retrieve its dimensions, every frame in a extract or prepare stage. Also it reacts to changes so we can have stuff like this (tiling example): https://github.com/bevyengine/bevy/assets/26703856/a349a9f3-33c3-471f-8ef4-a0e5dfce3b01 # Related - [ ] Once #5103 or #10099 is merged I can enable tiling and slicing for texture sheets as ui # To discuss There is an other option, to consider slice/tiling as part of the asset, using the new asset preprocessing but I have no clue on how to do it. Also, instead of retrieving the Image dimensions, we could use the same system as the sprite sheet and have the user give the image dimensions directly (grid). But I think it's less user friendly --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: ickshonpe <david.curthoys@googlemail.com> Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
2024-01-15 15:40:06 +00:00
commands.insert_resource(AnimationState {
min: 128.0,
max: 512.0,
current: 128.0,
speed: 50.0,
});
Improved `UiImage` and `Sprite` scaling and slicing APIs (#16088) # Objective 1. UI texture slicing chops and scales an image to fit the size of a node and isn't meant to place any constraints on the size of the node itself, but because the required components changes required `ImageSize` and `ContentSize` for nodes with `UiImage`, texture sliced nodes are laid out using an `ImageMeasure`. 2. In 0.14 users could spawn a `(UiImage, NodeBundle)` which would display an image stretched to fill the UI node's bounds ignoring the image's instrinsic size. Now that `UiImage` requires `ContentSize`, there's no option to display an image without its size placing constrains on the UI layout (unless you force the `Node` to a fixed size, but that's not a solution). 3. It's desirable that the `Sprite` and `UiImage` share similar APIs. Fixes #16109 ## Solution * Remove the `Component` impl from `ImageScaleMode`. * Add a `Stretch` variant to `ImageScaleMode`. * Add a field `scale_mode: ImageScaleMode` to `Sprite`. * Add a field `mode: UiImageMode` to `UiImage`. * Add an enum `UiImageMode` similar to `ImageScaleMode` but with additional UI specific variants. * Remove the queries for `ImageScaleMode` from Sprite and UI extraction, and refer to the new fields instead. * Change `ui_layout_system` to update measure funcs on any change to `ContentSize`s to enable manual clearing without removing the component. * Don't add a measure unless `UiImageMode::Auto` is set in `update_image_content_size_system`. Mutably deref the `Mut<ContentSize>` if the `UiImage` is changed to force removal of any existing measure func. ## Testing Remove all the constraints from the ui_texture_slice example: ```rust //! This example illustrates how to create buttons with their textures sliced //! and kept in proportion instead of being stretched by the button dimensions use bevy::{ color::palettes::css::{GOLD, ORANGE}, prelude::*, winit::WinitSettings, }; fn main() { App::new() .add_plugins(DefaultPlugins) // Only run the app when there is user input. This will significantly reduce CPU/GPU use. .insert_resource(WinitSettings::desktop_app()) .add_systems(Startup, setup) .add_systems(Update, button_system) .run(); } fn button_system( mut interaction_query: Query< (&Interaction, &Children, &mut UiImage), (Changed<Interaction>, With<Button>), >, mut text_query: Query<&mut Text>, ) { for (interaction, children, mut image) in &mut interaction_query { let mut text = text_query.get_mut(children[0]).unwrap(); match *interaction { Interaction::Pressed => { **text = "Press".to_string(); image.color = GOLD.into(); } Interaction::Hovered => { **text = "Hover".to_string(); image.color = ORANGE.into(); } Interaction::None => { **text = "Button".to_string(); image.color = Color::WHITE; } } } } fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { let image = asset_server.load("textures/fantasy_ui_borders/panel-border-010.png"); let slicer = TextureSlicer { border: BorderRect::square(22.0), center_scale_mode: SliceScaleMode::Stretch, sides_scale_mode: SliceScaleMode::Stretch, max_corner_scale: 1.0, }; // ui camera commands.spawn(Camera2d); commands .spawn(Node { width: Val::Percent(100.0), height: Val::Percent(100.0), align_items: AlignItems::Center, justify_content: JustifyContent::Center, ..default() }) .with_children(|parent| { for [w, h] in [[150.0, 150.0], [300.0, 150.0], [150.0, 300.0]] { parent .spawn(( Button, Node { // width: Val::Px(w), // height: Val::Px(h), // horizontally center child text justify_content: JustifyContent::Center, // vertically center child text align_items: AlignItems::Center, margin: UiRect::all(Val::Px(20.0)), ..default() }, UiImage::new(image.clone()), ImageScaleMode::Sliced(slicer.clone()), )) .with_children(|parent| { // parent.spawn(( // Text::new("Button"), // TextFont { // font: asset_server.load("fonts/FiraSans-Bold.ttf"), // font_size: 33.0, // ..default() // }, // TextColor(Color::srgb(0.9, 0.9, 0.9)), // )); }); } }); } ``` This should result in a blank window, since without any constraints the texture slice image nodes should be zero-sized. But in main the image nodes are given the size of the underlying unsliced source image `textures/fantasy_ui_borders/panel-border-010.png`: <img width="321" alt="slicing" src="https://github.com/user-attachments/assets/cbd74c9c-14cd-4b4d-93c6-7c0152bb05ee"> For this PR need to change the lines: ``` UiImage::new(image.clone()), ImageScaleMode::Sliced(slicer.clone()), ``` to ``` UiImage::new(image.clone()).with_mode(UiImageMode::Sliced(slicer.clone()), ``` and then nothing should be rendered, as desired. --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-11-04 15:14:03 +00:00
commands.spawn(Sprite {
image: asset_server.load("branding/icon.png"),
image_mode: SpriteImageMode::Tiled {
Sprite slicing and tiling (#10588) > Replaces #5213 # Objective Implement sprite tiling and [9 slice scaling](https://en.wikipedia.org/wiki/9-slice_scaling) for `bevy_sprite`. Allowing slice scaling and texture tiling. Basic scaling vs 9 slice scaling: ![Traditional_scaling_vs_9-slice_scaling](https://user-images.githubusercontent.com/26703856/177335801-27f6fa27-c569-4ce6-b0e6-4f54e8f4e80a.svg) Slicing example: <img width="481" alt="Screenshot 2022-07-05 at 15 05 49" src="https://user-images.githubusercontent.com/26703856/177336112-9e961af0-c0af-4197-aec9-430c1170a79d.png"> Tiling example: <img width="1329" alt="Screenshot 2023-11-16 at 13 53 32" src="https://github.com/bevyengine/bevy/assets/26703856/14db39b7-d9e0-4bc3-ba0e-b1f2db39ae8f"> # Solution - `SpriteBundlue` now has a `scale_mode` component storing a `SpriteScaleMode` enum with three variants: - `Stretched` (default) - `Tiled` to have sprites tile horizontally and/or vertically - `Sliced` allowing 9 slicing the texture and optionally tile some sections with a `Textureslicer`. - `bevy_sprite` has two extra systems to compute a `ComputedTextureSlices` if necessary,: - One system react to changes on `Sprite`, `Handle<Image>` or `SpriteScaleMode` - The other listens to `AssetEvent<Image>` to compute slices on sprites when the texture is ready or changed - I updated the `bevy_sprite` extraction stage to extract potentially multiple textures instead of one, depending on the presence of `ComputedTextureSlices` - I added two examples showcasing the slicing and tiling feature. The addition of `ComputedTextureSlices` as a cache is to avoid querying the image data, to retrieve its dimensions, every frame in a extract or prepare stage. Also it reacts to changes so we can have stuff like this (tiling example): https://github.com/bevyengine/bevy/assets/26703856/a349a9f3-33c3-471f-8ef4-a0e5dfce3b01 # Related - [ ] Once #5103 or #10099 is merged I can enable tiling and slicing for texture sheets as ui # To discuss There is an other option, to consider slice/tiling as part of the asset, using the new asset preprocessing but I have no clue on how to do it. Also, instead of retrieving the Image dimensions, we could use the same system as the sprite sheet and have the user give the image dimensions directly (grid). But I think it's less user friendly --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: ickshonpe <david.curthoys@googlemail.com> Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
2024-01-15 15:40:06 +00:00
tile_x: true,
tile_y: true,
stretch_value: 0.5, // The image will tile every 128px
},
Improved `UiImage` and `Sprite` scaling and slicing APIs (#16088) # Objective 1. UI texture slicing chops and scales an image to fit the size of a node and isn't meant to place any constraints on the size of the node itself, but because the required components changes required `ImageSize` and `ContentSize` for nodes with `UiImage`, texture sliced nodes are laid out using an `ImageMeasure`. 2. In 0.14 users could spawn a `(UiImage, NodeBundle)` which would display an image stretched to fill the UI node's bounds ignoring the image's instrinsic size. Now that `UiImage` requires `ContentSize`, there's no option to display an image without its size placing constrains on the UI layout (unless you force the `Node` to a fixed size, but that's not a solution). 3. It's desirable that the `Sprite` and `UiImage` share similar APIs. Fixes #16109 ## Solution * Remove the `Component` impl from `ImageScaleMode`. * Add a `Stretch` variant to `ImageScaleMode`. * Add a field `scale_mode: ImageScaleMode` to `Sprite`. * Add a field `mode: UiImageMode` to `UiImage`. * Add an enum `UiImageMode` similar to `ImageScaleMode` but with additional UI specific variants. * Remove the queries for `ImageScaleMode` from Sprite and UI extraction, and refer to the new fields instead. * Change `ui_layout_system` to update measure funcs on any change to `ContentSize`s to enable manual clearing without removing the component. * Don't add a measure unless `UiImageMode::Auto` is set in `update_image_content_size_system`. Mutably deref the `Mut<ContentSize>` if the `UiImage` is changed to force removal of any existing measure func. ## Testing Remove all the constraints from the ui_texture_slice example: ```rust //! This example illustrates how to create buttons with their textures sliced //! and kept in proportion instead of being stretched by the button dimensions use bevy::{ color::palettes::css::{GOLD, ORANGE}, prelude::*, winit::WinitSettings, }; fn main() { App::new() .add_plugins(DefaultPlugins) // Only run the app when there is user input. This will significantly reduce CPU/GPU use. .insert_resource(WinitSettings::desktop_app()) .add_systems(Startup, setup) .add_systems(Update, button_system) .run(); } fn button_system( mut interaction_query: Query< (&Interaction, &Children, &mut UiImage), (Changed<Interaction>, With<Button>), >, mut text_query: Query<&mut Text>, ) { for (interaction, children, mut image) in &mut interaction_query { let mut text = text_query.get_mut(children[0]).unwrap(); match *interaction { Interaction::Pressed => { **text = "Press".to_string(); image.color = GOLD.into(); } Interaction::Hovered => { **text = "Hover".to_string(); image.color = ORANGE.into(); } Interaction::None => { **text = "Button".to_string(); image.color = Color::WHITE; } } } } fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { let image = asset_server.load("textures/fantasy_ui_borders/panel-border-010.png"); let slicer = TextureSlicer { border: BorderRect::square(22.0), center_scale_mode: SliceScaleMode::Stretch, sides_scale_mode: SliceScaleMode::Stretch, max_corner_scale: 1.0, }; // ui camera commands.spawn(Camera2d); commands .spawn(Node { width: Val::Percent(100.0), height: Val::Percent(100.0), align_items: AlignItems::Center, justify_content: JustifyContent::Center, ..default() }) .with_children(|parent| { for [w, h] in [[150.0, 150.0], [300.0, 150.0], [150.0, 300.0]] { parent .spawn(( Button, Node { // width: Val::Px(w), // height: Val::Px(h), // horizontally center child text justify_content: JustifyContent::Center, // vertically center child text align_items: AlignItems::Center, margin: UiRect::all(Val::Px(20.0)), ..default() }, UiImage::new(image.clone()), ImageScaleMode::Sliced(slicer.clone()), )) .with_children(|parent| { // parent.spawn(( // Text::new("Button"), // TextFont { // font: asset_server.load("fonts/FiraSans-Bold.ttf"), // font_size: 33.0, // ..default() // }, // TextColor(Color::srgb(0.9, 0.9, 0.9)), // )); }); } }); } ``` This should result in a blank window, since without any constraints the texture slice image nodes should be zero-sized. But in main the image nodes are given the size of the underlying unsliced source image `textures/fantasy_ui_borders/panel-border-010.png`: <img width="321" alt="slicing" src="https://github.com/user-attachments/assets/cbd74c9c-14cd-4b4d-93c6-7c0152bb05ee"> For this PR need to change the lines: ``` UiImage::new(image.clone()), ImageScaleMode::Sliced(slicer.clone()), ``` to ``` UiImage::new(image.clone()).with_mode(UiImageMode::Sliced(slicer.clone()), ``` and then nothing should be rendered, as desired. --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-11-04 15:14:03 +00:00
..default()
});
Sprite slicing and tiling (#10588) > Replaces #5213 # Objective Implement sprite tiling and [9 slice scaling](https://en.wikipedia.org/wiki/9-slice_scaling) for `bevy_sprite`. Allowing slice scaling and texture tiling. Basic scaling vs 9 slice scaling: ![Traditional_scaling_vs_9-slice_scaling](https://user-images.githubusercontent.com/26703856/177335801-27f6fa27-c569-4ce6-b0e6-4f54e8f4e80a.svg) Slicing example: <img width="481" alt="Screenshot 2022-07-05 at 15 05 49" src="https://user-images.githubusercontent.com/26703856/177336112-9e961af0-c0af-4197-aec9-430c1170a79d.png"> Tiling example: <img width="1329" alt="Screenshot 2023-11-16 at 13 53 32" src="https://github.com/bevyengine/bevy/assets/26703856/14db39b7-d9e0-4bc3-ba0e-b1f2db39ae8f"> # Solution - `SpriteBundlue` now has a `scale_mode` component storing a `SpriteScaleMode` enum with three variants: - `Stretched` (default) - `Tiled` to have sprites tile horizontally and/or vertically - `Sliced` allowing 9 slicing the texture and optionally tile some sections with a `Textureslicer`. - `bevy_sprite` has two extra systems to compute a `ComputedTextureSlices` if necessary,: - One system react to changes on `Sprite`, `Handle<Image>` or `SpriteScaleMode` - The other listens to `AssetEvent<Image>` to compute slices on sprites when the texture is ready or changed - I updated the `bevy_sprite` extraction stage to extract potentially multiple textures instead of one, depending on the presence of `ComputedTextureSlices` - I added two examples showcasing the slicing and tiling feature. The addition of `ComputedTextureSlices` as a cache is to avoid querying the image data, to retrieve its dimensions, every frame in a extract or prepare stage. Also it reacts to changes so we can have stuff like this (tiling example): https://github.com/bevyengine/bevy/assets/26703856/a349a9f3-33c3-471f-8ef4-a0e5dfce3b01 # Related - [ ] Once #5103 or #10099 is merged I can enable tiling and slicing for texture sheets as ui # To discuss There is an other option, to consider slice/tiling as part of the asset, using the new asset preprocessing but I have no clue on how to do it. Also, instead of retrieving the Image dimensions, we could use the same system as the sprite sheet and have the user give the image dimensions directly (grid). But I think it's less user friendly --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: ickshonpe <david.curthoys@googlemail.com> Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
2024-01-15 15:40:06 +00:00
}
fn animate(mut sprites: Query<&mut Sprite>, mut state: ResMut<AnimationState>, time: Res<Time>) {
if state.current >= state.max || state.current <= state.min {
state.speed = -state.speed;
};
state.current += state.speed * time.delta_secs();
Sprite slicing and tiling (#10588) > Replaces #5213 # Objective Implement sprite tiling and [9 slice scaling](https://en.wikipedia.org/wiki/9-slice_scaling) for `bevy_sprite`. Allowing slice scaling and texture tiling. Basic scaling vs 9 slice scaling: ![Traditional_scaling_vs_9-slice_scaling](https://user-images.githubusercontent.com/26703856/177335801-27f6fa27-c569-4ce6-b0e6-4f54e8f4e80a.svg) Slicing example: <img width="481" alt="Screenshot 2022-07-05 at 15 05 49" src="https://user-images.githubusercontent.com/26703856/177336112-9e961af0-c0af-4197-aec9-430c1170a79d.png"> Tiling example: <img width="1329" alt="Screenshot 2023-11-16 at 13 53 32" src="https://github.com/bevyengine/bevy/assets/26703856/14db39b7-d9e0-4bc3-ba0e-b1f2db39ae8f"> # Solution - `SpriteBundlue` now has a `scale_mode` component storing a `SpriteScaleMode` enum with three variants: - `Stretched` (default) - `Tiled` to have sprites tile horizontally and/or vertically - `Sliced` allowing 9 slicing the texture and optionally tile some sections with a `Textureslicer`. - `bevy_sprite` has two extra systems to compute a `ComputedTextureSlices` if necessary,: - One system react to changes on `Sprite`, `Handle<Image>` or `SpriteScaleMode` - The other listens to `AssetEvent<Image>` to compute slices on sprites when the texture is ready or changed - I updated the `bevy_sprite` extraction stage to extract potentially multiple textures instead of one, depending on the presence of `ComputedTextureSlices` - I added two examples showcasing the slicing and tiling feature. The addition of `ComputedTextureSlices` as a cache is to avoid querying the image data, to retrieve its dimensions, every frame in a extract or prepare stage. Also it reacts to changes so we can have stuff like this (tiling example): https://github.com/bevyengine/bevy/assets/26703856/a349a9f3-33c3-471f-8ef4-a0e5dfce3b01 # Related - [ ] Once #5103 or #10099 is merged I can enable tiling and slicing for texture sheets as ui # To discuss There is an other option, to consider slice/tiling as part of the asset, using the new asset preprocessing but I have no clue on how to do it. Also, instead of retrieving the Image dimensions, we could use the same system as the sprite sheet and have the user give the image dimensions directly (grid). But I think it's less user friendly --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: ickshonpe <david.curthoys@googlemail.com> Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
2024-01-15 15:40:06 +00:00
for mut sprite in &mut sprites {
sprite.custom_size = Some(Vec2::splat(state.current));
}
}