Compare commits

...

2 Commits

Author SHA1 Message Date
Elijah C. Voigt a26102576b Banging my head against this animation bug... 2 years ago
Elijah Voigt 1219079c95 Most of the update for Bevy 0.12
Stuck on a weird Animation error...
2 years ago

913
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -4,11 +4,21 @@ version = "0.1.0"
edition = "2021" edition = "2021"
build = "build.rs" build = "build.rs"
[dependencies] [dependencies.bevy]
bevy_fmod = { version = "0.3", features = ["live-update"] } features = ["jpeg", "hdr", "file_watcher"]
bevy = { version = "0.11", features = ["jpeg", "hdr"] } version = "0.12"
serde = "1"
toml = { version = "0.8", features = ["parse"] } [dependencies.bevy_fmod]
features = ["live-update"]
git = "https://github.com/Salzian/bevy_fmod.git"
branch = "update-0.12"
[dependencies.serde]
version = "1"
[dependencies.toml]
version = "0.8"
features = ["parse"]
[profile.dev] [profile.dev]
opt-level = 3 opt-level = 3

@ -3,14 +3,16 @@
/// ///
use crate::prelude::*; use crate::prelude::*;
use bevy::{ use bevy::{
asset::{AssetLoader, LoadContext, LoadedAsset}, asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext},
prelude::*, prelude::*,
reflect::{TypePath, TypeUuid}, reflect::{TypePath, TypeUuid},
utils::thiserror,
utils::BoxedFuture, utils::BoxedFuture,
}; };
use bevy_fmod::prelude::AudioSource; use bevy_fmod::prelude::AudioSource;
use bevy_fmod::prelude::*; use bevy_fmod::prelude::*;
use serde::Deserialize; use serde::Deserialize;
use thiserror::Error;
pub(crate) struct AudioPlugin; pub(crate) struct AudioPlugin;
@ -28,8 +30,8 @@ impl Plugin for AudioPlugin {
"./assets/audio/Martian Chess Audio/Build/Desktop/SFX.bank", "./assets/audio/Martian Chess Audio/Build/Desktop/SFX.bank",
], ],
}); });
app.add_asset::<AudioSettings>() app.register_asset_loader(AudioSettingsLoader)
.init_asset_loader::<AudioSettingsLoader>(); .init_asset::<AudioSettings>();
app.add_systems(OnEnter(GameState::Menu), play_background); app.add_systems(OnEnter(GameState::Menu), play_background);
app.add_systems( app.add_systems(
@ -85,7 +87,7 @@ fn audio_trigger(
.expect("Load audio tweakfile"); .expect("Load audio tweakfile");
debug!("Audio tweaks: {:?}", tweak); debug!("Audio tweaks: {:?}", tweak);
let state = display_state.get(); let state = display_state.get();
events.iter().for_each(|event| { events.read().for_each(|event| {
let aud = match event { let aud = match event {
AudioEvent::MainMusic | AudioEvent::StopMainMusic => tweak.music.main.clone(), AudioEvent::MainMusic | AudioEvent::StopMainMusic => tweak.music.main.clone(),
AudioEvent::MenuSelect => tweak.menu.select.clone(), AudioEvent::MenuSelect => tweak.menu.select.clone(),
@ -136,9 +138,9 @@ fn audio_trigger(
}); });
} }
#[derive(Debug, Deserialize, TypeUuid, TypePath)] #[derive(Debug, Deserialize, TypeUuid, TypePath, Asset)]
#[uuid = "5c7a67ee-56a9-4fe9-9e91-1a709fbc3cf0"] #[uuid = "5c7a67ee-56a9-4fe9-9e91-1a709fbc3cf0"]
pub(crate) struct AudioSettings { pub struct AudioSettings {
#[serde(default)] #[serde(default)]
display2d: PlaySettings, display2d: PlaySettings,
#[serde(default)] #[serde(default)]
@ -173,20 +175,36 @@ struct PlaySettings {
invalid: String, invalid: String,
} }
#[derive(Debug, Error)]
pub enum AudioSettingsLoadError {
#[error("Could not read audio setting: {0}")]
LoadError(#[from] std::io::Error),
#[error("Could not load audio setting: {0}")]
ReadError(#[from] std::str::Utf8Error),
#[error("Could not parse audio settings: {0}")]
ParseError(#[from] toml::de::Error),
}
#[derive(Default)] #[derive(Default)]
pub struct AudioSettingsLoader; pub struct AudioSettingsLoader;
impl AssetLoader for AudioSettingsLoader { impl AssetLoader for AudioSettingsLoader {
type Asset = AudioSettings;
type Settings = ();
type Error = AudioSettingsLoadError;
fn load<'a>( fn load<'a>(
&'a self, &'a self,
bytes: &'a [u8], reader: &'a mut Reader,
load_context: &'a mut LoadContext, _settings: &'a Self::Settings,
) -> BoxedFuture<'a, Result<(), bevy::asset::Error>> { _load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
Box::pin(async move { Box::pin(async move {
let s = std::str::from_utf8(bytes)?; let mut bytes = Vec::new();
let custom_asset = toml::from_str::<AudioSettings>(s)?; reader.read_to_end(&mut bytes).await?;
load_context.set_default_asset(LoadedAsset::new(custom_asset)); let s = std::str::from_utf8(bytes.as_slice())?;
Ok(()) let custom_asset: AudioSettings = toml::from_str(s)?;
Ok(custom_asset)
}) })
} }

@ -1,10 +1,12 @@
use std::str::Utf8Error; use std::str::Utf8Error;
use bevy::{ use bevy::{
asset::{AssetLoader, LoadContext, LoadedAsset}, asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext},
reflect::{TypePath, TypeUuid}, reflect::{TypePath, TypeUuid},
utils::thiserror,
utils::BoxedFuture, utils::BoxedFuture,
}; };
use thiserror::Error;
use crate::prelude::*; use crate::prelude::*;
@ -12,8 +14,8 @@ pub(crate) struct CreditsPlugin;
impl Plugin for CreditsPlugin { impl Plugin for CreditsPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_asset::<CreditsText>() app.register_asset_loader(CreditsTextLoader)
.init_asset_loader::<CreditsTextLoader>() .init_asset::<CreditsText>()
.add_systems(Startup, init_credits_ui) .add_systems(Startup, init_credits_ui)
.add_systems( .add_systems(
Update, Update,
@ -27,25 +29,39 @@ impl Plugin for CreditsPlugin {
} }
} }
#[derive(Debug, TypeUuid, TypePath)] #[derive(Debug, TypeUuid, TypePath, Asset)]
#[uuid = "43df4a09-b5f0-4619-9223-8cf67dc9e844"] #[uuid = "43df4a09-b5f0-4619-9223-8cf67dc9e844"]
pub struct CreditsText { pub struct CreditsText {
sections: Vec<TextSection>, sections: Vec<TextSection>,
} }
#[derive(Debug, Error)]
enum CreditsTextLoadError {
#[error("Could not read audio setting: {0}")]
LoadError(#[from] std::io::Error),
#[error("Could not load audio setting: {0}")]
ReadError(#[from] std::str::Utf8Error),
}
#[derive(Default)] #[derive(Default)]
struct CreditsTextLoader; struct CreditsTextLoader;
impl AssetLoader for CreditsTextLoader { impl AssetLoader for CreditsTextLoader {
type Asset = CreditsText;
type Settings = ();
type Error = CreditsTextLoadError;
fn load<'a>( fn load<'a>(
&'a self, &'a self,
bytes: &'a [u8], reader: &'a mut Reader,
load_context: &'a mut LoadContext, _settings: &'a Self::Settings,
) -> BoxedFuture<'a, Result<(), bevy::asset::Error>> { _load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
Box::pin(async move { Box::pin(async move {
let custom_asset = parse_credits(bytes)?; let mut bytes = Vec::new();
load_context.set_default_asset(LoadedAsset::new(custom_asset)); reader.read_to_end(&mut bytes).await?;
Ok(()) let custom_asset = parse_credits(bytes.as_slice())?;
Ok(custom_asset)
}) })
} }
@ -111,11 +127,13 @@ fn update_credits(
credits_texts: Res<Assets<CreditsText>>, credits_texts: Res<Assets<CreditsText>>,
mut query: Query<(&mut Text, &Handle<CreditsText>)>, mut query: Query<(&mut Text, &Handle<CreditsText>)>,
) { ) {
reader.iter().for_each(|event| match event { reader.read().for_each(|event| match event {
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { AssetEvent::Added { id }
| AssetEvent::Modified { id }
| AssetEvent::LoadedWithDependencies { id } => {
query query
.iter_mut() .iter_mut()
.filter(|(_, this_handle)| handle.clone() == (*this_handle).clone()) .filter(|(_, this_handle)| Handle::Weak(id.clone()) == (*this_handle).clone())
.for_each(|(mut text, this_handle)| { .for_each(|(mut text, this_handle)| {
if let Some(credits_text) = credits_texts.get(this_handle) { if let Some(credits_text) = credits_texts.get(this_handle) {
text.sections = credits_text.sections.clone(); text.sections = credits_text.sections.clone();

@ -1,9 +1,5 @@
use bevy::{ use bevy::{
asset::diagnostic::AssetCountDiagnosticsPlugin, diagnostic::DiagnosticsStore,
diagnostic::{
DiagnosticsStore, EntityCountDiagnosticsPlugin, FrameTimeDiagnosticsPlugin,
SystemInformationDiagnosticsPlugin,
},
input::{keyboard::KeyboardInput, ButtonState}, input::{keyboard::KeyboardInput, ButtonState},
utils::{hashbrown::hash_map::Iter, HashMap}, utils::{hashbrown::hash_map::Iter, HashMap},
}; };
@ -14,26 +10,17 @@ pub(crate) struct DebugPlugin;
impl Plugin for DebugPlugin { impl Plugin for DebugPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(( app.init_resource::<DebugInfo>()
FrameTimeDiagnosticsPlugin, .add_systems(Startup, init_debug_ui)
EntityCountDiagnosticsPlugin::default(), .add_systems(
AssetCountDiagnosticsPlugin::<Gltf>::default(), Update,
AssetCountDiagnosticsPlugin::<Image>::default(), (
AssetCountDiagnosticsPlugin::<Scene>::default(), toggle_debug_mode.run_if(on_event::<KeyboardInput>()),
AssetCountDiagnosticsPlugin::<Font>::default(), display_diagnostics.run_if(resource_exists::<DebugEnabled>()),
SystemInformationDiagnosticsPlugin::default(), toggle_debug_ui.run_if(resource_changed_or_removed::<DebugEnabled>()),
)) camera_info.run_if(resource_exists::<DebugEnabled>()),
.init_resource::<DebugInfo>() ),
.add_systems(Startup, init_debug_ui) );
.add_systems(
Update,
(
toggle_debug_mode.run_if(on_event::<KeyboardInput>()),
display_diagnostics.run_if(resource_exists::<DebugEnabled>()),
toggle_debug_ui.run_if(resource_changed_or_removed::<DebugEnabled>()),
camera_info.run_if(resource_exists::<DebugEnabled>()),
),
);
} }
} }
@ -67,7 +54,7 @@ fn toggle_debug_mode(
mut commands: Commands, mut commands: Commands,
) { ) {
events events
.iter() .read()
.filter( .filter(
|KeyboardInput { |KeyboardInput {
state, key_code, .. state, key_code, ..
@ -132,11 +119,9 @@ fn display_diagnostics(
}); });
} }
fn camera_info( fn camera_info(mut debug_infos: ResMut<DebugInfo>, cameras: Query<(&Camera, &Name)>) {
mut debug_infos: ResMut<DebugInfo>, let camera_names = cameras
cameras: Query<(&Camera, &Name)>, .iter()
) {
let camera_names = cameras.iter()
.filter(|(c, _)| c.is_active) .filter(|(c, _)| c.is_active)
.map(|(_, n)| n.as_str()) .map(|(_, n)| n.as_str())
.collect::<Vec<&str>>() .collect::<Vec<&str>>()

@ -131,7 +131,7 @@ fn update_background(
mut events: EventReader<WindowResized>, mut events: EventReader<WindowResized>,
) { ) {
events events
.iter() .read()
.for_each(|&WindowResized { width, height, .. }| { .for_each(|&WindowResized { width, height, .. }| {
sprites.iter_mut().for_each(|mut sprite| { sprites.iter_mut().for_each(|mut sprite| {
sprite.custom_size = Some(Vec2 { sprite.custom_size = Some(Vec2 {

@ -3,6 +3,7 @@ use crate::{
prelude::*, prelude::*,
}; };
use bevy::{ use bevy::{
animation::RepeatAnimation,
core_pipeline::{tonemapping::DebandDither, Skybox}, core_pipeline::{tonemapping::DebandDither, Skybox},
input::mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel}, input::mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel},
render::{ render::{
@ -169,16 +170,18 @@ fn hydrate_camera(
assets_map: Res<AssetsMap>, assets_map: Res<AssetsMap>,
gltfs: Res<Assets<Gltf>>, gltfs: Res<Assets<Gltf>>,
state: Res<State<game::TurnState>>, state: Res<State<game::TurnState>>,
_clips: Res<Assets<AnimationClip>>,
mut players: Query<&mut AnimationPlayer>, mut players: Query<&mut AnimationPlayer>,
mut commands: Commands, mut commands: Commands,
) { ) {
events events
.iter() .iter()
.filter(|(name, _)| name.as_str() == "GameCam") .find(|(name, _)| name.as_str() == "GameCam")
.for_each(|(_, entity)| { .iter()
.for_each(|(_name, entity)| {
info!("Initialize 3d camera"); info!("Initialize 3d camera");
// Populate the components for the camera // Populate the components for the camera
commands.entity(entity).insert(( commands.entity(*entity).insert((
Display3d, Display3d,
DisplayState::Display3d, DisplayState::Display3d,
Camera3dBundle { Camera3dBundle {
@ -207,15 +210,19 @@ fn hydrate_camera(
let gltf = gltfs.get(&assets_map.models).expect("Load GLTF content"); let gltf = gltfs.get(&assets_map.models).expect("Load GLTF content");
// Set it to the default position by starting the initial animation // Set it to the default position by starting the initial animation
if let Ok(mut player) = players.get_mut(entity) { if let Ok(mut player) = players.get_mut(*entity) {
debug!("Animations: {:?}", gltf.named_animations.keys()); debug!("Animations: {:?}", gltf.named_animations.keys());
// GameCamIntro1, GameCamIntro2, GameCamSide1>2, GameCamSide2>1 // GameCamIntro1, GameCamIntro2, GameCamSide1>2, GameCamSide2>1
info!("Animation 1: {:?}", _clips.get(gltf.named_animations.get("GameCamIntro1").unwrap()));
info!("Animation 2: {:?}", _clips.get(gltf.named_animations.get("GameCamIntro2").unwrap()));
let animation = match state.get() { let animation = match state.get() {
game::TurnState::SideA => gltf.named_animations.get("GameCamIntro1"), game::TurnState::SideA => gltf.named_animations.get("GameCamIntro1"),
game::TurnState::SideB => gltf.named_animations.get("GameCamIntro2"), game::TurnState::SideB => gltf.named_animations.get("GameCamIntro2"),
}; }.expect("Camera startup");
info!("Compatible: {:?}", _clips.get(animation).unwrap().compatible_with(_name));
info!("Entity: {:?}", *entity);
player player
.play(animation.expect("Camera Startup").clone()) .play(animation.clone())
.pause(); .pause();
} }
}); });
@ -318,7 +325,7 @@ fn move_camera(
mut events: EventReader<MouseMotion>, mut events: EventReader<MouseMotion>,
mut camera: Query<&mut Transform, (With<Display3d>, With<Camera>)>, mut camera: Query<&mut Transform, (With<Display3d>, With<Camera>)>,
) { ) {
events.iter().for_each(|MouseMotion { delta }| { events.read().for_each(|MouseMotion { delta }| {
if buttons.pressed(MouseButton::Left) { if buttons.pressed(MouseButton::Left) {
camera.iter_mut().for_each(|mut t| { camera.iter_mut().for_each(|mut t| {
t.rotate_around(Vec3::ZERO, Quat::from_rotation_y(delta.x / 256.0)); t.rotate_around(Vec3::ZERO, Quat::from_rotation_y(delta.x / 256.0));
@ -333,7 +340,7 @@ fn mouse_zoom(
mut events: EventReader<MouseWheel>, mut events: EventReader<MouseWheel>,
mut camera: Query<&mut Transform, (With<Display3d>, With<Camera>)>, mut camera: Query<&mut Transform, (With<Display3d>, With<Camera>)>,
) { ) {
events.iter().for_each(|MouseWheel { unit, y, .. }| { events.read().for_each(|MouseWheel { unit, y, .. }| {
camera.iter_mut().for_each(|mut t| { camera.iter_mut().for_each(|mut t| {
match unit { match unit {
MouseScrollUnit::Line => { MouseScrollUnit::Line => {
@ -480,7 +487,7 @@ fn select(
mut selections: EventWriter<game::Selection>, mut selections: EventWriter<game::Selection>,
) { ) {
events events
.iter() .read()
.filter(|ev| ev.state == ButtonState::Pressed) .filter(|ev| ev.state == ButtonState::Pressed)
.for_each(|_| { .for_each(|_| {
windows.iter().for_each(|window| { windows.iter().for_each(|window| {
@ -586,7 +593,7 @@ fn put_down(
children: Query<&Children>, children: Query<&Children>,
mut players: Query<&mut AnimationPlayer>, mut players: Query<&mut AnimationPlayer>,
) { ) {
events.iter().for_each(|entity| { events.read().for_each(|entity| {
if let Ok(_piece) = query.get_mut(entity) { if let Ok(_piece) = query.get_mut(entity) {
let gltf = gltfs.get(&assets_map.models).expect("Load GLTF content"); let gltf = gltfs.get(&assets_map.models).expect("Load GLTF content");
children.iter_descendants(entity).for_each(|child| { children.iter_descendants(entity).for_each(|child| {
@ -598,7 +605,7 @@ fn put_down(
animation.expect("PutDown Animation").clone(), animation.expect("PutDown Animation").clone(),
Duration::from_secs_f32(0.75), Duration::from_secs_f32(0.75),
) )
.stop_repeating(); .set_repeat(RepeatAnimation::Never);
} }
}) })
} }

@ -423,7 +423,7 @@ pub(crate) fn update_board(
mut commands: Commands, mut commands: Commands,
mut played: Local<bool>, mut played: Local<bool>,
) { ) {
events.iter().for_each(|Move { from, to, .. }| { events.read().for_each(|Move { from, to, .. }| {
pieces.iter_mut().for_each(|(entity, mut index)| { pieces.iter_mut().for_each(|(entity, mut index)| {
if *index == *from { if *index == *from {
match to { match to {
@ -481,7 +481,7 @@ fn handle_selection(
mut done: Local<bool>, // Tracks if moves/audio submitted already even if multiple pieces (2d/3d) are moved. mut done: Local<bool>, // Tracks if moves/audio submitted already even if multiple pieces (2d/3d) are moved.
mut latest: Local<BoardIndex>, // Tracks the last one worked on mut latest: Local<BoardIndex>, // Tracks the last one worked on
) { ) {
selections.iter().for_each(|Selection(index)| { selections.read().for_each(|Selection(index)| {
// Skip indexes already processed // Skip indexes already processed
if *index != *latest { if *index != *latest {
// Set the latest index to the current index // Set the latest index to the current index

@ -1,3 +1,5 @@
use bevy::asset::{DependencyLoadState, RecursiveDependencyLoadState};
use crate::prelude::*; use crate::prelude::*;
pub(crate) struct LoadingPlugin; pub(crate) struct LoadingPlugin;
@ -57,20 +59,13 @@ fn loading(
tweakfile: Res<Assets<audio::AudioSettings>>, tweakfile: Res<Assets<audio::AudioSettings>>,
mut next_state: ResMut<NextState<GameState>>, mut next_state: ResMut<NextState<GameState>>,
) { ) {
let s_ids = sprites let s_ids = sprites.ids().collect::<Vec<AssetId<Image>>>();
.ids()
.filter(|&id| matches!(id, HandleId::AssetPathId(_)))
.collect::<Vec<HandleId>>();
let g_ids = gltfs let g_ids = gltfs.ids().collect::<Vec<AssetId<Gltf>>>();
.ids()
.filter(|&id| matches!(id, HandleId::AssetPathId(_)))
.collect::<Vec<HandleId>>();
let a_ids = tweakfile let a_ids = tweakfile
.ids() .ids()
.filter(|&id| matches!(id, HandleId::AssetPathId(_))) .collect::<Vec<AssetId<audio::AudioSettings>>>();
.collect::<Vec<HandleId>>();
debug!( debug!(
"Sprite len: {:?} | GLTF len: {:?} | Audio Tweakfile: {:?}", "Sprite len: {:?} | GLTF len: {:?} | Audio Tweakfile: {:?}",
@ -80,15 +75,36 @@ fn loading(
); );
if s_ids.len() > 0 && g_ids.len() > 0 { if s_ids.len() > 0 && g_ids.len() > 0 {
let s_ready = s_ids let s_ready = s_ids.iter().all(|&id| {
.iter() let load_state = server.get_load_states(id);
.all(|&id| server.get_load_state(id) == LoadState::Loaded); load_state
let g_ready = g_ids == Some((
.iter() LoadState::Loaded,
.all(|&id| server.get_load_state(id) == LoadState::Loaded); DependencyLoadState::Loaded,
let a_ready = a_ids RecursiveDependencyLoadState::Loaded,
.iter() ))
.all(|&id| server.get_load_state(id) == LoadState::Loaded); || load_state == None
});
let g_ready = g_ids.iter().all(|&id| {
let load_state = server.get_load_states(id);
load_state
== Some((
LoadState::Loaded,
DependencyLoadState::Loaded,
RecursiveDependencyLoadState::Loaded,
))
|| load_state == None
});
let a_ready = a_ids.iter().all(|&id| {
let load_state = server.get_load_states(id);
load_state
== Some((
LoadState::Loaded,
DependencyLoadState::Loaded,
RecursiveDependencyLoadState::Loaded,
))
|| load_state == None
});
if s_ready && g_ready && a_ready { if s_ready && g_ready && a_ready {
next_state.set(GameState::Menu) next_state.set(GameState::Menu)
} }

@ -15,10 +15,7 @@ mod ui;
use std::time::Duration; use std::time::Duration;
use bevy::{ use bevy::input::ButtonState;
asset::{ChangeWatcher, HandleId},
input::ButtonState,
};
use crate::prelude::*; use crate::prelude::*;
@ -55,10 +52,6 @@ fn main() {
..default() ..default()
}), }),
..default() ..default()
})
.set(AssetPlugin {
watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
..default()
}), }),
); );
app.add_plugins(credits::CreditsPlugin); app.add_plugins(credits::CreditsPlugin);

@ -187,7 +187,7 @@ pub(crate) fn exit_to_menu(
mut next_state: ResMut<NextState<GameState>>, mut next_state: ResMut<NextState<GameState>>,
) { ) {
events events
.iter() .read()
.filter( .filter(
|KeyboardInput { |KeyboardInput {
key_code, state, .. key_code, state, ..

Loading…
Cancel
Save