mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 06:00:20 +00:00
Move TextureAtlas into UiImage and remove impl Component for TextureAtlas (#16072)
# Objective Fixes #16064 ## Solution - Add TextureAtlas to `UiImage::texture_atlas` - Add `TextureAtlas::from_atlas_image` for parity with `Sprite` - Rename `UiImage::texture` to `UiImage::image` for parity with `Sprite` - Port relevant implementations and uses - Remove `derive(Component)` for `TextureAtlas` --- ## Migration Guide Before: ```rust commands.spawn(( UiImage::new(image), TextureAtlas { index, layout }, )); ``` After: ```rust commands.spawn(UiImage::from_atlas_image(image, TextureAtlas { index, layout })); ``` Before: ```rust commands.spawn(UiImage { texture: some_image, ..default() }) ``` After: ```rust commands.spawn(UiImage { image: some_image, ..default() }) ```
This commit is contained in:
parent
2cdad48b30
commit
9274bfed27
10 changed files with 71 additions and 70 deletions
|
@ -1,5 +1,4 @@
|
|||
use bevy_asset::{Asset, AssetId, Assets, Handle};
|
||||
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||
use bevy_math::{URect, UVec2};
|
||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||
#[cfg(feature = "serialize")]
|
||||
|
@ -152,7 +151,7 @@ impl TextureAtlasLayout {
|
|||
}
|
||||
}
|
||||
|
||||
/// Component used to draw a specific section of a texture.
|
||||
/// An index into a [`TextureAtlasLayout`], which corresponds to a specific section of a texture.
|
||||
///
|
||||
/// It stores a handle to [`TextureAtlasLayout`] and the index of the current section of the atlas.
|
||||
/// The texture atlas contains various *sections* of a given texture, allowing users to have a single
|
||||
|
@ -164,8 +163,8 @@ impl TextureAtlasLayout {
|
|||
/// - [`animated sprite sheet example`](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_sheet.rs)
|
||||
/// - [`sprite animation event example`](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_animation.rs)
|
||||
/// - [`texture atlas example`](https://github.com/bevyengine/bevy/blob/latest/examples/2d/texture_atlas.rs)
|
||||
#[derive(Component, Default, Debug, Clone, Reflect)]
|
||||
#[reflect(Component, Default, Debug)]
|
||||
#[derive(Default, Debug, Clone, Reflect)]
|
||||
#[reflect(Default, Debug)]
|
||||
pub struct TextureAtlas {
|
||||
/// Texture atlas layout handle
|
||||
pub layout: Handle<TextureAtlasLayout>,
|
||||
|
|
|
@ -40,7 +40,7 @@ use bevy_render::{
|
|||
ExtractSchedule, Render,
|
||||
};
|
||||
use bevy_sprite::TextureAtlasLayout;
|
||||
use bevy_sprite::{BorderRect, ImageScaleMode, SpriteAssetEvents, TextureAtlas};
|
||||
use bevy_sprite::{BorderRect, ImageScaleMode, SpriteAssetEvents};
|
||||
|
||||
use crate::{Display, Node};
|
||||
use bevy_text::{ComputedTextBlock, PositionedGlyph, TextColor, TextLayoutInfo};
|
||||
|
@ -317,14 +317,13 @@ pub fn extract_uinode_images(
|
|||
Option<&CalculatedClip>,
|
||||
Option<&TargetCamera>,
|
||||
&UiImage,
|
||||
Option<&TextureAtlas>,
|
||||
),
|
||||
Without<ImageScaleMode>,
|
||||
>,
|
||||
>,
|
||||
mapping: Extract<Query<RenderEntity>>,
|
||||
) {
|
||||
for (entity, uinode, transform, view_visibility, clip, camera, image, atlas) in &uinode_query {
|
||||
for (entity, uinode, transform, view_visibility, clip, camera, image) in &uinode_query {
|
||||
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
||||
else {
|
||||
continue;
|
||||
|
@ -337,12 +336,14 @@ pub fn extract_uinode_images(
|
|||
// Skip invisible images
|
||||
if !view_visibility.get()
|
||||
|| image.color.is_fully_transparent()
|
||||
|| image.texture.id() == TRANSPARENT_IMAGE_HANDLE.id()
|
||||
|| image.image.id() == TRANSPARENT_IMAGE_HANDLE.id()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let atlas_rect = atlas
|
||||
let atlas_rect = image
|
||||
.texture_atlas
|
||||
.as_ref()
|
||||
.and_then(|s| s.texture_rect(&texture_atlases))
|
||||
.map(|r| r.as_rect());
|
||||
|
||||
|
@ -376,7 +377,7 @@ pub fn extract_uinode_images(
|
|||
color: image.color.into(),
|
||||
rect,
|
||||
clip: clip.map(|clip| clip.clip),
|
||||
image: image.texture.id(),
|
||||
image: image.image.id(),
|
||||
camera_entity: render_camera_entity,
|
||||
item: ExtractedUiItem::Node {
|
||||
atlas_scaling,
|
||||
|
|
|
@ -24,8 +24,7 @@ use bevy_render::{
|
|||
Extract, ExtractSchedule, Render, RenderSet,
|
||||
};
|
||||
use bevy_sprite::{
|
||||
ImageScaleMode, SliceScaleMode, SpriteAssetEvents, TextureAtlas, TextureAtlasLayout,
|
||||
TextureSlicer,
|
||||
ImageScaleMode, SliceScaleMode, SpriteAssetEvents, TextureAtlasLayout, TextureSlicer,
|
||||
};
|
||||
use bevy_transform::prelude::GlobalTransform;
|
||||
use bevy_utils::HashMap;
|
||||
|
@ -258,22 +257,12 @@ pub fn extract_ui_texture_slices(
|
|||
Option<&TargetCamera>,
|
||||
&UiImage,
|
||||
&ImageScaleMode,
|
||||
Option<&TextureAtlas>,
|
||||
)>,
|
||||
>,
|
||||
mapping: Extract<Query<RenderEntity>>,
|
||||
) {
|
||||
for (
|
||||
entity,
|
||||
uinode,
|
||||
transform,
|
||||
view_visibility,
|
||||
clip,
|
||||
camera,
|
||||
image,
|
||||
image_scale_mode,
|
||||
atlas,
|
||||
) in &slicers_query
|
||||
for (entity, uinode, transform, view_visibility, clip, camera, image, image_scale_mode) in
|
||||
&slicers_query
|
||||
{
|
||||
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
||||
else {
|
||||
|
@ -287,12 +276,14 @@ pub fn extract_ui_texture_slices(
|
|||
// Skip invisible images
|
||||
if !view_visibility.get()
|
||||
|| image.color.is_fully_transparent()
|
||||
|| image.texture.id() == TRANSPARENT_IMAGE_HANDLE.id()
|
||||
|| image.image.id() == TRANSPARENT_IMAGE_HANDLE.id()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let atlas_rect = atlas
|
||||
let atlas_rect = image
|
||||
.texture_atlas
|
||||
.as_ref()
|
||||
.and_then(|s| s.texture_rect(&texture_atlases))
|
||||
.map(|r| r.as_rect());
|
||||
|
||||
|
@ -318,7 +309,7 @@ pub fn extract_ui_texture_slices(
|
|||
max: uinode.calculated_size,
|
||||
},
|
||||
clip: clip.map(|clip| clip.clip),
|
||||
image: image.texture.id(),
|
||||
image: image.image.id(),
|
||||
camera_entity,
|
||||
image_scale_mode: image_scale_mode.clone(),
|
||||
atlas_rect,
|
||||
|
|
|
@ -9,7 +9,7 @@ use bevy_render::{
|
|||
texture::{Image, TRANSPARENT_IMAGE_HANDLE},
|
||||
view::Visibility,
|
||||
};
|
||||
use bevy_sprite::BorderRect;
|
||||
use bevy_sprite::{BorderRect, TextureAtlas};
|
||||
use bevy_transform::components::Transform;
|
||||
use bevy_utils::warn_once;
|
||||
use bevy_window::{PrimaryWindow, WindowRef};
|
||||
|
@ -2053,15 +2053,17 @@ pub struct UiImage {
|
|||
/// Handle to the texture.
|
||||
///
|
||||
/// This defaults to a [`TRANSPARENT_IMAGE_HANDLE`], which points to a fully transparent 1x1 texture.
|
||||
pub texture: Handle<Image>,
|
||||
pub image: Handle<Image>,
|
||||
/// The (optional) texture atlas used to render the image
|
||||
pub texture_atlas: Option<TextureAtlas>,
|
||||
/// Whether the image should be flipped along its x-axis
|
||||
pub flip_x: bool,
|
||||
/// Whether the image should be flipped along its y-axis
|
||||
pub flip_y: bool,
|
||||
/// An optional rectangle representing the region of the image to render, instead of rendering
|
||||
/// the full image. This is an easy one-off alternative to using a [`TextureAtlas`](bevy_sprite::TextureAtlas).
|
||||
/// the full image. This is an easy one-off alternative to using a [`TextureAtlas`].
|
||||
///
|
||||
/// When used with a [`TextureAtlas`](bevy_sprite::TextureAtlas), the rect
|
||||
/// When used with a [`TextureAtlas`], the rect
|
||||
/// is offset by the atlas's minimal (top-left) corner position.
|
||||
pub rect: Option<Rect>,
|
||||
}
|
||||
|
@ -2079,8 +2081,9 @@ impl Default for UiImage {
|
|||
// This should be white because the tint is multiplied with the image,
|
||||
// so if you set an actual image with default tint you'd want its original colors
|
||||
color: Color::WHITE,
|
||||
texture_atlas: None,
|
||||
// This texture needs to be transparent by default, to avoid covering the background color
|
||||
texture: TRANSPARENT_IMAGE_HANDLE,
|
||||
image: TRANSPARENT_IMAGE_HANDLE,
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
rect: None,
|
||||
|
@ -2092,7 +2095,7 @@ impl UiImage {
|
|||
/// Create a new [`UiImage`] with the given texture.
|
||||
pub fn new(texture: Handle<Image>) -> Self {
|
||||
Self {
|
||||
texture,
|
||||
image: texture,
|
||||
color: Color::WHITE,
|
||||
..Default::default()
|
||||
}
|
||||
|
@ -2103,14 +2106,24 @@ impl UiImage {
|
|||
/// This is primarily useful for debugging / mocking the extents of your image.
|
||||
pub fn solid_color(color: Color) -> Self {
|
||||
Self {
|
||||
texture: Handle::default(),
|
||||
image: Handle::default(),
|
||||
color,
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
texture_atlas: None,
|
||||
rect: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`UiImage`] from an image, with an associated texture atlas
|
||||
pub fn from_atlas_image(image: Handle<Image>, atlas: TextureAtlas) -> Self {
|
||||
Self {
|
||||
image,
|
||||
texture_atlas: Some(atlas),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the color tint
|
||||
#[must_use]
|
||||
pub const fn with_color(mut self, color: Color) -> Self {
|
||||
|
|
|
@ -4,7 +4,7 @@ use bevy_ecs::prelude::*;
|
|||
use bevy_math::{UVec2, Vec2};
|
||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||
use bevy_render::texture::Image;
|
||||
use bevy_sprite::{TextureAtlas, TextureAtlasLayout};
|
||||
use bevy_sprite::TextureAtlasLayout;
|
||||
use bevy_window::{PrimaryWindow, Window};
|
||||
use taffy::{MaybeMath, MaybeResolve};
|
||||
|
||||
|
@ -97,15 +97,7 @@ pub fn update_image_content_size_system(
|
|||
textures: Res<Assets<Image>>,
|
||||
|
||||
atlases: Res<Assets<TextureAtlasLayout>>,
|
||||
mut query: Query<
|
||||
(
|
||||
&mut ContentSize,
|
||||
&UiImage,
|
||||
&mut UiImageSize,
|
||||
Option<&TextureAtlas>,
|
||||
),
|
||||
UpdateImageFilter,
|
||||
>,
|
||||
mut query: Query<(&mut ContentSize, &UiImage, &mut UiImageSize), UpdateImageFilter>,
|
||||
) {
|
||||
let combined_scale_factor = windows
|
||||
.get_single()
|
||||
|
@ -113,10 +105,10 @@ pub fn update_image_content_size_system(
|
|||
.unwrap_or(1.)
|
||||
* ui_scale.0;
|
||||
|
||||
for (mut content_size, image, mut image_size, atlas_image) in &mut query {
|
||||
if let Some(size) = match atlas_image {
|
||||
for (mut content_size, image, mut image_size) in &mut query {
|
||||
if let Some(size) = match &image.texture_atlas {
|
||||
Some(atlas) => atlas.texture_rect(&atlases).map(|t| t.size()),
|
||||
None => textures.get(&image.texture).map(Image::size),
|
||||
None => textures.get(&image.image).map(Image::size),
|
||||
} {
|
||||
// Update only if size or scale factor has changed to avoid needless layout calculations
|
||||
if size != image_size.size
|
||||
|
|
|
@ -116,7 +116,7 @@ fn setup(
|
|||
|
||||
commands.spawn((
|
||||
UiImage {
|
||||
texture: metering_mask,
|
||||
image: metering_mask,
|
||||
..default()
|
||||
},
|
||||
Node {
|
||||
|
|
|
@ -84,6 +84,7 @@ fn setup(
|
|||
commands.spawn((
|
||||
Sprite {
|
||||
image: texture_handle.clone(),
|
||||
texture_atlas: Some(TextureAtlas::from(texture_atlas_handle.clone())),
|
||||
custom_size: Some(tile_size),
|
||||
..default()
|
||||
},
|
||||
|
@ -92,7 +93,6 @@ fn setup(
|
|||
rotation,
|
||||
scale,
|
||||
},
|
||||
TextureAtlas::from(texture_atlas_handle.clone()),
|
||||
AnimationTimer(timer),
|
||||
));
|
||||
}
|
||||
|
@ -112,13 +112,16 @@ struct AnimationTimer(Timer);
|
|||
fn animate_sprite(
|
||||
time: Res<Time>,
|
||||
texture_atlases: Res<Assets<TextureAtlasLayout>>,
|
||||
mut query: Query<(&mut AnimationTimer, &mut TextureAtlas)>,
|
||||
mut query: Query<(&mut AnimationTimer, &mut Sprite)>,
|
||||
) {
|
||||
for (mut timer, mut sheet) in query.iter_mut() {
|
||||
for (mut timer, mut sprite) in query.iter_mut() {
|
||||
timer.tick(time.delta());
|
||||
if timer.just_finished() {
|
||||
let texture_atlas = texture_atlases.get(&sheet.layout).unwrap();
|
||||
sheet.index = (sheet.index + 1) % texture_atlas.textures.len();
|
||||
let Some(atlas) = &mut sprite.texture_atlas else {
|
||||
continue;
|
||||
};
|
||||
let texture_atlas = texture_atlases.get(&atlas.layout).unwrap();
|
||||
atlas.index = (atlas.index + 1) % texture_atlas.textures.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,14 +44,13 @@ fn setup(
|
|||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
UiImage::new(texture_handle),
|
||||
UiImage::from_atlas_image(texture_handle, TextureAtlas::from(texture_atlas_handle)),
|
||||
Node {
|
||||
width: Val::Px(256.),
|
||||
height: Val::Px(256.),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(ANTIQUE_WHITE.into()),
|
||||
TextureAtlas::from(texture_atlas_handle),
|
||||
Outline::new(Val::Px(8.0), Val::ZERO, CRIMSON.into()),
|
||||
));
|
||||
parent
|
||||
|
@ -65,13 +64,12 @@ fn setup(
|
|||
});
|
||||
}
|
||||
|
||||
fn increment_atlas_index(
|
||||
mut atlas_images: Query<&mut TextureAtlas>,
|
||||
keyboard: Res<ButtonInput<KeyCode>>,
|
||||
) {
|
||||
fn increment_atlas_index(mut ui_images: Query<&mut UiImage>, keyboard: Res<ButtonInput<KeyCode>>) {
|
||||
if keyboard.just_pressed(KeyCode::Space) {
|
||||
for mut atlas_image in &mut atlas_images {
|
||||
atlas_image.index = (atlas_image.index + 1) % 6;
|
||||
for mut ui_image in &mut ui_images {
|
||||
if let Some(atlas) = &mut ui_image.texture_atlas {
|
||||
atlas.index = (atlas.index + 1) % 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,17 +19,19 @@ fn main() {
|
|||
|
||||
fn button_system(
|
||||
mut interaction_query: Query<
|
||||
(&Interaction, &mut TextureAtlas, &Children, &mut UiImage),
|
||||
(&Interaction, &Children, &mut UiImage),
|
||||
(Changed<Interaction>, With<Button>),
|
||||
>,
|
||||
mut text_query: Query<&mut Text>,
|
||||
) {
|
||||
for (interaction, mut atlas, children, mut image) in &mut interaction_query {
|
||||
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();
|
||||
atlas.index = (atlas.index + 1) % 30;
|
||||
if let Some(atlas) = &mut image.texture_atlas {
|
||||
atlas.index = (atlas.index + 1) % 30;
|
||||
}
|
||||
image.color = GOLD.into();
|
||||
}
|
||||
Interaction::Hovered => {
|
||||
|
@ -79,7 +81,13 @@ fn setup(
|
|||
parent
|
||||
.spawn((
|
||||
Button,
|
||||
UiImage::new(texture_handle.clone()),
|
||||
UiImage::from_atlas_image(
|
||||
texture_handle.clone(),
|
||||
TextureAtlas {
|
||||
index: idx,
|
||||
layout: atlas_layout_handle.clone(),
|
||||
},
|
||||
),
|
||||
Node {
|
||||
width: Val::Px(w),
|
||||
height: Val::Px(h),
|
||||
|
@ -91,10 +99,6 @@ fn setup(
|
|||
..default()
|
||||
},
|
||||
ImageScaleMode::Sliced(slicer.clone()),
|
||||
TextureAtlas {
|
||||
index: idx,
|
||||
layout: atlas_layout_handle.clone(),
|
||||
},
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
|
|
|
@ -58,7 +58,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
] {
|
||||
parent.spawn((
|
||||
UiImage {
|
||||
texture: image.clone(),
|
||||
image: image.clone(),
|
||||
flip_x,
|
||||
flip_y,
|
||||
..default()
|
||||
|
|
Loading…
Reference in a new issue