|
@@ -0,0 +1,136 @@
|
|
|
|
+use bevy::core::Stopwatch;
|
|
|
|
+use bevy::prelude::*;
|
|
|
|
+
|
|
|
|
+use crate::assets::LOGO;
|
|
|
|
+use crate::game_state::GameState;
|
|
|
|
+
|
|
|
|
+#[derive(Clone, Debug)]
|
|
|
|
+pub struct LogoFilename(pub String);
|
|
|
|
+
|
|
|
|
+#[derive(Component)]
|
|
|
|
+struct Logo;
|
|
|
|
+
|
|
|
|
+#[derive(Component)]
|
|
|
|
+struct LogoFader {
|
|
|
|
+ time: Stopwatch,
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Logo should take 10 seconds
|
|
|
|
+const LOGO_DURATION: f32 = 10.0;
|
|
|
|
+
|
|
|
|
+pub struct LogoPlugin;
|
|
|
|
+
|
|
|
|
+fn logo_filename(filename: Option<Res<LogoFilename>>) -> String {
|
|
|
|
+ filename.map_or_else(|| LOGO.to_string(), |res| res.0.clone())
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fn setup_logo(
|
|
|
|
+ mut commands: Commands,
|
|
|
|
+ filename: Option<Res<LogoFilename>>,
|
|
|
|
+ assert_server: Res<AssetServer>,
|
|
|
|
+ windows: Res<Windows>,
|
|
|
|
+) {
|
|
|
|
+ let path = logo_filename(filename);
|
|
|
|
+ let window = windows.primary();
|
|
|
|
+ let (win_w, win_h) = (window.width(), window.height());
|
|
|
|
+
|
|
|
|
+ commands
|
|
|
|
+ .spawn()
|
|
|
|
+ .insert(Logo)
|
|
|
|
+ .insert_bundle(SpriteBundle {
|
|
|
|
+ texture: assert_server.load(&path),
|
|
|
|
+ transform: Transform {
|
|
|
|
+ translation: Vec3::new(0.0, 0.0, 0.0),
|
|
|
|
+ ..Default::default()
|
|
|
|
+ },
|
|
|
|
+ ..Default::default()
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ commands
|
|
|
|
+ .spawn()
|
|
|
|
+ .insert(LogoFader { time: Stopwatch::new() })
|
|
|
|
+ .insert_bundle(SpriteBundle {
|
|
|
|
+ sprite: Sprite {
|
|
|
|
+ color: Color::rgba(0.0, 0.0, 0.0, 0.0),
|
|
|
|
+ custom_size: Some(Vec2::new(win_w, win_h)),
|
|
|
|
+ ..Default::default()
|
|
|
|
+ },
|
|
|
|
+ transform: Transform {
|
|
|
|
+ translation: Vec3::new(0.0, 0.0, 1.0),
|
|
|
|
+ ..Default::default()
|
|
|
|
+ },
|
|
|
|
+ ..Default::default()
|
|
|
|
+ });
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fn resize_logo(
|
|
|
|
+ mut query: Query<&mut Transform, With<Logo>>,
|
|
|
|
+ filename: Option<Res<LogoFilename>>,
|
|
|
|
+ windows: Res<Windows>,
|
|
|
|
+ images: Res<Assets<Image>>,
|
|
|
|
+) {
|
|
|
|
+ let mut logo_transform = query.single_mut();
|
|
|
|
+ let window = windows.primary();
|
|
|
|
+ let (win_w, win_h) = (window.width(), window.height());
|
|
|
|
+ let win_aspect = win_w / win_h;
|
|
|
|
+
|
|
|
|
+ if let Some(logo_image) = images.get(logo_filename(filename)) {
|
|
|
|
+ let size = logo_image.size();
|
|
|
|
+ let (logo_w, logo_h) = (size.x, size.y);
|
|
|
|
+ let logo_aspect = logo_w / logo_h;
|
|
|
|
+
|
|
|
|
+ let scale = if win_aspect > logo_aspect {
|
|
|
|
+ win_h / logo_h
|
|
|
|
+ } else {
|
|
|
|
+ win_w / logo_w
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ logo_transform.scale.x = scale;
|
|
|
|
+ logo_transform.scale.y = scale;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fn resize_fader(
|
|
|
|
+ mut query: Query<&mut Sprite, With<LogoFader>>,
|
|
|
|
+ windows: Res<Windows>,
|
|
|
|
+) {
|
|
|
|
+ let mut sprite = query.single_mut();
|
|
|
|
+ let window = windows.primary();
|
|
|
|
+ sprite.custom_size = Some(Vec2::new(window.width(), window.height()));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fn animate_logo(
|
|
|
|
+ mut query: Query<(&mut Sprite, &mut LogoFader)>,
|
|
|
|
+ mut game_state: ResMut<State<GameState>>,
|
|
|
|
+ time: Res<Time>,
|
|
|
|
+) {
|
|
|
|
+ let (mut sprite, mut fader) = query.single_mut();
|
|
|
|
+ let timer: &mut Stopwatch = &mut fader.time;
|
|
|
|
+ timer.tick(time.delta());
|
|
|
|
+
|
|
|
|
+ let progress = timer.elapsed_secs();
|
|
|
|
+
|
|
|
|
+ if progress < LOGO_DURATION * 0.4 {
|
|
|
|
+ sprite.color = Color::rgba(0.0, 0.0, 0.0, (progress * std::f32::consts::PI / (LOGO_DURATION * 0.8)).cos());
|
|
|
|
+ } else if progress >= LOGO_DURATION * 0.4 && progress < LOGO_DURATION * 0.6 {
|
|
|
|
+ sprite.color = Color::rgba(0.0, 0.0, 0.0, 0.0);
|
|
|
|
+ } else if progress >= LOGO_DURATION * 0.6 && progress < LOGO_DURATION {
|
|
|
|
+ sprite.color = Color::rgba(0.0, 0.0, 0.0, ((progress - LOGO_DURATION * 0.2) * std::f32::consts::PI / (LOGO_DURATION * 0.8)).cos().abs());
|
|
|
|
+ } else {
|
|
|
|
+ game_state.set(GameState::MainMenu).unwrap();
|
|
|
|
+ sprite.color = Color::rgba(0.0, 0.0, 0.0, 1.0);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl Plugin for LogoPlugin {
|
|
|
|
+ fn build(&self, app: &mut App) {
|
|
|
|
+ let state = GameState::Logo;
|
|
|
|
+ app.add_state(state);
|
|
|
|
+ app.add_system_set(SystemSet::on_enter(state).with_system(setup_logo));
|
|
|
|
+ app.add_system_set(SystemSet::on_update(state)
|
|
|
|
+ .with_system(resize_logo)
|
|
|
|
+ .with_system(animate_logo)
|
|
|
|
+ .with_system(resize_fader)
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+}
|