mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-11-22 20:00:53 +00:00
feat(animation):
* moved triggering of frame marker events to the blueprints crate * added additional handling of frame markers for blueprint animations (yikes is this convoluted) * added additional animated blueprint for testing blueprint animation markers
This commit is contained in:
parent
49dd0bc536
commit
66df7fae99
@ -80,3 +80,148 @@ pub struct AnimationMarkerReached {
|
|||||||
pub frame: u32,
|
pub frame: u32,
|
||||||
pub marker_name: String,
|
pub marker_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
|
||||||
|
/// triggers events when a given animation marker is reached for INSTANCE animations
|
||||||
|
pub fn trigger_instance_animation_markers_events(
|
||||||
|
animation_infos: Query<(
|
||||||
|
Entity,
|
||||||
|
&AnimationMarkers,
|
||||||
|
&InstanceAnimationPlayerLink,
|
||||||
|
&InstanceAnimations,
|
||||||
|
&AnimationInfos,
|
||||||
|
)>,
|
||||||
|
animation_players: Query<&AnimationPlayer>,
|
||||||
|
animation_clips: Res<Assets<AnimationClip>>,
|
||||||
|
mut animation_marker_events: EventWriter<AnimationMarkerReached>,
|
||||||
|
) {
|
||||||
|
for (entity, markers, link, animations, animation_infos) in animation_infos.iter() {
|
||||||
|
let animation_player = animation_players.get(link.0).unwrap();
|
||||||
|
let animation_clip = animation_clips.get(animation_player.animation_clip());
|
||||||
|
|
||||||
|
if animation_clip.is_some() {
|
||||||
|
// println!("Entity {:?} markers {:?}", entity, markers);
|
||||||
|
// println!("Player {:?} {}", animation_player.elapsed(), animation_player.completions());
|
||||||
|
// FIMXE: yikes ! very inneficient ! perhaps add boilerplate to the "start playing animation" code so we know what is playing
|
||||||
|
let animation_name = animations.named_animations.iter().find_map(|(key, value)| {
|
||||||
|
if value == animation_player.animation_clip() {
|
||||||
|
Some(key)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if animation_name.is_some() {
|
||||||
|
let animation_name = animation_name.unwrap();
|
||||||
|
|
||||||
|
let animation_length_seconds = animation_clip.unwrap().duration();
|
||||||
|
let animation_length_frames = animation_infos
|
||||||
|
.animations
|
||||||
|
.iter()
|
||||||
|
.find(|anim| &anim.name == animation_name)
|
||||||
|
.unwrap()
|
||||||
|
.frames_length;
|
||||||
|
// TODO: we also need to take playback speed into account
|
||||||
|
let time_in_animation = animation_player.elapsed()
|
||||||
|
- (animation_player.completions() as f32) * animation_length_seconds;
|
||||||
|
let frame_seconds =
|
||||||
|
(animation_length_frames as f32 / animation_length_seconds) * time_in_animation;
|
||||||
|
let frame = frame_seconds as u32;
|
||||||
|
|
||||||
|
let matching_animation_marker = &markers.0[animation_name];
|
||||||
|
if matching_animation_marker.contains_key(&frame) {
|
||||||
|
let matching_markers_per_frame = matching_animation_marker.get(&frame).unwrap();
|
||||||
|
// println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame);
|
||||||
|
// emit an event AnimationMarkerReached(entity, animation_name, frame, marker_name)
|
||||||
|
// FIXME: problem, this can fire multiple times in a row, depending on animation length , speed , etc
|
||||||
|
for marker_name in matching_markers_per_frame {
|
||||||
|
animation_marker_events.send(AnimationMarkerReached {
|
||||||
|
entity: entity,
|
||||||
|
animation_name: animation_name.clone(),
|
||||||
|
frame: frame,
|
||||||
|
marker_name: marker_name.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// triggers events when a given animation marker is reached for BLUEPRINT animations
|
||||||
|
pub fn trigger_blueprint_animation_markers_events(
|
||||||
|
animation_infos: Query<(
|
||||||
|
Entity,
|
||||||
|
&BlueprintAnimationPlayerLink,
|
||||||
|
&BlueprintAnimations,
|
||||||
|
)>,
|
||||||
|
// FIXME: annoying hiearchy issue yet again: the Markers & AnimationInfos are stored INSIDE the blueprint, so we need to access them differently
|
||||||
|
all_animation_infos: Query<(Entity, &AnimationMarkers, &AnimationInfos, &Parent)>,
|
||||||
|
animation_players: Query<&AnimationPlayer>,
|
||||||
|
animation_clips: Res<Assets<AnimationClip>>,
|
||||||
|
mut animation_marker_events: EventWriter<AnimationMarkerReached>,
|
||||||
|
) {
|
||||||
|
for (entity, link, animations) in animation_infos.iter() {
|
||||||
|
let animation_player = animation_players.get(link.0).unwrap();
|
||||||
|
let animation_clip = animation_clips.get(animation_player.animation_clip());
|
||||||
|
|
||||||
|
// FIXME: horrible code
|
||||||
|
let mut markers:Option<&AnimationMarkers>= None;
|
||||||
|
let mut animation_infos:Option<&AnimationInfos>=None;
|
||||||
|
for (_, _markers, _animation_infos, parent) in all_animation_infos.iter(){
|
||||||
|
if parent.get() == entity {
|
||||||
|
markers = Some(_markers);
|
||||||
|
animation_infos = Some(_animation_infos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if animation_clip.is_some() && markers.is_some() && animation_infos.is_some() {
|
||||||
|
let markers = markers.unwrap();
|
||||||
|
let animation_infos = animation_infos.unwrap();
|
||||||
|
|
||||||
|
// println!("Entity {:?} markers {:?}", entity, markers);
|
||||||
|
// println!("Player {:?} {}", animation_player.elapsed(), animation_player.completions());
|
||||||
|
// FIMXE: yikes ! very inneficient ! perhaps add boilerplate to the "start playing animation" code so we know what is playing
|
||||||
|
let animation_name = animations.named_animations.iter().find_map(|(key, value)| {
|
||||||
|
if value == animation_player.animation_clip() {
|
||||||
|
Some(key)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if animation_name.is_some() {
|
||||||
|
let animation_name = animation_name.unwrap();
|
||||||
|
let animation_length_seconds = animation_clip.unwrap().duration();
|
||||||
|
let animation_length_frames = animation_infos
|
||||||
|
.animations
|
||||||
|
.iter()
|
||||||
|
.find(|anim| &anim.name == animation_name)
|
||||||
|
.unwrap()
|
||||||
|
.frames_length;
|
||||||
|
// TODO: we also need to take playback speed into account
|
||||||
|
let time_in_animation = animation_player.elapsed()
|
||||||
|
- (animation_player.completions() as f32) * animation_length_seconds;
|
||||||
|
let frame_seconds =
|
||||||
|
(animation_length_frames as f32 / animation_length_seconds) * time_in_animation;
|
||||||
|
let frame = frame_seconds as u32;
|
||||||
|
|
||||||
|
let matching_animation_marker = &markers.0[animation_name];
|
||||||
|
if matching_animation_marker.contains_key(&frame) {
|
||||||
|
let matching_markers_per_frame = matching_animation_marker.get(&frame).unwrap();
|
||||||
|
// println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame);
|
||||||
|
// emit an event AnimationMarkerReached(entity, animation_name, frame, marker_name)
|
||||||
|
// FIXME: problem, this can fire multiple times in a row, depending on animation length , speed , etc
|
||||||
|
for marker_name in matching_markers_per_frame {
|
||||||
|
animation_marker_events.send(AnimationMarkerReached {
|
||||||
|
entity: entity,
|
||||||
|
animation_name: animation_name.clone(),
|
||||||
|
frame: frame,
|
||||||
|
marker_name: marker_name.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -177,6 +177,11 @@ impl Plugin for BlueprintsPlugin {
|
|||||||
(spawned_blueprint_post_process, apply_deferred)
|
(spawned_blueprint_post_process, apply_deferred)
|
||||||
.chain()
|
.chain()
|
||||||
.in_set(GltfBlueprintsSet::AfterSpawn),
|
.in_set(GltfBlueprintsSet::AfterSpawn),
|
||||||
);
|
)
|
||||||
|
|
||||||
|
|
||||||
|
.add_systems(Update, (trigger_instance_animation_markers_events, trigger_blueprint_animation_markers_events))
|
||||||
|
|
||||||
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2993,6 +2993,17 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"typeInfo": "Struct"
|
"typeInfo": "Struct"
|
||||||
},
|
},
|
||||||
|
"bevy_example::game::animation::MarkerFox": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"isComponent": true,
|
||||||
|
"isResource": false,
|
||||||
|
"properties": {},
|
||||||
|
"required": [],
|
||||||
|
"short_name": "MarkerFox",
|
||||||
|
"title": "bevy_example::game::animation::MarkerFox",
|
||||||
|
"type": "object",
|
||||||
|
"typeInfo": "Struct"
|
||||||
|
},
|
||||||
"bevy_example::test_components::AComponentWithAnExtremlyExageratedOrMaybeNotButCouldBeNameOrWut": {
|
"bevy_example::test_components::AComponentWithAnExtremlyExageratedOrMaybeNotButCouldBeNameOrWut": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"isComponent": true,
|
"isComponent": true,
|
||||||
|
Binary file not shown.
@ -24,6 +24,11 @@ pub struct Marker2;
|
|||||||
/// flag component for testing
|
/// flag component for testing
|
||||||
pub struct Marker3;
|
pub struct Marker3;
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
/// flag component for testing
|
||||||
|
pub struct MarkerFox;
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct AnimTest(Handle<Gltf>);
|
pub struct AnimTest(Handle<Gltf>);
|
||||||
|
|
||||||
@ -91,9 +96,32 @@ pub fn play_animations(
|
|||||||
(With<AnimationInfos>, With<Marker3>),
|
(With<AnimationInfos>, With<Marker3>),
|
||||||
>,
|
>,
|
||||||
|
|
||||||
|
animated_fox: Query<
|
||||||
|
(&BlueprintAnimationPlayerLink, &BlueprintAnimations),
|
||||||
|
(With<MarkerFox>),
|
||||||
|
>,
|
||||||
|
|
||||||
mut animation_players: Query<&mut AnimationPlayer>,
|
mut animation_players: Query<&mut AnimationPlayer>,
|
||||||
keycode: Res<ButtonInput<KeyCode>>,
|
keycode: Res<ButtonInput<KeyCode>>,
|
||||||
) {
|
) {
|
||||||
|
if keycode.just_pressed(KeyCode::KeyP) {
|
||||||
|
for (link, animations) in animated_fox.iter() {
|
||||||
|
println!("animations {:?}", animations.named_animations);
|
||||||
|
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
||||||
|
let anim_name = "Run";
|
||||||
|
animation_player
|
||||||
|
.play_with_transition(
|
||||||
|
animations
|
||||||
|
.named_animations
|
||||||
|
.get(anim_name)
|
||||||
|
.expect("animation name should be in the list")
|
||||||
|
.clone(),
|
||||||
|
Duration::from_secs(5),
|
||||||
|
)
|
||||||
|
.repeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if keycode.just_pressed(KeyCode::KeyM) {
|
if keycode.just_pressed(KeyCode::KeyM) {
|
||||||
for (link, animations) in animated_marker1.iter() {
|
for (link, animations) in animated_marker1.iter() {
|
||||||
println!("animations {:?}", animations.named_animations);
|
println!("animations {:?}", animations.named_animations);
|
||||||
@ -202,72 +230,6 @@ pub fn play_animations(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trigger_event_based_on_animation_marker(
|
|
||||||
animation_infos: Query<(
|
|
||||||
Entity,
|
|
||||||
&AnimationMarkers,
|
|
||||||
&InstanceAnimationPlayerLink,
|
|
||||||
&InstanceAnimations,
|
|
||||||
&AnimationInfos,
|
|
||||||
)>,
|
|
||||||
animation_players: Query<&AnimationPlayer>,
|
|
||||||
animation_clips: Res<Assets<AnimationClip>>,
|
|
||||||
mut animation_marker_events: EventWriter<AnimationMarkerReached>,
|
|
||||||
) {
|
|
||||||
for (entity, markers, link, animations, animation_infos) in animation_infos.iter() {
|
|
||||||
let animation_player = animation_players.get(link.0).unwrap();
|
|
||||||
let animation_clip = animation_clips.get(animation_player.animation_clip());
|
|
||||||
|
|
||||||
if animation_clip.is_some() {
|
|
||||||
// if marker_trackers.0.contains_key(k)
|
|
||||||
// marker_trackers.0
|
|
||||||
// println!("Entity {:?} markers {:?}", entity, markers);
|
|
||||||
// println!("Player {:?} {}", animation_player.elapsed(), animation_player.completions());
|
|
||||||
|
|
||||||
// FIMXE: yikes ! very inneficient ! perhaps add boilerplate to the "start playing animation" code so we know what is playing
|
|
||||||
let animation_name = animations.named_animations.iter().find_map(|(key, value)| {
|
|
||||||
if value == animation_player.animation_clip() {
|
|
||||||
Some(key)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if animation_name.is_some() {
|
|
||||||
let animation_name = animation_name.unwrap();
|
|
||||||
|
|
||||||
let animation_length_seconds = animation_clip.unwrap().duration();
|
|
||||||
let animation_length_frames = animation_infos
|
|
||||||
.animations
|
|
||||||
.iter()
|
|
||||||
.find(|anim| &anim.name == animation_name)
|
|
||||||
.unwrap()
|
|
||||||
.frames_length;
|
|
||||||
// TODO: we also need to take playback speed into account
|
|
||||||
let time_in_animation = animation_player.elapsed()
|
|
||||||
- (animation_player.completions() as f32) * animation_length_seconds;
|
|
||||||
let frame_seconds =
|
|
||||||
(animation_length_frames as f32 / animation_length_seconds) * time_in_animation;
|
|
||||||
let frame = frame_seconds as u32;
|
|
||||||
|
|
||||||
let matching_animation_marker = &markers.0[animation_name];
|
|
||||||
if matching_animation_marker.contains_key(&frame) {
|
|
||||||
let matching_markers_per_frame = matching_animation_marker.get(&frame).unwrap();
|
|
||||||
// println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame);
|
|
||||||
//emit an event , something like AnimationMarkerReached(entity, animation_name, frame, marker_name)
|
|
||||||
// FIXME: problem, this can fire multiple times in a row, depending on animation length , speed , etc
|
|
||||||
for marker_name in matching_markers_per_frame {
|
|
||||||
animation_marker_events.send(AnimationMarkerReached {
|
|
||||||
entity: entity,
|
|
||||||
animation_name: animation_name.clone(),
|
|
||||||
frame: frame,
|
|
||||||
marker_name: marker_name.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn react_to_animation_markers(
|
pub fn react_to_animation_markers(
|
||||||
mut animation_marker_events: EventReader<AnimationMarkerReached>,
|
mut animation_marker_events: EventReader<AnimationMarkerReached>,
|
||||||
|
@ -130,6 +130,7 @@ impl Plugin for GamePlugin {
|
|||||||
app.register_type::<Marker1>()
|
app.register_type::<Marker1>()
|
||||||
.register_type::<Marker2>()
|
.register_type::<Marker2>()
|
||||||
.register_type::<Marker3>()
|
.register_type::<Marker3>()
|
||||||
|
.register_type::<MarkerFox>()
|
||||||
|
|
||||||
.add_systems(Update, (spawn_test).run_if(in_state(GameState::InGame)))
|
.add_systems(Update, (spawn_test).run_if(in_state(GameState::InGame)))
|
||||||
.add_systems(Update, validate_export)
|
.add_systems(Update, validate_export)
|
||||||
@ -137,7 +138,7 @@ impl Plugin for GamePlugin {
|
|||||||
.add_systems(OnEnter(AppState::AppRunning), setup_game)
|
.add_systems(OnEnter(AppState::AppRunning), setup_game)
|
||||||
|
|
||||||
.add_systems(OnEnter(AppState::MenuRunning), setup_main_scene_animations)
|
.add_systems(OnEnter(AppState::MenuRunning), setup_main_scene_animations)
|
||||||
.add_systems(Update, (animations, trigger_event_based_on_animation_marker)
|
.add_systems(Update, (animations)
|
||||||
.run_if(in_state(AppState::AppRunning))
|
.run_if(in_state(AppState::AppRunning))
|
||||||
.after(GltfBlueprintsSet::AfterSpawn)
|
.after(GltfBlueprintsSet::AfterSpawn)
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user