Store baked animation matrices in a component.
This commit is contained in:
@@ -16,14 +16,20 @@ use bevy::{
|
||||
prelude::*,
|
||||
reflect::TypePath,
|
||||
};
|
||||
use bevy_ggrs::prelude::GgrsSchedule;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
fighter::{animation::AnimKind, manifest::FighterManifest},
|
||||
fighter::{
|
||||
animation::{AnimKind, FighterAnimationFrame},
|
||||
manifest::FighterManifest,
|
||||
visual::FighterAnimations,
|
||||
},
|
||||
math::{
|
||||
int::{FGWide, FGi32},
|
||||
vec3::FGVec3,
|
||||
},
|
||||
schedule::GameplaySet,
|
||||
};
|
||||
|
||||
pub const BAKED_ANIMATION_FPS: u32 = 60;
|
||||
@@ -42,7 +48,7 @@ impl FixedMat4 {
|
||||
[FGi32::ZERO, FGi32::ONE, FGi32::ZERO, FGi32::ZERO],
|
||||
[FGi32::ZERO, FGi32::ZERO, FGi32::ONE, FGi32::ZERO],
|
||||
[FGi32::ZERO, FGi32::ZERO, FGi32::ZERO, FGi32::ONE],
|
||||
]
|
||||
],
|
||||
};
|
||||
fn from_mat4(matrix: Mat4) -> io::Result<Self> {
|
||||
let values = matrix.to_cols_array();
|
||||
@@ -151,11 +157,17 @@ pub struct BakedFighterAnimations {
|
||||
}
|
||||
|
||||
impl BakedFighterAnimations {
|
||||
pub fn sample(&self, kind: AnimKind, frame: u32, bone: &str) -> Option<FixedMat4> {
|
||||
let bone_index = self.bone_names.iter().position(|name| name == bone)?;
|
||||
let frames = &self.clip(kind)?.frames;
|
||||
let pose = frames.get(frame as usize).or_else(|| frames.last())?;
|
||||
pose.get(bone_index).copied()
|
||||
pub fn sample_pose(&self, frame: &FighterAnimationFrame) -> Option<&[FixedMat4]> {
|
||||
let frames = &self.clip(frame.kind)?.frames;
|
||||
let frame_index = if frame.repeat {
|
||||
frame.frame % frames.len() as u32
|
||||
} 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> {
|
||||
@@ -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)]
|
||||
struct FighterBakeDeclaration {
|
||||
manifest_path: String,
|
||||
@@ -304,7 +372,11 @@ impl Plugin for BakedAnimationPlugin {
|
||||
FighterBakeTransformer,
|
||||
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]
|
||||
fn fixed_matrix_transforms_points() {
|
||||
let transform = FixedMat4 {
|
||||
|
||||
+103
-78
@@ -4,19 +4,20 @@ use bevy::color::palettes::css::ORANGE;
|
||||
use bevy::{color::palettes::css::YELLOW, prelude::*};
|
||||
use bevy_egui::{EguiContexts, egui};
|
||||
|
||||
use crate::debug_tools::{DebugSettings, FighterDebugRowKind};
|
||||
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::state::StateNameDebug;
|
||||
use crate::fighter::visual::FighterAnimations;
|
||||
use crate::fighter::{
|
||||
FighterECB, FighterFacingDirection, FighterHitboxes, FighterPreviousTranslation, FighterTranslation, FighterVelocity,
|
||||
FighterECB, FighterFacingDirection, FighterHitboxes, FighterPreviousTranslation,
|
||||
FighterTranslation, FighterVelocity,
|
||||
};
|
||||
use crate::input::FighterInput;
|
||||
use crate::math::vec3::FGVec3;
|
||||
use crate::player::Player;
|
||||
use crate::scripting::FighterAttackScript;
|
||||
use crate::debug_tools::{DebugSettings, FighterDebugRowKind};
|
||||
|
||||
pub fn debug_draw_ecb(
|
||||
ecbs: Query<(
|
||||
@@ -57,7 +58,9 @@ pub fn fighter_debug(
|
||||
let mut fighters = query.iter().collect::<Vec<_>>();
|
||||
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 {
|
||||
continue;
|
||||
};
|
||||
@@ -65,95 +68,109 @@ pub fn fighter_debug(
|
||||
continue;
|
||||
}
|
||||
|
||||
egui::Window::new(format!("Player {}", player.handle + 1)).show(contexts.ctx_mut()?, |ui| {
|
||||
for row in rows {
|
||||
match row {
|
||||
FighterDebugRowKind::None => {}
|
||||
FighterDebugRowKind::CurrentState => {
|
||||
let frame_count = anims
|
||||
.get(&animations.baked)
|
||||
.and_then(|animation| animation.frame_count(animation_frame.kind));
|
||||
if let Some(frame_count) = frame_count {
|
||||
egui::Window::new(format!("Player {}", player.handle + 1)).show(
|
||||
contexts.ctx_mut()?,
|
||||
|ui| {
|
||||
for row in rows {
|
||||
match row {
|
||||
FighterDebugRowKind::None => {}
|
||||
FighterDebugRowKind::CurrentState => {
|
||||
let frame_count = anims
|
||||
.get(&animations.baked)
|
||||
.and_then(|animation| animation.frame_count(animation_frame.kind));
|
||||
if let Some(frame_count) = frame_count {
|
||||
ui.label(format!(
|
||||
"State: {:?} ({:?} {}/{})",
|
||||
state_name.0,
|
||||
animation_frame.kind,
|
||||
animation_frame.frame,
|
||||
frame_count
|
||||
));
|
||||
} else {
|
||||
ui.label(format!(
|
||||
"State: {:?} ({:?})",
|
||||
state_name.0, animation_frame.kind
|
||||
));
|
||||
}
|
||||
}
|
||||
FighterDebugRowKind::Velocity => {
|
||||
ui.label(format!(
|
||||
"State: {:?} ({:?} {}/{})",
|
||||
state_name.0, animation_frame.kind, animation_frame.frame, frame_count
|
||||
"Velocity: ({:.2}, {:.2})",
|
||||
velocity.0.x, velocity.0.y
|
||||
));
|
||||
} else {
|
||||
ui.label(format!("State: {:?} ({:?})", state_name.0, animation_frame.kind));
|
||||
}
|
||||
FighterDebugRowKind::Input => {
|
||||
const STICK_SIZE: f32 = 50.0;
|
||||
const STICK_SIZE_FRACTION: f32 = 0.75;
|
||||
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>(),
|
||||
);
|
||||
|
||||
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 painter = ui.painter();
|
||||
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 * movement,
|
||||
STICK_SIZE * STICK_SIZE_FRACTION * 0.5,
|
||||
button_bg,
|
||||
);
|
||||
ui.label(format!(
|
||||
"X: {:.2}\nY:{:.2}",
|
||||
last_frame.movement.x, last_frame.movement.y
|
||||
));
|
||||
});
|
||||
}
|
||||
FighterDebugRowKind::Position => {
|
||||
ui.label(format!("Position: {:?}", translation.0));
|
||||
}
|
||||
FighterDebugRowKind::Facing => {
|
||||
ui.label(format!("Facing: {:?}", facing));
|
||||
}
|
||||
}
|
||||
FighterDebugRowKind::Velocity => {
|
||||
ui.label(format!("Velocity: ({:.2}, {:.2})", velocity.0.x, velocity.0.y));
|
||||
}
|
||||
FighterDebugRowKind::Input => {
|
||||
const STICK_SIZE: f32 = 50.0;
|
||||
const STICK_SIZE_FRACTION: f32 = 0.75;
|
||||
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>());
|
||||
|
||||
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 painter = ui.painter();
|
||||
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 * movement,
|
||||
STICK_SIZE * STICK_SIZE_FRACTION * 0.5,
|
||||
button_bg,
|
||||
);
|
||||
ui.label(format!("X: {:.2}\nY:{:.2}", last_frame.movement.x, last_frame.movement.y));
|
||||
});
|
||||
}
|
||||
FighterDebugRowKind::Position => {
|
||||
ui.label(format!("Position: {:?}", translation.0));
|
||||
},
|
||||
FighterDebugRowKind::Facing => {
|
||||
ui.label(format!("Facing: {:?}", facing));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn animation_debug(
|
||||
query: Query<(&GlobalTransform, &FighterAnimationFrame, &FighterAnimations)>,
|
||||
baked_anims: Res<Assets<BakedFighterAnimations>>,
|
||||
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(
|
||||
out.cols[3][0].to_num(),
|
||||
out.cols[3][1].to_num(),
|
||||
out.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.axes(*global_transform, 1.0);
|
||||
}
|
||||
pub fn animation_debug(query: Query<(&GlobalTransform, &FighterBoneMatrices)>, mut gizmos: Gizmos) {
|
||||
for (global_transform, matrices) in query {
|
||||
for (_, matrix) in matrices.iter() {
|
||||
let pos = global_transform.transform_point(Vec3::new(
|
||||
matrix.cols[3][0].to_num(),
|
||||
matrix.cols[3][1].to_num(),
|
||||
matrix.cols[3][2].to_num(),
|
||||
));
|
||||
gizmos.cross(pos, 0.5, bevy::color::palettes::css::RED);
|
||||
gizmos.axes(*global_transform, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attack_debug(
|
||||
query: Query<(&FighterHitboxes, &FighterAnimationFrame, &FighterAnimations, &FighterTranslation, &FighterFacingDirection)>,
|
||||
baked_anims: Res<Assets<BakedFighterAnimations>>,
|
||||
query: Query<(
|
||||
&FighterHitboxes,
|
||||
&FighterBoneMatrices,
|
||||
&FighterTranslation,
|
||||
&FighterFacingDirection,
|
||||
)>,
|
||||
attack_scripts: Res<Assets<FighterAttackScript>>,
|
||||
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 {
|
||||
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);
|
||||
|
||||
for i in hitboxes.active_hitboxes.iter() {
|
||||
@@ -161,16 +178,24 @@ pub fn attack_debug(
|
||||
let radius = script.hitboxes[*i].radius;
|
||||
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 {
|
||||
let trf = player_trf.mul(trf);
|
||||
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 pos = trf.transform_point(offset);
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-16
@@ -7,6 +7,7 @@ use bevy_ggrs::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod animation;
|
||||
pub mod attack;
|
||||
pub mod attributes;
|
||||
pub mod baked_animation;
|
||||
pub mod collision;
|
||||
@@ -16,7 +17,6 @@ pub mod manifest;
|
||||
pub mod motion;
|
||||
pub mod state;
|
||||
pub mod visual;
|
||||
pub mod attack;
|
||||
|
||||
// The fighter components are re-exported so the rest of the crate can say
|
||||
// `fighter::FighterVelocity` without caring which file it lives in. Named
|
||||
@@ -29,8 +29,18 @@ pub use motion::{FighterPreviousTranslation, FighterTranslation, FighterVelocity
|
||||
|
||||
use crate::{
|
||||
fighter::{
|
||||
animation::animation_init, 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,
|
||||
animation::animation_init,
|
||||
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;
|
||||
|
||||
@@ -126,7 +136,10 @@ impl Plugin for FighterPlugin {
|
||||
)
|
||||
.add_systems(
|
||||
Update,
|
||||
(motion::apply_fighter_translation_to_visuals, animation::apply_animation)
|
||||
(
|
||||
motion::apply_fighter_translation_to_visuals,
|
||||
animation::apply_animation,
|
||||
)
|
||||
.chain()
|
||||
.run_if(in_state(crate::AppState::InMatch)),
|
||||
)
|
||||
@@ -171,7 +184,7 @@ impl Plugin for FighterPlugin {
|
||||
#[derive(Component, Clone)]
|
||||
pub struct FighterHitboxes {
|
||||
pub attack_script: Option<Handle<FighterAttackScript>>,
|
||||
pub active_hitboxes: Vec<usize>
|
||||
pub active_hitboxes: Vec<usize>,
|
||||
}
|
||||
|
||||
impl FighterHitboxes {
|
||||
@@ -195,18 +208,18 @@ pub fn spawn_fighter(
|
||||
Player {
|
||||
handle: player_handle,
|
||||
},
|
||||
Fighter {
|
||||
manifest: manifest,
|
||||
},
|
||||
Fighter { manifest: manifest },
|
||||
FighterFacingDirection::Right,
|
||||
(FighterECB {
|
||||
vertical_half: FGi32::lit("5.0"),
|
||||
horizontal_half: FGi32::lit("2.5"),
|
||||
},
|
||||
FighterHitboxes {
|
||||
attack_script: None,
|
||||
active_hitboxes: vec![]
|
||||
}),
|
||||
(
|
||||
FighterECB {
|
||||
vertical_half: FGi32::lit("5.0"),
|
||||
horizontal_half: FGi32::lit("2.5"),
|
||||
},
|
||||
FighterHitboxes {
|
||||
attack_script: None,
|
||||
active_hitboxes: vec![],
|
||||
},
|
||||
),
|
||||
(
|
||||
FighterVelocity::default(),
|
||||
FighterTranslation(spawn_position),
|
||||
@@ -217,6 +230,7 @@ pub fn spawn_fighter(
|
||||
attack_scripts,
|
||||
FighterState::Fall(FallState),
|
||||
animation::FighterAnimationFrame::new(animation::AnimKind::Wait, true),
|
||||
baked_animation::FighterBoneMatrices::default(),
|
||||
FighterVisual,
|
||||
FighterInput::default(),
|
||||
crate::camera::FighterCameraExtents::default(),
|
||||
|
||||
@@ -7,12 +7,13 @@ use bevy::prelude::*;
|
||||
|
||||
use crate::fighter::animation::{AnimKind, FighterAnimationFrame, FighterAnimationPlayerLink};
|
||||
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::manifest::FighterManifest;
|
||||
use crate::fighter::visual::FighterAnimations;
|
||||
use crate::fighter::{
|
||||
Fighter, FighterECB, FighterFacingDirection, FighterHitboxes, FighterPreviousTranslation, FighterTranslation, FighterVelocity,
|
||||
Fighter, FighterECB, FighterFacingDirection, FighterHitboxes, FighterPreviousTranslation,
|
||||
FighterTranslation, FighterVelocity,
|
||||
};
|
||||
use crate::game_settings::GameSettings;
|
||||
use crate::input::FighterInput;
|
||||
@@ -24,13 +25,13 @@ pub mod air_dodge;
|
||||
pub mod dash;
|
||||
pub mod fall;
|
||||
pub mod ground;
|
||||
pub mod ground_attack;
|
||||
pub mod jump;
|
||||
pub mod land;
|
||||
pub mod run;
|
||||
pub mod turn;
|
||||
pub mod wait;
|
||||
pub mod walk;
|
||||
pub mod ground_attack;
|
||||
|
||||
macro_rules! fighter_states {
|
||||
($($variant:ident => $ty:ty),* $(,)?) => {
|
||||
@@ -131,11 +132,6 @@ impl FighterStateContext<'_> {
|
||||
.expect("Current animation should have baked frames")
|
||||
< 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! {
|
||||
@@ -282,7 +278,7 @@ pub fn state_interrupt_system(
|
||||
animations,
|
||||
animation_player_link,
|
||||
fighter,
|
||||
attack_script_assets
|
||||
attack_script_assets,
|
||||
} = fighter;
|
||||
let Ok((mut animation_player, mut animation_transitions)) =
|
||||
animation_players.get_mut(animation_player_link.player())
|
||||
|
||||
+10
-4
@@ -21,14 +21,19 @@
|
||||
//! save, click Rebuild in jackdaw (or run `jd build`) and it
|
||||
//! 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_wind_waker_shader::prelude::*;
|
||||
use clap::Parser;
|
||||
use jackdaw_runtime::EditorCategory;
|
||||
|
||||
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;
|
||||
@@ -42,8 +47,8 @@ mod math;
|
||||
pub mod netcode;
|
||||
pub mod player;
|
||||
pub mod schedule;
|
||||
pub mod stage;
|
||||
pub mod scripting;
|
||||
pub mod stage;
|
||||
|
||||
#[derive(States, Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
|
||||
pub enum AppState {
|
||||
@@ -145,7 +150,8 @@ fn wait_for_asset_processor(
|
||||
processor: Option<Res<AssetProcessor>>,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+13
-15
@@ -6,8 +6,6 @@ use bevy::asset::AssetMode::Processed;
|
||||
use bevy::camera_controller::free_camera::FreeCameraPlugin;
|
||||
use bevy::image::{ImageAddressMode, ImagePlugin, ImageSamplerDescriptor};
|
||||
use bevy::prelude::*;
|
||||
use bevy::remote::RemotePlugin;
|
||||
use bevy::remote::http::RemoteHttpPlugin;
|
||||
use bevy_egui::EguiPlugin;
|
||||
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
||||
|
||||
@@ -15,17 +13,19 @@ fn main() -> AppExit {
|
||||
// A `Repeat` sampler so the tileable materials authored in the
|
||||
// editor render the same way at runtime; Bevy's default
|
||||
// `ClampToEdge` smears texture edges on brush geometry.
|
||||
let default_plugins = DefaultPlugins.set(ImagePlugin {
|
||||
default_sampler: ImageSamplerDescriptor {
|
||||
address_mode_u: ImageAddressMode::Repeat,
|
||||
address_mode_v: ImageAddressMode::Repeat,
|
||||
address_mode_w: ImageAddressMode::Repeat,
|
||||
..ImageSamplerDescriptor::linear()
|
||||
},
|
||||
}).set(AssetPlugin {
|
||||
mode: Processed,
|
||||
..default()
|
||||
});
|
||||
let default_plugins = DefaultPlugins
|
||||
.set(ImagePlugin {
|
||||
default_sampler: ImageSamplerDescriptor {
|
||||
address_mode_u: ImageAddressMode::Repeat,
|
||||
address_mode_v: ImageAddressMode::Repeat,
|
||||
address_mode_w: ImageAddressMode::Repeat,
|
||||
..ImageSamplerDescriptor::linear()
|
||||
},
|
||||
})
|
||||
.set(AssetPlugin {
|
||||
mode: Processed,
|
||||
..default()
|
||||
});
|
||||
|
||||
App::new()
|
||||
.add_plugins(default_plugins)
|
||||
@@ -33,8 +33,6 @@ fn main() -> AppExit {
|
||||
.add_plugins(jackdaw_runtime::JackdawPlugin)
|
||||
.add_plugins(game_test::GamePlugin)
|
||||
.add_plugins(FreeCameraPlugin)
|
||||
.add_plugins(RemotePlugin::default())
|
||||
.add_plugins(RemoteHttpPlugin::default())
|
||||
.add_plugins(EguiPlugin::default())
|
||||
.add_plugins(WorldInspectorPlugin::default()) // adds default options and `InspectorEguiImpl`s
|
||||
.insert_resource(Time::<Fixed>::from_hz(60.0))
|
||||
|
||||
Reference in New Issue
Block a user