refactor(animation): cleanups , reorg & tweaks
This commit is contained in:
parent
df3e335114
commit
2523691513
|
@ -29,8 +29,6 @@ pub struct InstanceAnimations {
|
||||||
/// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid
|
/// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid
|
||||||
pub struct InstanceAnimationPlayerLink(pub Entity);
|
pub struct InstanceAnimationPlayerLink(pub Entity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Stores Animation information: name, frame informations etc
|
/// Stores Animation information: name, frame informations etc
|
||||||
#[derive(Reflect, Default, Debug)]
|
#[derive(Reflect, Default, Debug)]
|
||||||
pub struct AnimationInfo {
|
pub struct AnimationInfo {
|
||||||
|
|
|
@ -110,6 +110,19 @@
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"typeInfo": "List"
|
"typeInfo": "List"
|
||||||
},
|
},
|
||||||
|
"alloc::vec::Vec<bevy_gltf_blueprints::animation::AnimationInfo>": {
|
||||||
|
"isComponent": false,
|
||||||
|
"isResource": false,
|
||||||
|
"items": {
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/bevy_gltf_blueprints::animation::AnimationInfo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"short_name": "Vec<AnimationInfo>",
|
||||||
|
"title": "alloc::vec::Vec<bevy_gltf_blueprints::animation::AnimationInfo>",
|
||||||
|
"type": "array",
|
||||||
|
"typeInfo": "List"
|
||||||
|
},
|
||||||
"alloc::vec::Vec<bevy_render::color::Color>": {
|
"alloc::vec::Vec<bevy_render::color::Color>": {
|
||||||
"isComponent": false,
|
"isComponent": false,
|
||||||
"isResource": false,
|
"isResource": false,
|
||||||
|
@ -2947,36 +2960,36 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"typeInfo": "Struct"
|
"typeInfo": "Struct"
|
||||||
},
|
},
|
||||||
"bevy_example::game::Marker1": {
|
"bevy_example::game::animation::Marker1": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"isComponent": true,
|
"isComponent": true,
|
||||||
"isResource": false,
|
"isResource": false,
|
||||||
"properties": {},
|
"properties": {},
|
||||||
"required": [],
|
"required": [],
|
||||||
"short_name": "Marker1",
|
"short_name": "Marker1",
|
||||||
"title": "bevy_example::game::Marker1",
|
"title": "bevy_example::game::animation::Marker1",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"typeInfo": "Struct"
|
"typeInfo": "Struct"
|
||||||
},
|
},
|
||||||
"bevy_example::game::Marker2": {
|
"bevy_example::game::animation::Marker2": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"isComponent": true,
|
"isComponent": true,
|
||||||
"isResource": false,
|
"isResource": false,
|
||||||
"properties": {},
|
"properties": {},
|
||||||
"required": [],
|
"required": [],
|
||||||
"short_name": "Marker2",
|
"short_name": "Marker2",
|
||||||
"title": "bevy_example::game::Marker2",
|
"title": "bevy_example::game::animation::Marker2",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"typeInfo": "Struct"
|
"typeInfo": "Struct"
|
||||||
},
|
},
|
||||||
"bevy_example::game::Marker3": {
|
"bevy_example::game::animation::Marker3": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"isComponent": true,
|
"isComponent": true,
|
||||||
"isResource": false,
|
"isResource": false,
|
||||||
"properties": {},
|
"properties": {},
|
||||||
"required": [],
|
"required": [],
|
||||||
"short_name": "Marker3",
|
"short_name": "Marker3",
|
||||||
"title": "bevy_example::game::Marker3",
|
"title": "bevy_example::game::animation::Marker3",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"typeInfo": "Struct"
|
"typeInfo": "Struct"
|
||||||
},
|
},
|
||||||
|
@ -3549,25 +3562,90 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"typeInfo": "Struct"
|
"typeInfo": "Struct"
|
||||||
},
|
},
|
||||||
"bevy_gltf_blueprints::animation::Animated": {
|
"bevy_gltf_blueprints::animation::AnimationInfo": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"isComponent": false,
|
||||||
|
"isResource": false,
|
||||||
|
"properties": {
|
||||||
|
"frame_end": {
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/f32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"frame_end_override": {
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/f32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"frame_start": {
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/f32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"frame_start_override": {
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/f32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"frames_length": {
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/f32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/alloc::string::String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"frame_start",
|
||||||
|
"frame_end",
|
||||||
|
"frames_length",
|
||||||
|
"frame_start_override",
|
||||||
|
"frame_end_override"
|
||||||
|
],
|
||||||
|
"short_name": "AnimationInfo",
|
||||||
|
"title": "bevy_gltf_blueprints::animation::AnimationInfo",
|
||||||
|
"type": "object",
|
||||||
|
"typeInfo": "Struct"
|
||||||
|
},
|
||||||
|
"bevy_gltf_blueprints::animation::AnimationInfos": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"isComponent": true,
|
"isComponent": true,
|
||||||
"isResource": false,
|
"isResource": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"animations": {
|
"animations": {
|
||||||
"type": {
|
"type": {
|
||||||
"$ref": "#/$defs/alloc::vec::Vec<alloc::string::String>"
|
"$ref": "#/$defs/alloc::vec::Vec<bevy_gltf_blueprints::animation::AnimationInfo>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"animations"
|
"animations"
|
||||||
],
|
],
|
||||||
"short_name": "Animated",
|
"short_name": "AnimationInfos",
|
||||||
"title": "bevy_gltf_blueprints::animation::Animated",
|
"title": "bevy_gltf_blueprints::animation::AnimationInfos",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"typeInfo": "Struct"
|
"typeInfo": "Struct"
|
||||||
},
|
},
|
||||||
|
"bevy_gltf_blueprints::animation::AnimationMarkers": {
|
||||||
|
"isComponent": true,
|
||||||
|
"isResource": false,
|
||||||
|
"items": false,
|
||||||
|
"prefixItems": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/bevy_utils::hashbrown::HashMap<alloc::string::String, bevy_utils::hashbrown::HashMap<u32, alloc::vec::Vec<alloc::string::String>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"short_name": "AnimationMarkers",
|
||||||
|
"title": "bevy_gltf_blueprints::animation::AnimationMarkers",
|
||||||
|
"type": "array",
|
||||||
|
"typeInfo": "TupleStruct"
|
||||||
|
},
|
||||||
"bevy_gltf_blueprints::animation::BlueprintAnimations": {
|
"bevy_gltf_blueprints::animation::BlueprintAnimations": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"isComponent": true,
|
"isComponent": true,
|
||||||
|
@ -10934,6 +11012,32 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"typeInfo": "Map"
|
"typeInfo": "Map"
|
||||||
},
|
},
|
||||||
|
"bevy_utils::hashbrown::HashMap<alloc::string::String, bevy_utils::hashbrown::HashMap<u32, alloc::vec::Vec<alloc::string::String>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": {
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/bevy_utils::hashbrown::HashMap<u32, alloc::vec::Vec<alloc::string::String>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isComponent": false,
|
||||||
|
"isResource": false,
|
||||||
|
"short_name": "HashMap<String, HashMap<u32, Vec<String>, DefaultHashBuilder>, DefaultHashBuilder>",
|
||||||
|
"title": "bevy_utils::hashbrown::HashMap<alloc::string::String, bevy_utils::hashbrown::HashMap<u32, alloc::vec::Vec<alloc::string::String>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>",
|
||||||
|
"type": "object",
|
||||||
|
"typeInfo": "Map"
|
||||||
|
},
|
||||||
|
"bevy_utils::hashbrown::HashMap<u32, alloc::vec::Vec<alloc::string::String>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": {
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/alloc::vec::Vec<alloc::string::String>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isComponent": false,
|
||||||
|
"isResource": false,
|
||||||
|
"short_name": "HashMap<u32, Vec<String>, DefaultHashBuilder>",
|
||||||
|
"title": "bevy_utils::hashbrown::HashMap<u32, alloc::vec::Vec<alloc::string::String>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>",
|
||||||
|
"type": "object",
|
||||||
|
"typeInfo": "Map"
|
||||||
|
},
|
||||||
"bevy_utils::smallvec::SmallVec<[bevy_ecs::entity::Entity; 8]>": {
|
"bevy_utils::smallvec::SmallVec<[bevy_ecs::entity::Entity; 8]>": {
|
||||||
"isComponent": false,
|
"isComponent": false,
|
||||||
"isResource": false,
|
"isResource": false,
|
||||||
|
|
|
@ -0,0 +1,249 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use bevy_gltf_blueprints::{AnimationInfos, AnimationMarkerReached, AnimationMarkerTrackers, AnimationMarkers, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, BlueprintsList, GltfBlueprintsSet, InstanceAnimationPlayerLink, InstanceAnimations};
|
||||||
|
|
||||||
|
use bevy::{
|
||||||
|
gltf::Gltf, prelude::*
|
||||||
|
};
|
||||||
|
use bevy_gltf_worlflow_examples_common_rapier::{AppState, GameState};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
/// flag component for testing
|
||||||
|
pub struct Marker1;
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
/// flag component for testing
|
||||||
|
pub struct Marker2;
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
/// flag component for testing
|
||||||
|
pub struct Marker3;
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct AnimTest(Handle<Gltf>);
|
||||||
|
|
||||||
|
|
||||||
|
pub fn setup_main_scene_animations(
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
mut commands: Commands,
|
||||||
|
) {
|
||||||
|
commands.insert_resource(AnimTest(asset_server.load("models/World.glb")));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn animations(
|
||||||
|
added_animation_players:Query<(Entity, &Name, &AnimationPlayer)>,
|
||||||
|
added_animation_infos:Query<(Entity, &Name, &AnimationInfos),(Added<AnimationInfos>)>,
|
||||||
|
animtest: Res<AnimTest>,
|
||||||
|
mut commands: Commands,
|
||||||
|
assets_gltf: Res<Assets<Gltf>>,
|
||||||
|
parents: Query<&Parent>,
|
||||||
|
) {
|
||||||
|
for (entity, name, animation_infos) in added_animation_infos.iter() {
|
||||||
|
//println!("animated stuf {:?} on entity {}", animation_infos, name);
|
||||||
|
let gltf = assets_gltf.get(&animtest.0).unwrap();
|
||||||
|
let mut matching_data = true;
|
||||||
|
for animation_info in &animation_infos.animations {
|
||||||
|
if !gltf.named_animations.contains_key(&animation_info.name){
|
||||||
|
matching_data = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matching_data {
|
||||||
|
println!("inserting Animations components into {} ({:?})", name, entity);
|
||||||
|
println!("Found match {:?}", gltf.named_animations);
|
||||||
|
commands.entity(entity).insert(
|
||||||
|
InstanceAnimations {
|
||||||
|
named_animations: gltf.named_animations.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
for ancestor in parents.iter_ancestors(entity) {
|
||||||
|
if added_animation_players.contains(ancestor) {
|
||||||
|
// println!("found match with animationPlayer !! {:?}",names.get(ancestor));
|
||||||
|
commands.entity(entity).insert(InstanceAnimationPlayerLink(ancestor));
|
||||||
|
}
|
||||||
|
// info!("{:?} is an ancestor of {:?}", ancestor, player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn play_animations(
|
||||||
|
animated_marker1: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations), (With<AnimationInfos>, With<Marker1>)>,
|
||||||
|
animated_marker2: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations), (With<AnimationInfos>, With<Marker2>)>,
|
||||||
|
animated_marker3: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations, &BlueprintAnimationPlayerLink, &BlueprintAnimations), (With<AnimationInfos>, With<Marker3>)>,
|
||||||
|
|
||||||
|
mut animation_players: Query<&mut AnimationPlayer>,
|
||||||
|
keycode: Res<ButtonInput<KeyCode>>,
|
||||||
|
|
||||||
|
) {
|
||||||
|
if keycode.just_pressed(KeyCode::KeyM) {
|
||||||
|
for (link, animations) in animated_marker1.iter() {
|
||||||
|
println!("animations {:?}", animations.named_animations);
|
||||||
|
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
||||||
|
let anim_name = "Blueprint1_move";
|
||||||
|
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::KeyJ) {
|
||||||
|
for (link, animations) in animated_marker1.iter() {
|
||||||
|
println!("animations {:?}", animations.named_animations);
|
||||||
|
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
||||||
|
let anim_name = "Blueprint1_jump";
|
||||||
|
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::KeyA) {
|
||||||
|
for (link, animations) in animated_marker2.iter() {
|
||||||
|
println!("animations {:?}", animations.named_animations);
|
||||||
|
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
||||||
|
let anim_name = "Blueprint1_move";
|
||||||
|
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::KeyB) {
|
||||||
|
for (link, animations) in animated_marker2.iter() {
|
||||||
|
println!("animations {:?}", animations.named_animations);
|
||||||
|
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
||||||
|
let anim_name = "Blueprint1_jump";
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// play instance animation
|
||||||
|
if keycode.just_pressed(KeyCode::KeyW) {
|
||||||
|
for (link, animations, _, _) in animated_marker3.iter() {
|
||||||
|
println!("animations {:?}", animations.named_animations);
|
||||||
|
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
||||||
|
let anim_name = "Blueprint8_move";
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// play blueprint animation
|
||||||
|
if keycode.just_pressed(KeyCode::KeyX) {
|
||||||
|
for (_, _, link, animations) in animated_marker3.iter() {
|
||||||
|
println!("animations {:?}", animations.named_animations);
|
||||||
|
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
||||||
|
let anim_name = "Walk";
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
mut animation_marker_events: EventReader<AnimationMarkerReached>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for event in animation_marker_events.read() {
|
||||||
|
println!("animation marker event {:?}", event)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
pub mod in_game;
|
pub mod in_game;
|
||||||
|
pub mod animation;
|
||||||
|
pub use in_game::*;
|
||||||
|
pub use animation::*;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap, fs, time::Duration
|
collections::HashMap, fs, time::Duration
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy_gltf_blueprints::{AnimationInfos, AnimationMarkerReached, AnimationMarkerTrackers, AnimationMarkers, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, BlueprintsList, GltfBlueprintsSet, InstanceAnimationPlayerLink, InstanceAnimations};
|
use bevy_gltf_blueprints::{AnimationInfos, AnimationMarkerReached, AnimationMarkerTrackers, AnimationMarkers, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, BlueprintsList, GltfBlueprintsSet, InstanceAnimationPlayerLink, InstanceAnimations};
|
||||||
pub use in_game::*;
|
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
ecs::query, gltf::Gltf, prelude::*, render::view::screenshot::ScreenshotManager, time::common_conditions::on_timer, window::PrimaryWindow
|
ecs::query, gltf::Gltf, prelude::*, render::view::screenshot::ScreenshotManager, time::common_conditions::on_timer, window::PrimaryWindow
|
||||||
|
@ -118,260 +121,6 @@ fn exit_game(mut app_exit_events: ResMut<Events<bevy::app::AppExit>>) {
|
||||||
app_exit_events.send(bevy::app::AppExit);
|
app_exit_events.send(bevy::app::AppExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
struct MainAnimations(Vec<Handle<AnimationClip>>);
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
struct AnimTest(Handle<Gltf>);
|
|
||||||
fn setup_main_scene_animations(
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
mut commands: Commands,
|
|
||||||
) {
|
|
||||||
/*commands.insert_resource(MainAnimations(vec![
|
|
||||||
asset_server.load("models/World.glb#Blueprint1_jump"),
|
|
||||||
asset_server.load("models/World.glb#Blueprint1_move"),
|
|
||||||
|
|
||||||
// asset_server.load("models/library/Blueprint6_animated.glb#Run"),
|
|
||||||
|
|
||||||
]));*/
|
|
||||||
|
|
||||||
commands.insert_resource(AnimTest(asset_server.load("models/World.glb")));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn animations(
|
|
||||||
added_animation_players:Query<(Entity, &Name, &AnimationPlayer)>,
|
|
||||||
added_animation_infos:Query<(Entity, &Name, &AnimationInfos),(Added<AnimationInfos>)>,
|
|
||||||
|
|
||||||
animtest: Res<AnimTest>,
|
|
||||||
|
|
||||||
mut commands: Commands,
|
|
||||||
assets_gltf: Res<Assets<Gltf>>,
|
|
||||||
|
|
||||||
parents: Query<&Parent>,
|
|
||||||
names: Query<&Name>,
|
|
||||||
) {
|
|
||||||
for (entity, name, animation_infos) in added_animation_infos.iter() {
|
|
||||||
//println!("animated stuf {:?} on entity {}", animation_infos, name);
|
|
||||||
let gltf = assets_gltf.get(&animtest.0).unwrap();
|
|
||||||
let mut matching_data = true;
|
|
||||||
for animation_info in &animation_infos.animations {
|
|
||||||
if !gltf.named_animations.contains_key(&animation_info.name){
|
|
||||||
matching_data = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if matching_data {
|
|
||||||
println!("inserting Animations components into {} ({:?})", name, entity);
|
|
||||||
println!("Found match {:?}", gltf.named_animations);
|
|
||||||
commands.entity(entity).insert(
|
|
||||||
InstanceAnimations {
|
|
||||||
named_animations: gltf.named_animations.clone(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
for ancestor in parents.iter_ancestors(entity) {
|
|
||||||
if added_animation_players.contains(ancestor) {
|
|
||||||
// println!("found match with animationPlayer !! {:?}",names.get(ancestor));
|
|
||||||
commands.entity(entity).insert(InstanceAnimationPlayerLink(ancestor));
|
|
||||||
}
|
|
||||||
// info!("{:?} is an ancestor of {:?}", ancestor, player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn play_animations(
|
|
||||||
animated_marker1: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations), (With<AnimationInfos>, With<Marker1>)>,
|
|
||||||
animated_marker2: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations), (With<AnimationInfos>, With<Marker2>)>,
|
|
||||||
animated_marker3: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations, &BlueprintAnimationPlayerLink, &BlueprintAnimations), (With<AnimationInfos>, With<Marker3>)>,
|
|
||||||
|
|
||||||
mut animation_players: Query<&mut AnimationPlayer>,
|
|
||||||
keycode: Res<ButtonInput<KeyCode>>,
|
|
||||||
|
|
||||||
) {
|
|
||||||
if keycode.just_pressed(KeyCode::KeyM) {
|
|
||||||
for (link, animations) in animated_marker1.iter() {
|
|
||||||
println!("animations {:?}", animations.named_animations);
|
|
||||||
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
|
||||||
let anim_name = "Blueprint1_move";
|
|
||||||
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::KeyJ) {
|
|
||||||
for (link, animations) in animated_marker1.iter() {
|
|
||||||
println!("animations {:?}", animations.named_animations);
|
|
||||||
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
|
||||||
let anim_name = "Blueprint1_jump";
|
|
||||||
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::KeyA) {
|
|
||||||
for (link, animations) in animated_marker2.iter() {
|
|
||||||
println!("animations {:?}", animations.named_animations);
|
|
||||||
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
|
||||||
let anim_name = "Blueprint1_move";
|
|
||||||
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::KeyB) {
|
|
||||||
for (link, animations) in animated_marker2.iter() {
|
|
||||||
println!("animations {:?}", animations.named_animations);
|
|
||||||
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
|
||||||
let anim_name = "Blueprint1_jump";
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// play instance animation
|
|
||||||
if keycode.just_pressed(KeyCode::KeyW) {
|
|
||||||
for (link, animations, _, _) in animated_marker3.iter() {
|
|
||||||
println!("animations {:?}", animations.named_animations);
|
|
||||||
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
|
||||||
let anim_name = "Blueprint8_move";
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// play blueprint animation
|
|
||||||
if keycode.just_pressed(KeyCode::KeyX) {
|
|
||||||
for (_, _, link, animations) in animated_marker3.iter() {
|
|
||||||
println!("animations {:?}", animations.named_animations);
|
|
||||||
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
|
||||||
let anim_name = "Walk";
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn react_to_animation_markers(
|
|
||||||
mut animation_marker_events: EventReader<AnimationMarkerReached>
|
|
||||||
)
|
|
||||||
{
|
|
||||||
for event in animation_marker_events.read() {
|
|
||||||
println!("animation marker event {:?}", event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
/// flag component for testing
|
|
||||||
pub struct Marker1;
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
/// flag component for testing
|
|
||||||
pub struct Marker2;
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
/// flag component for testing
|
|
||||||
pub struct Marker3;
|
|
||||||
|
|
||||||
pub struct GamePlugin;
|
pub struct GamePlugin;
|
||||||
impl Plugin for GamePlugin {
|
impl Plugin for GamePlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
|
|
Loading…
Reference in New Issue