Skip to content
Snippets Groups Projects
Unverified Commit 15c5bdbd authored by shuo's avatar shuo Committed by GitHub
Browse files

Add an example of menu hover animation (#59)

parent eeec3713
No related branches found
No related tags found
No related merge requests found
...@@ -3,6 +3,22 @@ use bevy_inspector_egui::WorldInspectorPlugin; ...@@ -3,6 +3,22 @@ use bevy_inspector_egui::WorldInspectorPlugin;
use bevy_tweening::{lens::*, *}; use bevy_tweening::{lens::*, *};
use std::time::Duration; use std::time::Duration;
const NORMAL_COLOR: Color = Color::rgba(162. / 255., 226. / 255., 95. / 255., 1.);
const HOVER_COLOR: Color = Color::AZURE;
const CLICK_COLOR: Color = Color::ALICE_BLUE;
const TEXT_COLOR: Color = Color::rgba(83. / 255., 163. / 255., 130. / 255., 1.);
const INIT_TRANSITION_DONE: u64 = 1;
/// The menu in this example has two set of animations:
/// one for appearance, one for interaction. Interaction animations
/// are only enabled after appearance animations finished.
///
/// The logic is handled as:
/// 1. Appearance animations send a `TweenComplete` event with `INIT_TRANSITION_DONE`
/// 2. The `enable_interaction_after_initial_animation` system adds a label component
/// `InitTransitionDone` to any button component which completed its appearance animation,
/// to mark it as active.
/// 3. The `interaction` system only queries buttons with a `InitTransitionDone` marker.
fn main() { fn main() {
App::default() App::default()
.insert_resource(WindowDescriptor { .insert_resource(WindowDescriptor {
...@@ -14,6 +30,8 @@ fn main() { ...@@ -14,6 +30,8 @@ fn main() {
}) })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_system(bevy::window::close_on_esc) .add_system(bevy::window::close_on_esc)
.add_system(interaction)
.add_system(enable_interaction_after_initial_animation)
.add_plugin(TweeningPlugin) .add_plugin(TweeningPlugin)
.add_plugin(WorldInspectorPlugin::new()) .add_plugin(WorldInspectorPlugin::new())
.add_startup_system(setup) .add_startup_system(setup)
...@@ -45,7 +63,12 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { ...@@ -45,7 +63,12 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.insert(Name::new("menu")) .insert(Name::new("menu"))
.with_children(|container| { .with_children(|container| {
let mut start_time_ms = 0; let mut start_time_ms = 0;
for text in &["Continue", "New Game", "Settings", "Quit"] { for (text, label) in [
("Continue", ButtonLabel::Continue),
("New Game", ButtonLabel::NewGame),
("Settings", ButtonLabel::Settings),
("Quit", ButtonLabel::Quit),
] {
let tween_scale = Tween::new( let tween_scale = Tween::new(
EaseFunction::BounceOut, EaseFunction::BounceOut,
Duration::from_secs(2), Duration::from_secs(2),
...@@ -53,16 +76,19 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { ...@@ -53,16 +76,19 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
start: Vec3::splat(0.01), start: Vec3::splat(0.01),
end: Vec3::ONE, end: Vec3::ONE,
}, },
); )
.with_completed_event(INIT_TRANSITION_DONE);
let animator = if start_time_ms > 0 { let animator = if start_time_ms > 0 {
let delay = Delay::new(Duration::from_millis(start_time_ms)); let delay = Delay::new(Duration::from_millis(start_time_ms));
Animator::new(delay.then(tween_scale)) Animator::new(delay.then(tween_scale))
} else { } else {
Animator::new(tween_scale) Animator::new(tween_scale)
}; };
start_time_ms += 500; start_time_ms += 500;
container container
.spawn_bundle(NodeBundle { .spawn_bundle(ButtonBundle {
node: Node { node: Node {
size: Vec2::new(300., 80.), size: Vec2::new(300., 80.),
}, },
...@@ -76,12 +102,13 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { ...@@ -76,12 +102,13 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
justify_content: JustifyContent::Center, justify_content: JustifyContent::Center,
..default() ..default()
}, },
color: UiColor(Color::rgb_u8(162, 226, 95)), color: UiColor(NORMAL_COLOR),
transform: Transform::from_scale(Vec3::splat(0.01)), transform: Transform::from_scale(Vec3::splat(0.01)),
..default() ..default()
}) })
.insert(Name::new(format!("button:{}", text))) .insert(Name::new(format!("button:{}", text)))
.insert(animator) .insert(animator)
.insert(label)
.with_children(|parent| { .with_children(|parent| {
parent.spawn_bundle(TextBundle { parent.spawn_bundle(TextBundle {
text: Text::from_section( text: Text::from_section(
...@@ -89,7 +116,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { ...@@ -89,7 +116,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
TextStyle { TextStyle {
font: font.clone(), font: font.clone(),
font_size: 48.0, font_size: 48.0,
color: Color::rgb_u8(83, 163, 130), color: TEXT_COLOR,
}, },
) )
.with_alignment(TextAlignment { .with_alignment(TextAlignment {
...@@ -102,3 +129,85 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { ...@@ -102,3 +129,85 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
} }
}); });
} }
fn enable_interaction_after_initial_animation(
mut commands: Commands,
mut reader: EventReader<TweenCompleted>,
) {
for event in reader.iter() {
if event.user_data == INIT_TRANSITION_DONE {
commands.entity(event.entity).insert(InitTransitionDone);
}
}
}
#[derive(Component)]
struct InitTransitionDone;
#[derive(Component, Clone, Copy)]
enum ButtonLabel {
Continue,
NewGame,
Settings,
Quit,
}
fn interaction(
mut interaction_query: Query<
(
&mut Animator<Transform>,
&Transform,
&Interaction,
&mut UiColor,
&ButtonLabel,
),
(Changed<Interaction>, With<InitTransitionDone>),
>,
) {
for (mut animator, transform, interaction, mut color, button_label) in &mut interaction_query {
match *interaction {
Interaction::Clicked => {
*color = CLICK_COLOR.into();
match button_label {
ButtonLabel::Continue => {
println!("Continue clicked");
}
ButtonLabel::NewGame => {
println!("NewGame clicked");
}
ButtonLabel::Settings => {
println!("Settings clicked");
}
ButtonLabel::Quit => {
println!("Quit clicked");
}
}
}
Interaction::Hovered => {
*color = HOVER_COLOR.into();
animator.set_tweenable(Tween::new(
EaseFunction::QuadraticIn,
Duration::from_millis(200),
TransformScaleLens {
start: Vec3::ONE,
end: Vec3::splat(1.1),
},
));
}
Interaction::None => {
*color = NORMAL_COLOR.into();
let start_scale = transform.scale;
animator.set_tweenable(Tween::new(
EaseFunction::QuadraticIn,
Duration::from_millis(200),
TransformScaleLens {
start: start_scale,
end: Vec3::ONE,
},
));
}
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment