mid refactor: dissolve::{in,out} tagging for animation

So far things are borked, but it _feels_ like we're going in the right
direction
main
Elijah C. Voigt 2 years ago
parent 72811d6d04
commit 7b4682bc21

@ -191,6 +191,7 @@ drone_blue = "DroneBlue"
pawn_blue = "PawnBlue" pawn_blue = "PawnBlue"
board = "Gameboard" board = "Gameboard"
valid_move = "Valid Move Spot" valid_move = "Valid Move Spot"
title = "Title"
[display3d.models.animations] [display3d.models.animations]
intro_a = "GameCamIntro1" intro_a = "GameCamIntro1"

@ -41,6 +41,7 @@ impl Plugin for Display3dPlugin {
), ),
), ),
set_board_model.run_if(any_component_added::<game::BoardComponent>()), set_board_model.run_if(any_component_added::<game::BoardComponent>()),
set_title_model.run_if(any_component_added::<TitleText>()),
set_valid_move_model.run_if(any_component_added::<game::ValidMove>()), set_valid_move_model.run_if(any_component_added::<game::ValidMove>()),
set_tile_hitbox.run_if(any_component_added::<game::Tile>()), set_tile_hitbox.run_if(any_component_added::<game::Tile>()),
select select
@ -49,7 +50,10 @@ impl Plugin for Display3dPlugin {
.run_if(just_pressed(MouseButton::Left)), .run_if(just_pressed(MouseButton::Left)),
pick_up.run_if(any_component_added::<game::Selected>()), pick_up.run_if(any_component_added::<game::Selected>()),
put_down.run_if(any_component_removed::<game::Selected>()), put_down.run_if(any_component_removed::<game::Selected>()),
setup_capture_piece.run_if(any_component_added::<Handle<StandardMaterial>>().or_else(any_component_changed::<Handle<StandardMaterial>>())), setup_dissolve_materials.run_if(
any_component_added::<Handle<StandardMaterial>>()
.or_else(any_component_changed::<Handle<StandardMaterial>>()),
),
capture_piece.run_if(any_with_component::<game::Captured>), capture_piece.run_if(any_with_component::<game::Captured>),
skip_animation skip_animation
.run_if(just_pressed(KeyCode::Enter).or_else(just_pressed(MouseButton::Left))) .run_if(just_pressed(KeyCode::Enter).or_else(just_pressed(MouseButton::Left)))
@ -87,13 +91,37 @@ impl Plugin for Display3dPlugin {
.run_if(run_once()) .run_if(run_once())
.run_if(in_state(DisplayState::Display3d)), .run_if(in_state(DisplayState::Display3d)),
), ),
); )
.add_systems(
Update,
continue_title.run_if(
in_state(GameState::Title).and_then(
just_pressed(KeyCode::Enter).or_else(just_pressed(MouseButton::Left)),
),
),
)
.add_systems(Update, dissolve_animation.run_if(any_with_component::<Dissolving>));
} }
} }
#[derive(Debug, Component, PartialEq)] #[derive(Debug, Component, PartialEq)]
pub(crate) struct Display3d; pub(crate) struct Display3d;
#[derive(Debug, Component)]
pub(crate) struct TitleText;
#[derive(Debug, Component)]
pub(crate) struct Dissolvable {
start: f32,
duration: f32,
}
#[derive(Debug, Component)]
pub(crate) enum Dissolving {
In(f32),
Out(f32),
}
#[derive(Debug, Resource, Clone)] #[derive(Debug, Resource, Clone)]
struct AssetsMap { struct AssetsMap {
hitbox_shape: Handle<Mesh>, hitbox_shape: Handle<Mesh>,
@ -122,7 +150,7 @@ fn load_assets(
}); });
} }
/// Initialize the 3d board /// Initialize the board and pieces
fn initialize(mut commands: Commands, board: Res<game::Board>, assets: Res<AssetsMap>) { fn initialize(mut commands: Commands, board: Res<game::Board>, assets: Res<AssetsMap>) {
info!("Initializing root"); info!("Initializing root");
commands commands
@ -194,8 +222,19 @@ fn initialize(mut commands: Commands, board: Res<game::Board>, assets: Res<Asset
*index, *index,
SceneBundle { ..default() }, SceneBundle { ..default() },
game::Selectable, game::Selectable,
Dissolvable { start: 1.0, duration: 3.0 }, // Marks pieces as dissolving
)); ));
}); });
// Title
parent.spawn((
DisplayState::Display3d,
Display3d,
GameState::Title,
SceneBundle { ..default() },
TitleText,
Dissolvable { start: 0.0, duration: 3.0 }, // Marks title text as dissolving
));
}); });
}); });
} }
@ -221,8 +260,13 @@ fn hydrate_camera(
.get_handle::<Image>("display3d_models_skybox_file") .get_handle::<Image>("display3d_models_skybox_file")
.unwrap(); .unwrap();
let skybox_brightness = tweak.get::<f32>("display3d_skybox_brightness").unwrap(); let skybox_brightness = tweak.get::<f32>("display3d_skybox_brightness").unwrap();
let environment_map_intensity = tweak.get::<f32>("display3d_environment_map_light_intensity").unwrap(); let environment_map_intensity = tweak
let fog_settings = tweak.get::<tweaks::TweakFogSettings>("display3d_fog").unwrap().into(); .get::<f32>("display3d_environment_map_light_intensity")
.unwrap();
let fog_settings = tweak
.get::<tweaks::TweakFogSettings>("display3d_fog")
.unwrap()
.into();
info!("Hydrating camera {:?}", entity); info!("Hydrating camera {:?}", entity);
// Populate the components for the camera // Populate the components for the camera
commands.entity(entity).insert(( commands.entity(entity).insert((
@ -304,7 +348,15 @@ fn update_tweaks(
if let Some(tweak) = tweaks.get(tweaks_file.handle.clone()) { if let Some(tweak) = tweaks.get(tweaks_file.handle.clone()) {
warn!("Updating tweaks!"); warn!("Updating tweaks!");
camera_settings.iter_mut().for_each( camera_settings.iter_mut().for_each(
|(entity, mut fog, mut color_grading, mut tonemapping, mut bloom, mut skybox, mut environment_map_light)| { |(
entity,
mut fog,
mut color_grading,
mut tonemapping,
mut bloom,
mut skybox,
mut environment_map_light,
)| {
*fog = tweak *fog = tweak
.get::<tweaks::TweakFogSettings>("display3d_fog") .get::<tweaks::TweakFogSettings>("display3d_fog")
.unwrap() .unwrap()
@ -321,9 +373,7 @@ fn update_tweaks(
.get::<tweaks::TweakBloomSettings>("display3d_bloom") .get::<tweaks::TweakBloomSettings>("display3d_bloom")
.unwrap() .unwrap()
.into(); .into();
skybox.brightness = tweak skybox.brightness = tweak.get::<f32>("display3d_skybox_brightness").unwrap();
.get::<f32>("display3d_skybox_brightness")
.unwrap();
environment_map_light.intensity = tweak environment_map_light.intensity = tweak
.get::<f32>("display3d_environment_map_light_intensity") .get::<f32>("display3d_environment_map_light_intensity")
.unwrap(); .unwrap();
@ -410,6 +460,37 @@ fn set_board_model(
}); });
} }
fn set_title_model(
mut titles: Query<(&mut Handle<Scene>, &mut Transform), (Added<TitleText>, With<Display3d>)>,
gltfs: Res<Assets<Gltf>>,
tweaks: Res<Assets<Tweaks>>,
tweaks_file: Res<tweak::GameTweaks>,
) {
let tweak = tweaks
.get(tweaks_file.handle.clone())
.expect("Load tweakfile");
titles.iter_mut().for_each(|(mut handle, mut transform)| {
info!("Setting title model");
let assets_handle = tweak
.get_handle::<Gltf>("display3d_models_assets_file")
.unwrap();
let gltf = gltfs.get(assets_handle).expect("Load GLTF content");
*handle = gltf
.named_scenes
.get(
tweak
.get::<String>("display3d_models_scenes_title")
.unwrap()
.as_str(),
)
.expect("Game title model")
.clone();
transform.translation -= Vec3::Y * 0.5;
transform.rotate_local_z(std::f32::consts::PI);
transform.rotate_local_y(std::f32::consts::PI / 2.0);
});
}
/// Given a board index returns the Vec3 location in space /// Given a board index returns the Vec3 location in space
fn board_translation(&BoardIndex { x, y }: &BoardIndex) -> Vec3 { fn board_translation(&BoardIndex { x, y }: &BoardIndex) -> Vec3 {
// Scale x down by 4 to account for -4..4 scaling // Scale x down by 4 to account for -4..4 scaling
@ -1097,26 +1178,19 @@ impl MaterialExtension for DissolveExtension {
} }
} }
// Component for 'backing up' components which are temporarily not used
#[derive(Debug, Component, Clone)]
struct Backup<T: Component>(T);
/// Sets up all pieces to have an associated "dissolve" material ready for capture /// Sets up all pieces to have an associated "dissolve" material ready for capture
fn setup_capture_piece( fn setup_dissolve_materials(
// All entities with materials are candidates for this procedure // All entities with materials are candidates for this procedure
events: Query< events: Query<(Entity, &Handle<StandardMaterial>), Added<Handle<StandardMaterial>>>,
(Entity, &Handle<StandardMaterial>),
Added<Handle<StandardMaterial>>,
>,
// Only process newly created pieces (we do not delete pieces at runtime) // Only process newly created pieces (we do not delete pieces at runtime)
query: Query<Entity, (With<Piece>, Added<Children>)>, query: Query<Entity, (With<Dissolvable>, Added<Children>)>,
// Children of pieces are the actual meshes that need materials // Children of pieces are the actual meshes that need materials
parents: Query<&Parent>, parents: Query<&Parent>,
// Used to create DissovleMaterial // Used to create DissolveMaterial
standard_materials: Res<Assets<StandardMaterial>>, standard_materials: Res<Assets<StandardMaterial>>,
// Used to create Handle<DissolveMaterial> // Used to create Handle<DissolveMaterial>
mut dissolve_materials: ResMut<Assets<DissolveMaterial>>, mut dissolve_materials: ResMut<Assets<DissolveMaterial>>,
// Used to insert Backup(Handle<DissolveMaterial>); // Used to insert Handle<DissolveMaterial>;
mut commands: Commands, mut commands: Commands,
// Cache dissolve textures that have already been created // Cache dissolve textures that have already been created
mut cache: Local<HashMap<Handle<StandardMaterial>, Handle<DissolveMaterial>>>, mut cache: Local<HashMap<Handle<StandardMaterial>, Handle<DissolveMaterial>>>,
@ -1152,7 +1226,9 @@ fn setup_capture_piece(
cache.insert(std_handle.clone(), dis_handle.clone()); cache.insert(std_handle.clone(), dis_handle.clone());
// Add the dissolve handle as a Backup(T) // Add the dissolve handle as a Backup(T)
commands.entity(child).insert(Backup(dis_handle.clone())); commands.entity(child)
.insert(dis_handle.clone())
.remove::<Handle<StandardMaterial>>();
}); });
} }
@ -1167,50 +1243,17 @@ fn capture_piece(
(&mut Visibility, &mut Transform, &Side), (&mut Visibility, &mut Transform, &Side),
(With<Display3d>, With<game::Captured>), (With<Display3d>, With<game::Captured>),
>, >,
dissolving: Query<Entity, With<Dissolving>>,
mut state: Local<Option<game::CaptureFlow>>, mut state: Local<Option<game::CaptureFlow>>,
mut kids: Local<Vec<Entity>>,
mut prog: Local<f32>,
mut dissolve_materials: ResMut<Assets<DissolveMaterial>>,
object_standard_materials: Query<(
Entity,
&Handle<StandardMaterial>,
&Backup<Handle<DissolveMaterial>>,
)>,
object_dissolve_materials: Query<(
Entity,
&Handle<DissolveMaterial>,
&Backup<Handle<StandardMaterial>>,
)>,
children: Query<&Children>,
mut commands: Commands, mut commands: Commands,
time: Res<Time>,
score: Res<game::Score>, score: Res<game::Score>,
) { ) {
let duration: f32 = 3.0;
match *state { match *state {
// State is None, so we need to initiate the animation by swapping the StadardMaterial for the DissolveMaterial // State is None, so we need to initiate the animation
None => { None => {
*state = events.iter().next().map(|entity| { *state = events.iter().next().map(|entity| {
// Reset dissolve progress // Insert the "Dissolving::Out" tag on the entity we want to fade out
*prog = 1.0; commands.entity(entity).insert(Dissolving::Out(3.0));
// store the kids we want to process
*kids = object_standard_materials
.iter_many(children.iter_descendants(entity))
.map(|(child, std_handle, Backup(dis_handle))| {
// Swap standard and dissolve material
commands
.entity(child)
.insert(dis_handle.clone())
.insert(Backup(std_handle.clone()))
.remove::<Handle<StandardMaterial>>()
.remove::<Backup<Handle<DissolveMaterial>>>();
// Return child entity to be processed later in flow
child
})
.collect();
// Set the next state to start fading out // Set the next state to start fading out
game::CaptureFlow::FadeOut(entity) game::CaptureFlow::FadeOut(entity)
@ -1219,39 +1262,12 @@ fn capture_piece(
Some(s) => { Some(s) => {
match s { match s {
game::CaptureFlow::FadeOut(_entity) => { game::CaptureFlow::FadeOut(_entity) => {
// Play fade-out animation // Wait for fade-out animation
{ // If all pieces are done dissolving
// Calculate how much of the animation has passed if dissolving.is_empty() {
let delta = time.delta_seconds() / duration; // Move to next state now that animation is done
*state = s.next();
// Set progress to current - delta // This takes effect after updating all children
*prog -= delta;
// If progress is less than zero
if *prog <= 0.0 {
// Set to exactly 0 for simplicity
*prog = 0.0;
// Move to next state now that animation is done
*state = s.next();
// This takes effect after updating all children
}
object_dissolve_materials.iter_many(kids.iter()).for_each(
|(_child, dis_handle, _)| {
let dissolve_material = dissolve_materials
.get_mut(dis_handle)
.expect("Get the dissolve material");
// Change the material's value to create animation
dissolve_material.extension.percentage = *prog;
debug!(
"Play fade out animation {:?} {:?}",
delta, dissolve_material.extension.percentage
);
},
);
} }
} }
game::CaptureFlow::Store(entity) => { game::CaptureFlow::Store(entity) => {
@ -1272,57 +1288,16 @@ fn capture_piece(
t.translation = t.translation =
capture_translation(side, score.captures(!*side).saturating_sub(1)); capture_translation(side, score.captures(!*side).saturating_sub(1));
commands.entity(entity).insert(Dissolving::In(3.0));
*state = s.next(); *state = s.next();
} }
game::CaptureFlow::FadeIn(entity) => { game::CaptureFlow::FadeIn(_entity) => {
let (mut v, _, _) = query // If we have completed the dissolve-in animation
.get_mut(entity) if dissolving.is_empty() {
.expect("Visibility and Transform of captured piece");
// Show piece now that it is moved
*v = Visibility::Inherited;
// Calculate how much of the animation has passed
let delta = time.delta_seconds() / duration;
// Move the animation forward by delta
*prog += delta;
// If we have completed the animation
if *prog >= 1.0 {
// Move to next state now that animation is done // Move to next state now that animation is done
*state = s.next(); *state = s.next();
} }
// Play fade-in animation
{
object_dissolve_materials.iter_many(kids.iter()).for_each(
|(child, dis_handle, Backup(std_handle))| {
let dissolve_material = dissolve_materials
.get_mut(dis_handle)
.expect("Get the dissolve material");
// Change the material's value to create animation
dissolve_material.extension.percentage = *prog;
debug!(
"Play fade in animation {:?} {:?}",
delta, dissolve_material.extension.percentage
);
// If we are done with the animation cleanup
if dissolve_material.extension.percentage >= 1.0 {
// Re-add the original material
commands
.entity(child)
.insert(std_handle.clone())
.insert(Backup(dis_handle.clone()))
.remove::<Handle<DissolveMaterial>>()
.remove::<Backup<Handle<StandardMaterial>>>();
}
},
);
}
} }
} }
} }
@ -1374,3 +1349,77 @@ fn monitor_animations(
} }
}); });
} }
fn continue_title(mut next_state: ResMut<NextState<GameState>>) {
next_state.set(GameState::Play)
}
/// When a piece is tagged with `Dissolving` play the dissolve animation
/// This is done by updating the Material and referencing the `Dissolvable` tag
/// Calculating how far along the animation it should be update the material's percentage
/// Materials are on the children of the tagged entity
fn dissolve_animation(
mut query: Query<(Entity, &mut Dissolvable, &mut Dissolving)>,
children: Query<&Children>,
// Used to create Handle<DissolveMaterial>
mut dissolve_materials: ResMut<Assets<DissolveMaterial>>,
object_materials: Query<(
Entity,
&Handle<DissolveMaterial>,
)>,
mut commands: Commands,
time: Res<Time>,
) {
query.iter_mut().for_each(|(entity, dissolvable, mut dissolving)| {
// Calculate how much of the animation has passed
let delta = time.delta_seconds() / dissolvable.duration;
let percentage = match *dissolving {
Dissolving::In(mut sec) => {
// Decrease the amount of time left on this animation
sec -= time.delta_seconds();
// Check if seconds is below 0.0
if sec < 0.0 {
sec = 0.0;
}
// Calculate the target percentage value
sec / dissolvable.duration
}
Dissolving::Out(mut sec) => {
// Decrease the amount of time left on this animation
sec -= time.delta_seconds();
// Check if seconds is below 0.0
if sec < 0.0 {
sec = 0.0;
}
// Calculate the target percentage value
(dissolvable.duration - sec) / dissolvable.duration
}
};
object_materials.iter_many(children.iter_descendants(entity)).for_each(
|(_child, handle)| {
let dissolve_material = dissolve_materials
.get_mut(handle)
.expect("Get the dissolve material");
// Change the material's value to create animation
dissolve_material.extension.percentage = percentage;
debug!(
"Play fade out animation {:?} {:?}",
delta, dissolve_material.extension.percentage
);
},
);
// If animation is done, remove dissolving component
if percentage <= 0.0 || percentage >= 1.0 {
commands.entity(entity).remove::<Dissolving>();
}
});
}

@ -222,7 +222,7 @@ fn manage_scroll_text_animation(
}); });
} else { } else {
commands.entity(r).remove::<ui::TextScrollAnimation>(); commands.entity(r).remove::<ui::TextScrollAnimation>();
next_state.set(GameState::Play); next_state.set(GameState::Title);
} }
} }
}); });

@ -69,6 +69,7 @@ pub enum GameState {
Loading, Loading,
Credits, Credits,
Intro, Intro,
Title,
Play, Play,
Endgame, Endgame,
Restart, Restart,

@ -315,6 +315,9 @@ fn handle_escape(
} }
// State(Intro): Escape -> Play // State(Intro): Escape -> Play
GameState::Intro => { GameState::Intro => {
next_game_state.set(GameState::Title);
}
GameState::Title => {
next_game_state.set(GameState::Play); next_game_state.set(GameState::Play);
} }
GameState::Restart | GameState::Quit => { GameState::Restart | GameState::Quit => {

Loading…
Cancel
Save