Skip to content
Snippets Groups Projects
Verified Commit 09a697aa authored by Louis's avatar Louis :fire:
Browse files

Copy initial impl from Cresthollow

parents
No related branches found
No related tags found
No related merge requests found
/target
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/micro_bevy_splash.iml" filepath="$PROJECT_DIR$/.idea/micro_bevy_splash.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
\ No newline at end of file
This diff is collapsed.
[package]
name = "micro_bevy_splash"
version = "0.1.0"
edition = "2021"
[dependencies]
log = "0.4.21"
micro_musicbox = "0.9.0"
bevy_render = "0.13"
bevy_ecs = "0.13"
bevy_sprite = "0.13"
bevy_text = "0.13"
bevy_app = "0.13"
bevy_asset = "0.13"
bevy_input = "0.13"
bevy_math = "0.13"
bevy_transform = "0.13"
bevy_time = "0.13"
bevy_hierarchy = "0.13"
#[dependencies.bevy]
#version = "0.13"
#default-features = false
#features = [
# "bevy_render",
# "bevy_sprite",
# "bevy_text"
#]
\ No newline at end of file
use micro_musicbox::prelude::AudioSource;
use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
use std::time::Duration;
use bevy_asset::Handle;
use bevy_ecs::prelude::*;
use bevy_render::prelude::*;
#[derive(Clone, Copy, Debug)]
pub enum SplashStepType {
FadeColour { from: Color, to: Color },
Wait,
}
#[derive(Clone, Copy, Debug)]
pub struct SplashStep {
duration: f32,
step_type: SplashStepType,
}
pub type SplashAnimation = Vec<SplashStep>;
#[derive(Resource)]
pub struct LogoHandle(pub Handle<Image>);
impl Deref for LogoHandle {
type Target = Handle<Image>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Resource)]
pub struct StingHandle(pub Handle<AudioSource>);
impl Deref for StingHandle {
type Target = Handle<AudioSource>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug, Resource, Default)]
pub struct SplashSettings<StateType: States> {
pub run_in: StateType,
pub transition_to: StateType,
pub sting_path: String,
pub logo_path: String,
pub duration: Duration,
pub skip_on_input: bool,
pub bg_color_tween: Option<(Color, Color)>,
pub logo_color_tween: Option<(Color, Color)>,
pub tween_duration: Option<Duration>,
}
#[derive(Clone, Copy, Debug, Component)]
pub struct ScaleToWindow;
#[derive(Default, Resource)]
pub struct TweenClearColor {
pub from: Color,
pub to: Color,
pub progress: Duration,
pub duration: Duration,
}
#[derive(Default, Resource)]
pub struct SplashTime(pub Duration);
impl Deref for SplashTime {
type Target = Duration;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for SplashTime {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
\ No newline at end of file
#![allow(clippy::type_complexity)]
// use bevy::prelude::*;
use std::time::Duration;
use bevy_app::{App, Plugin, Update};
use bevy_ecs::prelude::*;
pub use crate::components::SplashSettings;
use crate::components::{SplashTime, TweenClearColor};
mod components;
mod system;
#[derive(Default, Debug, Resource)]
struct SplashManager {
elapsed: Duration,
}
#[derive(PartialOrd, PartialEq, Copy, Clone, Resource, Default)]
pub enum SplashState {
#[default]
Loading,
Running,
}
impl SplashState {
pub fn is_loading(&self) -> bool {
matches!(&self, SplashState::Loading)
}
pub fn is_running(&self) -> bool {
matches!(&self, SplashState::Running)
}
}
pub fn is_loading(state: Res<SplashState>) -> bool {
state.is_loading()
}
pub fn is_running(state: Res<SplashState>) -> bool {
state.is_running()
}
pub fn started_running(state: Res<SplashState>) -> bool {
state.is_changed() && state.is_running()
}
pub struct AdventSplashPlugin<StateType: States> {
settings: SplashSettings<StateType>,
}
impl<StateType: States> AdventSplashPlugin<StateType> {
pub fn with_settings(settings: SplashSettings<StateType>) -> AdventSplashPlugin<StateType> {
Self { settings }
}
}
impl<StateType: States + Copy> Plugin for AdventSplashPlugin<StateType> {
fn build(&self, app: &mut App) {
app.init_resource::<SplashState>()
.insert_resource(SplashTime(Duration::from_secs(5)))
.insert_resource(self.settings.clone())
.add_systems(
OnEnter(self.settings.run_in),
system::load_assets::<StateType>,
)
.add_systems(
Update,
system::monitor_asset_loading
.run_if(is_loading)
.run_if(in_state(self.settings.run_in)),
)
.add_systems(
Update,
system::spawn_splash_assets::<StateType>
.run_if(started_running)
.run_if(in_state(self.settings.run_in)),
)
.add_systems(
Update,
(system::apply_window_scale, system::tick_time::<StateType>)
.run_if(is_running)
.run_if(in_state(self.settings.run_in)),
)
.add_systems(
Update,
system::tween_clear_color
.run_if(is_running)
.run_if(in_state(self.settings.run_in))
.run_if(resource_exists::<TweenClearColor>),
)
.add_systems(OnExit(self.settings.run_in), system::cleanup_assets);
if self.settings.skip_on_input {
app.add_systems(
Update,
system::window_skip::<StateType>
.run_if(is_running)
.run_if(in_state(self.settings.run_in)),
);
}
}
}
use crate::components::{
LogoHandle, ScaleToWindow, SplashSettings, SplashTime, StingHandle, TweenClearColor,
};
use crate::SplashState;
use micro_musicbox::prelude::MusicBox;
use std::ops::Deref;
use std::time::Duration;
use bevy_asset::{Assets, AssetServer, Handle, LoadState, RecursiveDependencyLoadState};
use bevy_ecs::prelude::*;
use bevy_input::prelude::*;
use bevy_math::Vec2;
use bevy_render::prelude::*;
use bevy_render::texture::ImageSampler;
use bevy_sprite::prelude::*;
use bevy_time::Time;
use bevy_transform::prelude::Transform;
use bevy_hierarchy::DespawnRecursiveExt;
use log::error;
fn interpolate_colours(from: Color, to: Color, percent: f32) -> Color {
let diff_r = to.r() - from.r();
let diff_g = to.g() - from.g();
let diff_b = to.b() - from.b();
let diff_a = to.a() - from.a();
Color::rgba(
from.r() + diff_r * percent,
from.g() + diff_g * percent,
from.b() + diff_b * percent,
from.a() + diff_a * percent,
)
}
pub fn load_assets<StateTypes: States>(
mut commands: Commands,
settings: Res<SplashSettings<StateTypes>>,
assets: Res<AssetServer>,
) {
commands.insert_resource(LogoHandle(assets.load(&settings.logo_path)));
commands.insert_resource(StingHandle(assets.load(&settings.sting_path)));
}
pub fn monitor_asset_loading(
logo: Res<LogoHandle>,
sting: Res<StingHandle>,
assets: Res<AssetServer>,
mut state: ResMut<SplashState>,
) {
if [
logo.as_ref().0.id().untyped(),
sting.as_ref().0.id().untyped(),
]
.iter()
.flat_map(|handle| assets.get_recursive_dependency_load_state(*handle))
.fold(LoadState::Loaded, |total, current| {
if total == LoadState::Loaded {
match current {
RecursiveDependencyLoadState::NotLoaded => LoadState::NotLoaded,
RecursiveDependencyLoadState::Loading => LoadState::Loading,
RecursiveDependencyLoadState::Loaded => LoadState::Loaded,
RecursiveDependencyLoadState::Failed => LoadState::Failed,
}
} else {
total
}
}) == LoadState::Loaded
{
*state = SplashState::Running;
}
}
pub fn spawn_splash_assets<T: States>(
mut commands: Commands,
mut musicbox: MusicBox<AssetServer>,
mut textures: ResMut<Assets<Image>>,
logo: Res<LogoHandle>,
settings: Res<SplashSettings<T>>,
) {
if let (Some((from, to)), Some(duration)) = (settings.bg_color_tween, settings.tween_duration) {
commands.insert_resource(TweenClearColor {
from,
to,
duration,
progress: Duration::ZERO,
});
}
musicbox.play_ui_sfx(&settings.sting_path);
if let Some(tex) = textures.get_mut(&logo.0) {
tex.sampler = ImageSampler::linear();
commands.spawn((
ScaleToWindow,
SpriteBundle {
texture: logo.clone_weak(),
transform: Transform::from_xyz(0.0, 0.0, 10.0),
..Default::default()
},
));
} else {
error!("Failed to load splash texture");
}
}
pub fn tween_clear_color(
mut commands: Commands,
time: Res<Time>,
mut conf: ResMut<TweenClearColor>,
) {
conf.progress = conf.progress.saturating_add(time.delta());
if conf.progress >= conf.duration {
commands.insert_resource(ClearColor(conf.to));
commands.remove_resource::<TweenClearColor>();
return;
}
let progress = conf.progress.as_secs_f32() / conf.duration.as_secs_f32();
commands.insert_resource(ClearColor(interpolate_colours(
conf.from, conf.to, progress,
)));
}
pub fn apply_window_scale(
window_query: Query<&OrthographicProjection>,
mut scaled_query: Query<
(
&mut Transform,
Option<&Handle<Image>>,
),
(
With<Handle<Image>>,
With<ScaleToWindow>,
),
>,
images: Res<Assets<Image>>,
) {
let ortho = match window_query.get_single() {
Ok(val) => val,
Err(_e) => {
return;
}
};
let Vec2 {
x: camera_width,
y: camera_height,
} = ortho.area.size();
for (mut transform, img_handle) in &mut scaled_query {
let image = match img_handle {
Some(handle) => match images.get(handle) {
Some(image) => image,
_ => continue,
},
None => continue,
};
let scale_factor = match camera_width > camera_height {
true => camera_height / image.texture_descriptor.size.height as f32,
false => camera_width / image.texture_descriptor.size.width as f32,
};
transform.scale = [scale_factor, scale_factor, 1.0].into();
}
}
pub fn tick_time<StateType: States + Copy>(
mut next_state: ResMut<NextState<StateType>>,
time: Res<Time>,
conf: Res<SplashSettings<StateType>>,
mut timer: ResMut<SplashTime>,
) {
*timer = SplashTime(timer.saturating_sub(time.delta()));
if timer.0.is_zero() {
next_state.set(conf.transition_to);
}
}
pub fn window_skip<StateType: States + Copy>(
input: Res<ButtonInput<KeyCode>>,
mut state: ResMut<NextState<StateType>>,
settings: Res<SplashSettings<StateType>>,
) {
if input.any_just_pressed(vec![KeyCode::Escape, KeyCode::Space, KeyCode::Enter]) {
state.set(settings.transition_to);
}
}
pub fn cleanup_assets(mut commands: Commands, ents: Query<Entity, With<ScaleToWindow>>) {
commands.remove_resource::<LogoHandle>();
commands.remove_resource::<StingHandle>();
commands.remove_resource::<TweenClearColor>();
commands.remove_resource::<SplashTime>();
commands.insert_resource(ClearColor(Color::BLACK));
for ent in &ents {
commands.entity(ent).despawn_recursive();
}
}
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