Store baked animation matrices in a component.

This commit is contained in:
2026-08-31 20:06:15 +02:00
parent 4a2ec33d60
commit 159fbb2273
6 changed files with 285 additions and 130 deletions
+124 -8
View File
@@ -16,14 +16,20 @@ use bevy::{
prelude::*, prelude::*,
reflect::TypePath, reflect::TypePath,
}; };
use bevy_ggrs::prelude::GgrsSchedule;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
fighter::{animation::AnimKind, manifest::FighterManifest}, fighter::{
animation::{AnimKind, FighterAnimationFrame},
manifest::FighterManifest,
visual::FighterAnimations,
},
math::{ math::{
int::{FGWide, FGi32}, int::{FGWide, FGi32},
vec3::FGVec3, vec3::FGVec3,
}, },
schedule::GameplaySet,
}; };
pub const BAKED_ANIMATION_FPS: u32 = 60; pub const BAKED_ANIMATION_FPS: u32 = 60;
@@ -42,7 +48,7 @@ impl FixedMat4 {
[FGi32::ZERO, FGi32::ONE, FGi32::ZERO, FGi32::ZERO], [FGi32::ZERO, FGi32::ONE, FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::ZERO, FGi32::ONE, FGi32::ZERO], [FGi32::ZERO, FGi32::ZERO, FGi32::ONE, FGi32::ZERO],
[FGi32::ZERO, FGi32::ZERO, FGi32::ZERO, FGi32::ONE], [FGi32::ZERO, FGi32::ZERO, FGi32::ZERO, FGi32::ONE],
] ],
}; };
fn from_mat4(matrix: Mat4) -> io::Result<Self> { fn from_mat4(matrix: Mat4) -> io::Result<Self> {
let values = matrix.to_cols_array(); let values = matrix.to_cols_array();
@@ -151,11 +157,17 @@ pub struct BakedFighterAnimations {
} }
impl BakedFighterAnimations { impl BakedFighterAnimations {
pub fn sample(&self, kind: AnimKind, frame: u32, bone: &str) -> Option<FixedMat4> { pub fn sample_pose(&self, frame: &FighterAnimationFrame) -> Option<&[FixedMat4]> {
let bone_index = self.bone_names.iter().position(|name| name == bone)?; let frames = &self.clip(frame.kind)?.frames;
let frames = &self.clip(kind)?.frames; let frame_index = if frame.repeat {
let pose = frames.get(frame as usize).or_else(|| frames.last())?; frame.frame % frames.len() as u32
pose.get(bone_index).copied() } else {
frame.frame
};
frames
.get(frame_index as usize)
.or_else(|| frames.last())
.map(Vec::as_slice)
} }
pub fn frame_count(&self, kind: AnimKind) -> Option<u32> { pub fn frame_count(&self, kind: AnimKind) -> Option<u32> {
@@ -169,6 +181,62 @@ impl BakedFighterAnimations {
} }
} }
/// The current baked model-space pose for a fighter, recalculated every
/// rollback frame from its animation clock and immutable baked asset.
#[derive(Component, Clone, Debug, Default)]
pub struct FighterBoneMatrices {
bone_names: Vec<String>,
matrices: Vec<FixedMat4>,
}
impl FighterBoneMatrices {
pub fn get(&self, bone: &str) -> Option<FixedMat4> {
let index = self.bone_names.iter().position(|name| name == bone)?;
self.matrices.get(index).copied()
}
pub fn iter(&self) -> impl Iterator<Item = (&str, FixedMat4)> {
self.bone_names
.iter()
.map(String::as_str)
.zip(self.matrices.iter().copied())
}
fn set_pose(&mut self, bone_names: &[String], matrices: &[FixedMat4]) {
assert_eq!(
bone_names.len(),
matrices.len(),
"baked animation pose does not match its bone names"
);
if self.bone_names.is_empty() {
self.bone_names.extend_from_slice(bone_names);
} else {
assert_eq!(self.bone_names, bone_names, "baked bone names changed");
}
self.matrices.clear();
self.matrices.extend_from_slice(matrices);
}
}
pub fn update_fighter_bone_matrices(
mut fighters: Query<(
&FighterAnimationFrame,
&FighterAnimations,
&mut FighterBoneMatrices,
)>,
baked_assets: Res<Assets<BakedFighterAnimations>>,
) {
for (frame, animations, mut matrices) in &mut fighters {
let baked = baked_assets
.get(&animations.baked)
.expect("fighter baked animation asset should be loaded before the match starts");
let pose = baked
.sample_pose(frame)
.expect("fighter animation frame should select a baked pose");
matrices.set_pose(&baked.bone_names, pose);
}
}
#[derive(Deserialize)] #[derive(Deserialize)]
struct FighterBakeDeclaration { struct FighterBakeDeclaration {
manifest_path: String, manifest_path: String,
@@ -304,7 +372,11 @@ impl Plugin for BakedAnimationPlugin {
FighterBakeTransformer, FighterBakeTransformer,
BakedFighterAnimationsSaver, BakedFighterAnimationsSaver,
)) ))
.set_default_asset_processor::<FighterBakeProcessor>("fighterbake"); .set_default_asset_processor::<FighterBakeProcessor>("fighterbake")
.add_systems(
GgrsSchedule,
update_fighter_bone_matrices.in_set(GameplaySet::Animation),
);
} }
} }
@@ -598,6 +670,50 @@ mod tests {
); );
} }
#[test]
fn pose_sampling_wraps_repeating_frames_and_clamps_finished_frames() {
let first = FixedMat4::IDENTITY;
let mut second = FixedMat4::IDENTITY;
second.translate(FGVec3::lit("2", "0", "0"));
let baked = BakedFighterAnimations {
frame_rate: BAKED_ANIMATION_FPS,
bone_names: vec!["root".into()],
clips: vec![BakedAnimationClip {
frames: vec![vec![first], vec![second]],
}],
clip_indices: HashMap::from([(AnimKind::Wait, 0)]),
};
let repeating = FighterAnimationFrame {
kind: AnimKind::Wait,
frame: 3,
repeat: true,
};
let finished = FighterAnimationFrame {
kind: AnimKind::Wait,
frame: 3,
repeat: false,
};
assert_eq!(baked.sample_pose(&repeating), Some(&[second][..]));
assert_eq!(baked.sample_pose(&finished), Some(&[second][..]));
}
#[test]
fn bone_matrix_component_keeps_names_aligned_with_the_current_pose() {
let first = FixedMat4::IDENTITY;
let mut second = FixedMat4::IDENTITY;
second.translate(FGVec3::lit("0", "3", "0"));
let mut matrices = FighterBoneMatrices::default();
matrices.set_pose(&["root".into(), "hand".into()], &[first, second]);
assert_eq!(matrices.get("hand"), Some(second));
assert_eq!(
matrices.iter().collect::<Vec<_>>(),
vec![("root", first), ("hand", second)]
);
}
#[test] #[test]
fn fixed_matrix_transforms_points() { fn fixed_matrix_transforms_points() {
let transform = FixedMat4 { let transform = FixedMat4 {
+65 -40
View File
@@ -4,19 +4,20 @@ use bevy::color::palettes::css::ORANGE;
use bevy::{color::palettes::css::YELLOW, prelude::*}; use bevy::{color::palettes::css::YELLOW, prelude::*};
use bevy_egui::{EguiContexts, egui}; use bevy_egui::{EguiContexts, egui};
use crate::debug_tools::{DebugSettings, FighterDebugRowKind};
use crate::fighter::animation::FighterAnimationFrame; use crate::fighter::animation::FighterAnimationFrame;
use crate::fighter::baked_animation::BakedFighterAnimations; use crate::fighter::baked_animation::{BakedFighterAnimations, FighterBoneMatrices};
use crate::fighter::ecb::FighterPreviousECB; use crate::fighter::ecb::FighterPreviousECB;
use crate::fighter::state::StateNameDebug; use crate::fighter::state::StateNameDebug;
use crate::fighter::visual::FighterAnimations; use crate::fighter::visual::FighterAnimations;
use crate::fighter::{ use crate::fighter::{
FighterECB, FighterFacingDirection, FighterHitboxes, FighterPreviousTranslation, FighterTranslation, FighterVelocity, FighterECB, FighterFacingDirection, FighterHitboxes, FighterPreviousTranslation,
FighterTranslation, FighterVelocity,
}; };
use crate::input::FighterInput; use crate::input::FighterInput;
use crate::math::vec3::FGVec3; use crate::math::vec3::FGVec3;
use crate::player::Player; use crate::player::Player;
use crate::scripting::FighterAttackScript; use crate::scripting::FighterAttackScript;
use crate::debug_tools::{DebugSettings, FighterDebugRowKind};
pub fn debug_draw_ecb( pub fn debug_draw_ecb(
ecbs: Query<( ecbs: Query<(
@@ -57,7 +58,9 @@ pub fn fighter_debug(
let mut fighters = query.iter().collect::<Vec<_>>(); let mut fighters = query.iter().collect::<Vec<_>>();
fighters.sort_unstable_by_key(|(player, ..)| player.handle); fighters.sort_unstable_by_key(|(player, ..)| player.handle);
for (player, velocity, input, animation_frame, state_name, animations, translation, facing) in fighters { for (player, velocity, input, animation_frame, state_name, animations, translation, facing) in
fighters
{
let Some(rows) = settings.player_rows(player.handle) else { let Some(rows) = settings.player_rows(player.handle) else {
continue; continue;
}; };
@@ -65,7 +68,9 @@ pub fn fighter_debug(
continue; continue;
} }
egui::Window::new(format!("Player {}", player.handle + 1)).show(contexts.ctx_mut()?, |ui| { egui::Window::new(format!("Player {}", player.handle + 1)).show(
contexts.ctx_mut()?,
|ui| {
for row in rows { for row in rows {
match row { match row {
FighterDebugRowKind::None => {} FighterDebugRowKind::None => {}
@@ -76,84 +81,96 @@ pub fn fighter_debug(
if let Some(frame_count) = frame_count { if let Some(frame_count) = frame_count {
ui.label(format!( ui.label(format!(
"State: {:?} ({:?} {}/{})", "State: {:?} ({:?} {}/{})",
state_name.0, animation_frame.kind, animation_frame.frame, frame_count state_name.0,
animation_frame.kind,
animation_frame.frame,
frame_count
)); ));
} else { } else {
ui.label(format!("State: {:?} ({:?})", state_name.0, animation_frame.kind)); ui.label(format!(
"State: {:?} ({:?})",
state_name.0, animation_frame.kind
));
} }
} }
FighterDebugRowKind::Velocity => { FighterDebugRowKind::Velocity => {
ui.label(format!("Velocity: ({:.2}, {:.2})", velocity.0.x, velocity.0.y)); ui.label(format!(
"Velocity: ({:.2}, {:.2})",
velocity.0.x, velocity.0.y
));
} }
FighterDebugRowKind::Input => { FighterDebugRowKind::Input => {
const STICK_SIZE: f32 = 50.0; const STICK_SIZE: f32 = 50.0;
const STICK_SIZE_FRACTION: f32 = 0.75; const STICK_SIZE_FRACTION: f32 = 0.75;
let last_frame = input.get_last_frame(); let last_frame = input.get_last_frame();
let movement = egui::Vec2::new(last_frame.movement.x.to_num(), -last_frame.movement.y.to_num::<f32>()); let movement = egui::Vec2::new(
last_frame.movement.x.to_num(),
-last_frame.movement.y.to_num::<f32>(),
);
ui.horizontal(|ui| { ui.horizontal(|ui| {
let (_, rect) = ui.allocate_space(egui::Vec2::new(STICK_SIZE*(1.0 + STICK_SIZE_FRACTION), STICK_SIZE*(1.0 + STICK_SIZE_FRACTION))); let (_, rect) = ui.allocate_space(egui::Vec2::new(
STICK_SIZE * (1.0 + STICK_SIZE_FRACTION),
STICK_SIZE * (1.0 + STICK_SIZE_FRACTION),
));
let painter = ui.painter(); let painter = ui.painter();
let button_bg = egui::Rgba::WHITE * egui::Rgba::from_white_alpha(0.25); let button_bg =
egui::Rgba::WHITE * egui::Rgba::from_white_alpha(0.25);
painter.circle_filled(rect.center(), STICK_SIZE * 0.5, button_bg); painter.circle_filled(rect.center(), STICK_SIZE * 0.5, button_bg);
painter.circle_filled( painter.circle_filled(
rect.center() + STICK_SIZE * 0.5 * movement, rect.center() + STICK_SIZE * 0.5 * movement,
STICK_SIZE * STICK_SIZE_FRACTION * 0.5, STICK_SIZE * STICK_SIZE_FRACTION * 0.5,
button_bg, button_bg,
); );
ui.label(format!("X: {:.2}\nY:{:.2}", last_frame.movement.x, last_frame.movement.y)); ui.label(format!(
"X: {:.2}\nY:{:.2}",
last_frame.movement.x, last_frame.movement.y
));
}); });
} }
FighterDebugRowKind::Position => { FighterDebugRowKind::Position => {
ui.label(format!("Position: {:?}", translation.0)); ui.label(format!("Position: {:?}", translation.0));
}, }
FighterDebugRowKind::Facing => { FighterDebugRowKind::Facing => {
ui.label(format!("Facing: {:?}", facing)); ui.label(format!("Facing: {:?}", facing));
} }
} }
} }
}); },
);
} }
Ok(()) Ok(())
} }
pub fn animation_debug( pub fn animation_debug(query: Query<(&GlobalTransform, &FighterBoneMatrices)>, mut gizmos: Gizmos) {
query: Query<(&GlobalTransform, &FighterAnimationFrame, &FighterAnimations)>, for (global_transform, matrices) in query {
baked_anims: Res<Assets<BakedFighterAnimations>>, for (_, matrix) in matrices.iter() {
mut gizmos: Gizmos,
) {
for (global_transform, frame, anims) in query {
let baked: &BakedFighterAnimations = baked_anims
.get(&anims.baked)
.expect("Baked anims should be in");
for bone in &baked.bone_names {
if bone != "Bone_30" {
continue;
}
let out = anims.sample_bone(&baked_anims, frame, bone);
if let Some(out) = out {
let pos = global_transform.transform_point(Vec3::new( let pos = global_transform.transform_point(Vec3::new(
out.cols[3][0].to_num(), matrix.cols[3][0].to_num(),
out.cols[3][1].to_num(), matrix.cols[3][1].to_num(),
out.cols[3][2].to_num(), matrix.cols[3][2].to_num(),
)); ));
//gizmos.text(pos, bone, 1.0, Vec2::new(0.0, 0.0), bevy::color::palettes::css::BLACK);
gizmos.cross(pos, 0.5, bevy::color::palettes::css::RED); gizmos.cross(pos, 0.5, bevy::color::palettes::css::RED);
gizmos.axes(*global_transform, 1.0); gizmos.axes(*global_transform, 1.0);
} }
} }
} }
}
pub fn attack_debug( pub fn attack_debug(
query: Query<(&FighterHitboxes, &FighterAnimationFrame, &FighterAnimations, &FighterTranslation, &FighterFacingDirection)>, query: Query<(
baked_anims: Res<Assets<BakedFighterAnimations>>, &FighterHitboxes,
&FighterBoneMatrices,
&FighterTranslation,
&FighterFacingDirection,
)>,
attack_scripts: Res<Assets<FighterAttackScript>>, attack_scripts: Res<Assets<FighterAttackScript>>,
mut gizmos: Gizmos, mut gizmos: Gizmos,
) { ) {
for (hitboxes, frame, animations, translation, facing_direction) in query { for (hitboxes, matrices, translation, facing_direction) in query {
if let Some(script) = &hitboxes.attack_script { if let Some(script) = &hitboxes.attack_script {
let script = attack_scripts.get(script).expect("Script ref should be valid"); let script = attack_scripts
.get(script)
.expect("Script ref should be valid");
let player_trf = translation.get_3d_transform(facing_direction); let player_trf = translation.get_3d_transform(facing_direction);
for i in hitboxes.active_hitboxes.iter() { for i in hitboxes.active_hitboxes.iter() {
@@ -161,16 +178,24 @@ pub fn attack_debug(
let radius = script.hitboxes[*i].radius; let radius = script.hitboxes[*i].radius;
let bone = &script.hitboxes[*i].bone; let bone = &script.hitboxes[*i].bone;
let trf = animations.sample_bone(&baked_anims, frame, bone); let trf = matrices.get(bone);
if let Some(trf) = trf { if let Some(trf) = trf {
let trf = player_trf.mul(trf); let trf = player_trf.mul(trf);
let start = player_trf.transform_point(FGVec3::lit("0.0", "1.0", "0.0")); let start = player_trf.transform_point(FGVec3::lit("0.0", "1.0", "0.0"));
let end = player_trf.transform_point(FGVec3::lit("0.0", "1.0", "1.0")); let end = player_trf.transform_point(FGVec3::lit("0.0", "1.0", "1.0"));
let pos = trf.transform_point(offset); let pos = trf.transform_point(offset);
let pos_draw = Vec3::new(pos.x.to_num(), pos.y.to_num(), pos.z.to_num()); let pos_draw = Vec3::new(pos.x.to_num(), pos.y.to_num(), pos.z.to_num());
//Primitive
//gizmos.primitive_3d(, isometry, color)
gizmos.sphere(pos_draw, radius.to_num(), bevy::color::palettes::css::RED); gizmos.sphere(pos_draw, radius.to_num(), bevy::color::palettes::css::RED);
gizmos.arrow(start.to_vec3(), end.to_vec3(),bevy::color::palettes::css::RED); gizmos.arrow(
start.to_vec3(),
end.to_vec3(),
bevy::color::palettes::css::RED,
);
} else {
warn!("Bone not found required by attack script: {}", bone);
} }
} }
} }
+25 -11
View File
@@ -7,6 +7,7 @@ use bevy_ggrs::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub mod animation; pub mod animation;
pub mod attack;
pub mod attributes; pub mod attributes;
pub mod baked_animation; pub mod baked_animation;
pub mod collision; pub mod collision;
@@ -16,7 +17,6 @@ pub mod manifest;
pub mod motion; pub mod motion;
pub mod state; pub mod state;
pub mod visual; pub mod visual;
pub mod attack;
// The fighter components are re-exported so the rest of the crate can say // The fighter components are re-exported so the rest of the crate can say
// `fighter::FighterVelocity` without caring which file it lives in. Named // `fighter::FighterVelocity` without caring which file it lives in. Named
@@ -29,8 +29,18 @@ pub use motion::{FighterPreviousTranslation, FighterTranslation, FighterVelocity
use crate::{ use crate::{
fighter::{ fighter::{
animation::animation_init, attack::FighterAttackScriptAssets, manifest::FighterManifest, state::{FighterState, fall::FallState}, visual::FighterAnimations, animation::animation_init,
}, input::FighterInput, math::{int::FGi32, vec::FGVec2}, player::Player, schedule::GameplaySet, scripting::FighterAttackScript, stage::line::StageCollision, attack::FighterAttackScriptAssets,
manifest::FighterManifest,
state::{FighterState, fall::FallState},
visual::FighterAnimations,
},
input::FighterInput,
math::{int::FGi32, vec::FGVec2},
player::Player,
schedule::GameplaySet,
scripting::FighterAttackScript,
stage::line::StageCollision,
}; };
use state::state_interrupt_system; use state::state_interrupt_system;
@@ -126,7 +136,10 @@ impl Plugin for FighterPlugin {
) )
.add_systems( .add_systems(
Update, Update,
(motion::apply_fighter_translation_to_visuals, animation::apply_animation) (
motion::apply_fighter_translation_to_visuals,
animation::apply_animation,
)
.chain() .chain()
.run_if(in_state(crate::AppState::InMatch)), .run_if(in_state(crate::AppState::InMatch)),
) )
@@ -171,7 +184,7 @@ impl Plugin for FighterPlugin {
#[derive(Component, Clone)] #[derive(Component, Clone)]
pub struct FighterHitboxes { pub struct FighterHitboxes {
pub attack_script: Option<Handle<FighterAttackScript>>, pub attack_script: Option<Handle<FighterAttackScript>>,
pub active_hitboxes: Vec<usize> pub active_hitboxes: Vec<usize>,
} }
impl FighterHitboxes { impl FighterHitboxes {
@@ -195,18 +208,18 @@ pub fn spawn_fighter(
Player { Player {
handle: player_handle, handle: player_handle,
}, },
Fighter { Fighter { manifest: manifest },
manifest: manifest,
},
FighterFacingDirection::Right, FighterFacingDirection::Right,
(FighterECB { (
FighterECB {
vertical_half: FGi32::lit("5.0"), vertical_half: FGi32::lit("5.0"),
horizontal_half: FGi32::lit("2.5"), horizontal_half: FGi32::lit("2.5"),
}, },
FighterHitboxes { FighterHitboxes {
attack_script: None, attack_script: None,
active_hitboxes: vec![] active_hitboxes: vec![],
}), },
),
( (
FighterVelocity::default(), FighterVelocity::default(),
FighterTranslation(spawn_position), FighterTranslation(spawn_position),
@@ -217,6 +230,7 @@ pub fn spawn_fighter(
attack_scripts, attack_scripts,
FighterState::Fall(FallState), FighterState::Fall(FallState),
animation::FighterAnimationFrame::new(animation::AnimKind::Wait, true), animation::FighterAnimationFrame::new(animation::AnimKind::Wait, true),
baked_animation::FighterBoneMatrices::default(),
FighterVisual, FighterVisual,
FighterInput::default(), FighterInput::default(),
crate::camera::FighterCameraExtents::default(), crate::camera::FighterCameraExtents::default(),
+5 -9
View File
@@ -7,12 +7,13 @@ use bevy::prelude::*;
use crate::fighter::animation::{AnimKind, FighterAnimationFrame, FighterAnimationPlayerLink}; use crate::fighter::animation::{AnimKind, FighterAnimationFrame, FighterAnimationPlayerLink};
use crate::fighter::attack::FighterAttackScriptAssets; use crate::fighter::attack::FighterAttackScriptAssets;
use crate::fighter::baked_animation::{BakedFighterAnimations, FixedMat4}; use crate::fighter::baked_animation::BakedFighterAnimations;
use crate::fighter::ecb::FighterPreviousECB; use crate::fighter::ecb::FighterPreviousECB;
use crate::fighter::manifest::FighterManifest; use crate::fighter::manifest::FighterManifest;
use crate::fighter::visual::FighterAnimations; use crate::fighter::visual::FighterAnimations;
use crate::fighter::{ use crate::fighter::{
Fighter, FighterECB, FighterFacingDirection, FighterHitboxes, FighterPreviousTranslation, FighterTranslation, FighterVelocity, Fighter, FighterECB, FighterFacingDirection, FighterHitboxes, FighterPreviousTranslation,
FighterTranslation, FighterVelocity,
}; };
use crate::game_settings::GameSettings; use crate::game_settings::GameSettings;
use crate::input::FighterInput; use crate::input::FighterInput;
@@ -24,13 +25,13 @@ pub mod air_dodge;
pub mod dash; pub mod dash;
pub mod fall; pub mod fall;
pub mod ground; pub mod ground;
pub mod ground_attack;
pub mod jump; pub mod jump;
pub mod land; pub mod land;
pub mod run; pub mod run;
pub mod turn; pub mod turn;
pub mod wait; pub mod wait;
pub mod walk; pub mod walk;
pub mod ground_attack;
macro_rules! fighter_states { macro_rules! fighter_states {
($($variant:ident => $ty:ty),* $(,)?) => { ($($variant:ident => $ty:ty),* $(,)?) => {
@@ -131,11 +132,6 @@ impl FighterStateContext<'_> {
.expect("Current animation should have baked frames") .expect("Current animation should have baked frames")
< self.animation_frame.frame < self.animation_frame.frame
} }
pub fn sample_bone(&self, bone: &str) -> Option<FixedMat4> {
self.animations
.sample_bone(self.baked_animations, self.animation_frame, bone)
}
} }
fighter_states! { fighter_states! {
@@ -282,7 +278,7 @@ pub fn state_interrupt_system(
animations, animations,
animation_player_link, animation_player_link,
fighter, fighter,
attack_script_assets attack_script_assets,
} = fighter; } = fighter;
let Ok((mut animation_player, mut animation_transitions)) = let Ok((mut animation_player, mut animation_transitions)) =
animation_players.get_mut(animation_player_link.player()) animation_players.get_mut(animation_player_link.player())
+10 -4
View File
@@ -21,14 +21,19 @@
//! save, click Rebuild in jackdaw (or run `jd build`) and it //! save, click Rebuild in jackdaw (or run `jd build`) and it
//! appears in `Add Component`. No registration code is needed. //! appears in `Add Component`. No registration code is needed.
use bevy::{asset::processor::{AssetProcessor, ProcessorState}, prelude::*, tasks::block_on}; use bevy::{
asset::processor::{AssetProcessor, ProcessorState},
prelude::*,
tasks::block_on,
};
use bevy_asset_loader::prelude::*; use bevy_asset_loader::prelude::*;
use bevy_wind_waker_shader::prelude::*; use bevy_wind_waker_shader::prelude::*;
use clap::Parser; use clap::Parser;
use jackdaw_runtime::EditorCategory; use jackdaw_runtime::EditorCategory;
use crate::{ use crate::{
camera::MatchCameraPlugin, fighter::manifest::FighterManifest, game_settings::GameSettings, schedule::GameplaySchedulePlugin, stage::manifest::StageManifest, camera::MatchCameraPlugin, fighter::manifest::FighterManifest, game_settings::GameSettings,
schedule::GameplaySchedulePlugin, stage::manifest::StageManifest,
}; };
mod args; mod args;
@@ -42,8 +47,8 @@ mod math;
pub mod netcode; pub mod netcode;
pub mod player; pub mod player;
pub mod schedule; pub mod schedule;
pub mod stage;
pub mod scripting; pub mod scripting;
pub mod stage;
#[derive(States, Debug, Clone, Copy, Eq, PartialEq, Hash, Default)] #[derive(States, Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
pub enum AppState { pub enum AppState {
@@ -145,7 +150,8 @@ fn wait_for_asset_processor(
processor: Option<Res<AssetProcessor>>, processor: Option<Res<AssetProcessor>>,
mut next_state: ResMut<NextState<AppState>>, mut next_state: ResMut<NextState<AppState>>,
) { ) {
if processor.is_none_or(|processor| block_on(processor.get_state()) == ProcessorState::Finished) { if processor.is_none_or(|processor| block_on(processor.get_state()) == ProcessorState::Finished)
{
next_state.set(AppState::LoadCommonAssets); next_state.set(AppState::LoadCommonAssets);
} }
} }
+4 -6
View File
@@ -6,8 +6,6 @@ use bevy::asset::AssetMode::Processed;
use bevy::camera_controller::free_camera::FreeCameraPlugin; use bevy::camera_controller::free_camera::FreeCameraPlugin;
use bevy::image::{ImageAddressMode, ImagePlugin, ImageSamplerDescriptor}; use bevy::image::{ImageAddressMode, ImagePlugin, ImageSamplerDescriptor};
use bevy::prelude::*; use bevy::prelude::*;
use bevy::remote::RemotePlugin;
use bevy::remote::http::RemoteHttpPlugin;
use bevy_egui::EguiPlugin; use bevy_egui::EguiPlugin;
use bevy_inspector_egui::quick::WorldInspectorPlugin; use bevy_inspector_egui::quick::WorldInspectorPlugin;
@@ -15,14 +13,16 @@ fn main() -> AppExit {
// A `Repeat` sampler so the tileable materials authored in the // A `Repeat` sampler so the tileable materials authored in the
// editor render the same way at runtime; Bevy's default // editor render the same way at runtime; Bevy's default
// `ClampToEdge` smears texture edges on brush geometry. // `ClampToEdge` smears texture edges on brush geometry.
let default_plugins = DefaultPlugins.set(ImagePlugin { let default_plugins = DefaultPlugins
.set(ImagePlugin {
default_sampler: ImageSamplerDescriptor { default_sampler: ImageSamplerDescriptor {
address_mode_u: ImageAddressMode::Repeat, address_mode_u: ImageAddressMode::Repeat,
address_mode_v: ImageAddressMode::Repeat, address_mode_v: ImageAddressMode::Repeat,
address_mode_w: ImageAddressMode::Repeat, address_mode_w: ImageAddressMode::Repeat,
..ImageSamplerDescriptor::linear() ..ImageSamplerDescriptor::linear()
}, },
}).set(AssetPlugin { })
.set(AssetPlugin {
mode: Processed, mode: Processed,
..default() ..default()
}); });
@@ -33,8 +33,6 @@ fn main() -> AppExit {
.add_plugins(jackdaw_runtime::JackdawPlugin) .add_plugins(jackdaw_runtime::JackdawPlugin)
.add_plugins(game_test::GamePlugin) .add_plugins(game_test::GamePlugin)
.add_plugins(FreeCameraPlugin) .add_plugins(FreeCameraPlugin)
.add_plugins(RemotePlugin::default())
.add_plugins(RemoteHttpPlugin::default())
.add_plugins(EguiPlugin::default()) .add_plugins(EguiPlugin::default())
.add_plugins(WorldInspectorPlugin::default()) // adds default options and `InspectorEguiImpl`s .add_plugins(WorldInspectorPlugin::default()) // adds default options and `InspectorEguiImpl`s
.insert_resource(Time::<Fixed>::from_hz(60.0)) .insert_resource(Time::<Fixed>::from_hz(60.0))