use std::time::Duration; use bevy::{prelude::*, utils::Instant}; use bevy_asset_loader::asset_collection::AssetCollection; use keyframe::functions::*; use crate::AppState; const FADE_IN_TIME: f32 = 2.5; const ZOOM_OUT_TIME: f32 = 2.0; const FULL_FADE_FINISHED: f32 = 4.0; const FADE_OUT_TIME: f32 = 1.0; const QUIT_TIME: f32 = 0.5; const LOGO_FINISHED_SCALE: f32 = 0.2; pub struct LogoPlugin; #[derive(AssetCollection, Resource)] pub struct LogoAssets { #[asset(path = "logo.ktx2")] logo_texture: Handle, } #[derive(Resource)] struct LogoData { logo_entity: Entity, camera_entity: Entity, } impl Plugin for LogoPlugin { fn build(&self, app: &mut App) { app.add_systems(OnEnter(AppState::Logo), load_logo) .add_systems(Update, fade_in_logo.run_if(in_state(AppState::Logo))) .add_systems(OnExit(AppState::Logo), cleanup_logo); } } #[derive(Component)] struct LogoTimer(Instant, Instant); impl LogoTimer { fn elapsed_secs(&self) -> f32 { self.0.elapsed().as_secs_f32() } fn finished(&self) -> bool { Instant::now() > self.1 } } fn load_logo(mut commands: Commands, assets: Res) { let now = Instant::now(); commands.insert_resource(ClearColor(Color::BLACK)); let sprite_entity = commands .spawn(( SpriteBundle { texture: assets.logo_texture.clone(), sprite: Sprite { color: Color::rgba(1.0, 1.0, 1.0, 0.0), ..Default::default() }, transform: Transform::from_scale(Vec3::ONE), ..Default::default() }, LogoTimer( now, now + Duration::from_secs_f32(QUIT_TIME + FULL_FADE_FINISHED + FADE_OUT_TIME), ), )) .id(); let camera_entity = commands.spawn(Camera2dBundle::default()).id(); commands.insert_resource(LogoData { logo_entity: sprite_entity, camera_entity, }); } fn fade_in_logo( mut query: Query<(&mut Sprite, &mut Transform, &LogoTimer)>, mut next_state: ResMut>, keyboard_input: Res>, ) { let (mut sprite, mut transform, timer) = query.single_mut(); let elapsed = timer.elapsed_secs(); if timer.finished() || keyboard_input.get_pressed().len() > 0 { next_state.set(AppState::LoadingMenu); } else if elapsed > FULL_FADE_FINISHED { sprite.color.set_a(keyframe::ease_with_scaled_time( EaseInCubic, FULL_FADE_FINISHED, 0.0, elapsed, FADE_OUT_TIME + FULL_FADE_FINISHED, )); } else { sprite.color.set_a(keyframe::ease_with_scaled_time( Linear, 0.0, 1.0, elapsed, FADE_IN_TIME, )); transform.scale = Vec3::splat(keyframe::ease_with_scaled_time( EaseOutQuint, 2.0, LOGO_FINISHED_SCALE, elapsed, ZOOM_OUT_TIME, )); } } fn cleanup_logo(mut commands: Commands, logo_data: Res) { commands.entity(logo_data.logo_entity).despawn_recursive(); commands.entity(logo_data.camera_entity).despawn_recursive(); commands.remove_resource::(); commands.remove_resource::(); }