mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Update ui_material example to be a slider instead (#14031)
# Objective - Some people have asked how to do image masking in UI. It's pretty easy to do using a `UiMaterial` assuming you know how to write shaders. ## Solution - Update the ui_material example to show the bevy banner slowly being revealed like a progress bar ## Notes I'm not entirely sure if we want this or not. For people that would be comfortable to use this for their own games they would probably have already figured out how to do it and for people that aren't familiar with shaders this isn't really enough to make an actual slider/progress bar. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com>
This commit is contained in:
parent
6573887d5c
commit
011f71a245
3 changed files with 58 additions and 33 deletions
|
@ -1,20 +0,0 @@
|
|||
// This shader draws a circle with a given input color
|
||||
#import bevy_ui::ui_vertex_output::UiVertexOutput
|
||||
|
||||
struct CustomUiMaterial {
|
||||
@location(0) color: vec4<f32>
|
||||
}
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> input: CustomUiMaterial;
|
||||
|
||||
@fragment
|
||||
fn fragment(in: UiVertexOutput) -> @location(0) vec4<f32> {
|
||||
// the UVs are now adjusted around the middle of the rect.
|
||||
let uv = in.uv * 2.0 - 1.0;
|
||||
|
||||
// circle alpha, the higher the power the harsher the falloff.
|
||||
let alpha = 1.0 - pow(sqrt(dot(uv, uv)), 100.0);
|
||||
|
||||
return vec4<f32>(input.color.rgb, alpha);
|
||||
}
|
18
assets/shaders/custom_ui_material.wgsl
Normal file
18
assets/shaders/custom_ui_material.wgsl
Normal file
|
@ -0,0 +1,18 @@
|
|||
// This shader draws a circle with a given input color
|
||||
#import bevy_ui::ui_vertex_output::UiVertexOutput
|
||||
|
||||
@group(1) @binding(0) var<uniform> color: vec4<f32>;
|
||||
@group(1) @binding(1) var<uniform> slider: f32;
|
||||
@group(1) @binding(2) var material_color_texture: texture_2d<f32>;
|
||||
@group(1) @binding(3) var material_color_sampler: sampler;
|
||||
|
||||
|
||||
@fragment
|
||||
fn fragment(in: UiVertexOutput) -> @location(0) vec4<f32> {
|
||||
if in.uv.x < slider {
|
||||
let output_color = textureSample(material_color_texture, material_color_sampler, in.uv) * color;
|
||||
return output_color;
|
||||
} else {
|
||||
return vec4(0.0);
|
||||
}
|
||||
}
|
|
@ -5,26 +5,22 @@ use bevy::reflect::TypePath;
|
|||
use bevy::render::render_resource::*;
|
||||
|
||||
/// This example uses a shader source file from the assets subdirectory
|
||||
const SHADER_ASSET_PATH: &str = "shaders/circle_shader.wgsl";
|
||||
const SHADER_ASSET_PATH: &str = "shaders/custom_ui_material.wgsl";
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugins(UiMaterialPlugin::<CustomUiMaterial>::default())
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, update)
|
||||
.add_systems(Update, animate)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn update(time: Res<Time>, mut ui_materials: ResMut<Assets<CustomUiMaterial>>) {
|
||||
for (_, material) in ui_materials.iter_mut() {
|
||||
// rainbow color effect
|
||||
let new_color = Color::hsl((time.elapsed_seconds() * 60.0) % 360.0, 1., 0.5);
|
||||
material.color = LinearRgba::from(new_color).to_f32_array().into();
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands, mut ui_materials: ResMut<Assets<CustomUiMaterial>>) {
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut ui_materials: ResMut<Assets<CustomUiMaterial>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
// Camera so we can see UI
|
||||
commands.spawn(Camera2dBundle::default());
|
||||
|
||||
|
@ -40,15 +36,18 @@ fn setup(mut commands: Commands, mut ui_materials: ResMut<Assets<CustomUiMateria
|
|||
..default()
|
||||
})
|
||||
.with_children(|parent| {
|
||||
let banner_scale_factor = 0.5;
|
||||
parent.spawn(MaterialNodeBundle {
|
||||
style: Style {
|
||||
position_type: PositionType::Absolute,
|
||||
width: Val::Px(250.0),
|
||||
height: Val::Px(250.0),
|
||||
width: Val::Px(905.0 * banner_scale_factor),
|
||||
height: Val::Px(363.0 * banner_scale_factor),
|
||||
..default()
|
||||
},
|
||||
material: ui_materials.add(CustomUiMaterial {
|
||||
color: LinearRgba::WHITE.to_f32_array().into(),
|
||||
slider: 0.5,
|
||||
color_texture: asset_server.load("branding/banner.png"),
|
||||
}),
|
||||
..default()
|
||||
});
|
||||
|
@ -57,8 +56,17 @@ fn setup(mut commands: Commands, mut ui_materials: ResMut<Assets<CustomUiMateria
|
|||
|
||||
#[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]
|
||||
struct CustomUiMaterial {
|
||||
/// Color multiplied with the image
|
||||
#[uniform(0)]
|
||||
color: Vec4,
|
||||
/// Represents how much of the image is visible
|
||||
/// Goes from 0 to 1
|
||||
#[uniform(1)]
|
||||
slider: f32,
|
||||
/// Image used to represent the slider
|
||||
#[texture(2)]
|
||||
#[sampler(3)]
|
||||
color_texture: Handle<Image>,
|
||||
}
|
||||
|
||||
impl UiMaterial for CustomUiMaterial {
|
||||
|
@ -66,3 +74,22 @@ impl UiMaterial for CustomUiMaterial {
|
|||
SHADER_ASSET_PATH.into()
|
||||
}
|
||||
}
|
||||
|
||||
// Fills the slider slowly over 2 seconds and resets it
|
||||
// Also updates the color of the image to a rainbow color
|
||||
fn animate(
|
||||
mut materials: ResMut<Assets<CustomUiMaterial>>,
|
||||
q: Query<&Handle<CustomUiMaterial>>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
let duration = 2.0;
|
||||
for handle in &q {
|
||||
if let Some(material) = materials.get_mut(handle) {
|
||||
// rainbow color effect
|
||||
let new_color = Color::hsl((time.elapsed_seconds() * 60.0) % 360.0, 1., 0.5);
|
||||
material.color = LinearRgba::from(new_color).to_f32_array().into();
|
||||
material.slider =
|
||||
((time.elapsed_seconds() % (duration * 2.0)) - duration).abs() / duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue