mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-11-26 13:32:32 +00:00
feat(animation): split animation logic/components into Blueprint vs Instance animations
* renamed existing animation components with Blueprint prefix * added almost identical but seperate InstanceAnimations & InstanceAnimationPlayerLink
This commit is contained in:
parent
27201e5cd6
commit
ce473a357e
@ -4,7 +4,7 @@ use bevy::utils::HashMap;
|
|||||||
#[derive(Component, Reflect, Default, Debug)]
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
/// storage for animations for a given entity (hierarchy), essentially a clone of gltf's `named_animations`
|
/// storage for animations for a given entity (hierarchy), essentially a clone of gltf's `named_animations`
|
||||||
pub struct Animations {
|
pub struct BlueprintAnimations {
|
||||||
pub named_animations: HashMap<String, Handle<AnimationClip>>,
|
pub named_animations: HashMap<String, Handle<AnimationClip>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,10 +13,25 @@ pub struct Animations {
|
|||||||
/// so that the root entity knows which of its children contains an actualy `AnimationPlayer` component
|
/// so that the root entity knows which of its children contains an actualy `AnimationPlayer` component
|
||||||
/// this is for convenience, because currently , Bevy's gltf parsing inserts `AnimationPlayers` "one level down"
|
/// this is for convenience, because currently , Bevy's gltf parsing inserts `AnimationPlayers` "one level down"
|
||||||
/// 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 AnimationPlayerLink(pub Entity);
|
pub struct BlueprintAnimationPlayerLink(pub Entity);
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Animated{
|
pub struct Animated{
|
||||||
pub animations: Vec<String>
|
pub animations: Vec<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
/// storage for animations for a given entity (hierarchy), essentially a clone of gltf's `named_animations`
|
||||||
|
pub struct InstanceAnimations {
|
||||||
|
pub named_animations: HashMap<String, Handle<AnimationClip>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
/// Stop gap helper component : this is inserted into a "root" entity (an entity representing a whole gltf file)
|
||||||
|
/// so that the root entity knows which of its children contains an actualy `AnimationPlayer` component
|
||||||
|
/// this is for convenience, because currently , Bevy's gltf parsing inserts `AnimationPlayers` "one level down"
|
||||||
|
/// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid
|
||||||
|
pub struct InstanceAnimationPlayerLink(pub Entity);
|
@ -119,8 +119,11 @@ impl Plugin for BlueprintsPlugin {
|
|||||||
.register_type::<BlueprintName>()
|
.register_type::<BlueprintName>()
|
||||||
.register_type::<MaterialInfo>()
|
.register_type::<MaterialInfo>()
|
||||||
.register_type::<SpawnHere>()
|
.register_type::<SpawnHere>()
|
||||||
.register_type::<Animations>()
|
|
||||||
|
.register_type::<BlueprintAnimations>()
|
||||||
|
.register_type::<InstanceAnimations>()
|
||||||
.register_type::<Animated>()
|
.register_type::<Animated>()
|
||||||
|
|
||||||
.register_type::<BlueprintsList>()
|
.register_type::<BlueprintsList>()
|
||||||
.register_type::<Vec<String>>()
|
.register_type::<Vec<String>>()
|
||||||
.register_type::<HashMap<String, Vec<String>>>()
|
.register_type::<HashMap<String, Vec<String>>>()
|
||||||
|
@ -2,7 +2,7 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use bevy::{gltf::Gltf, prelude::*, utils::HashMap};
|
use bevy::{gltf::Gltf, prelude::*, utils::HashMap};
|
||||||
|
|
||||||
use crate::{Animations, BluePrintsConfig};
|
use crate::{BlueprintAnimations, BluePrintsConfig};
|
||||||
|
|
||||||
/// this is a flag component for our levels/game world
|
/// this is a flag component for our levels/game world
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
@ -202,7 +202,6 @@ pub(crate) fn spawn_from_blueprints(
|
|||||||
Option<&Library>,
|
Option<&Library>,
|
||||||
Option<&AddToGameWorld>,
|
Option<&AddToGameWorld>,
|
||||||
Option<&Name>,
|
Option<&Name>,
|
||||||
Option<&Animations>
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
With<BlueprintAssetsLoaded>,
|
With<BlueprintAssetsLoaded>,
|
||||||
@ -228,7 +227,6 @@ pub(crate) fn spawn_from_blueprints(
|
|||||||
library_override,
|
library_override,
|
||||||
add_to_world,
|
add_to_world,
|
||||||
name,
|
name,
|
||||||
animations,
|
|
||||||
) in spawn_placeholders.iter()
|
) in spawn_placeholders.iter()
|
||||||
{
|
{
|
||||||
debug!(
|
debug!(
|
||||||
@ -283,15 +281,11 @@ pub(crate) fn spawn_from_blueprints(
|
|||||||
},
|
},
|
||||||
Spawned,
|
Spawned,
|
||||||
OriginalChildren(original_children),
|
OriginalChildren(original_children),
|
||||||
));
|
BlueprintAnimations { // these are animations specific to the inside of the blueprint
|
||||||
|
|
||||||
// only insert the animations if they are not present already: TODO ideally we want to be merging animations, though it could lead to clashes
|
|
||||||
if animations.is_none() {
|
|
||||||
commands.entity(entity).insert(Animations {
|
|
||||||
named_animations: gltf.named_animations.clone(),
|
named_animations: gltf.named_animations.clone(),
|
||||||
});
|
}
|
||||||
}
|
));
|
||||||
|
|
||||||
if add_to_world.is_some() {
|
if add_to_world.is_some() {
|
||||||
let world = game_world
|
let world = game_world
|
||||||
.get_single_mut()
|
.get_single_mut()
|
||||||
|
@ -5,7 +5,7 @@ use bevy::prelude::*;
|
|||||||
use bevy::scene::SceneInstance;
|
use bevy::scene::SceneInstance;
|
||||||
// use bevy::utils::hashbrown::HashSet;
|
// use bevy::utils::hashbrown::HashSet;
|
||||||
|
|
||||||
use super::{AnimationPlayerLink, Animations};
|
use super::{BlueprintAnimationPlayerLink, BlueprintAnimations};
|
||||||
use super::{SpawnHere, Spawned};
|
use super::{SpawnHere, Spawned};
|
||||||
use crate::{
|
use crate::{
|
||||||
AssetsToLoad, BlueprintAssetsLoaded, CopyComponents, InBlueprint, NoInBlueprint,
|
AssetsToLoad, BlueprintAssetsLoaded, CopyComponents, InBlueprint, NoInBlueprint,
|
||||||
@ -24,7 +24,7 @@ pub(crate) fn spawned_blueprint_post_process(
|
|||||||
Entity,
|
Entity,
|
||||||
&Children,
|
&Children,
|
||||||
&OriginalChildren,
|
&OriginalChildren,
|
||||||
&Animations,
|
&BlueprintAnimations,
|
||||||
Option<&NoInBlueprint>,
|
Option<&NoInBlueprint>,
|
||||||
Option<&Name>,
|
Option<&Name>,
|
||||||
),
|
),
|
||||||
@ -85,7 +85,7 @@ pub(crate) fn spawned_blueprint_post_process(
|
|||||||
// FIXME: stopgap solution: since we cannot use an AnimationPlayer at the root entity level
|
// FIXME: stopgap solution: since we cannot use an AnimationPlayer at the root entity level
|
||||||
// and we cannot update animation clips so that the EntityPaths point to one level deeper,
|
// and we cannot update animation clips so that the EntityPaths point to one level deeper,
|
||||||
// BUT we still want to have some marker/control at the root entity level, we add this
|
// BUT we still want to have some marker/control at the root entity level, we add this
|
||||||
commands.entity(original).insert(AnimationPlayerLink(added));
|
commands.entity(original).insert(BlueprintAnimationPlayerLink(added));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2969,6 +2969,17 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"typeInfo": "Struct"
|
"typeInfo": "Struct"
|
||||||
},
|
},
|
||||||
|
"bevy_example::game::Marker3": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"isComponent": true,
|
||||||
|
"isResource": false,
|
||||||
|
"properties": {},
|
||||||
|
"required": [],
|
||||||
|
"short_name": "Marker3",
|
||||||
|
"title": "bevy_example::game::Marker3",
|
||||||
|
"type": "object",
|
||||||
|
"typeInfo": "Struct"
|
||||||
|
},
|
||||||
"bevy_example::test_components::AComponentWithAnExtremlyExageratedOrMaybeNotButCouldBeNameOrWut": {
|
"bevy_example::test_components::AComponentWithAnExtremlyExageratedOrMaybeNotButCouldBeNameOrWut": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"isComponent": true,
|
"isComponent": true,
|
||||||
@ -3557,7 +3568,7 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"typeInfo": "Struct"
|
"typeInfo": "Struct"
|
||||||
},
|
},
|
||||||
"bevy_gltf_blueprints::animation::Animations": {
|
"bevy_gltf_blueprints::animation::BlueprintAnimations": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"isComponent": true,
|
"isComponent": true,
|
||||||
"isResource": false,
|
"isResource": false,
|
||||||
@ -3571,8 +3582,27 @@
|
|||||||
"required": [
|
"required": [
|
||||||
"named_animations"
|
"named_animations"
|
||||||
],
|
],
|
||||||
"short_name": "Animations",
|
"short_name": "BlueprintAnimations",
|
||||||
"title": "bevy_gltf_blueprints::animation::Animations",
|
"title": "bevy_gltf_blueprints::animation::BlueprintAnimations",
|
||||||
|
"type": "object",
|
||||||
|
"typeInfo": "Struct"
|
||||||
|
},
|
||||||
|
"bevy_gltf_blueprints::animation::InstanceAnimations": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"isComponent": true,
|
||||||
|
"isResource": false,
|
||||||
|
"properties": {
|
||||||
|
"named_animations": {
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/$defs/bevy_utils::hashbrown::HashMap<alloc::string::String, bevy_asset::handle::Handle<bevy_animation::AnimationClip>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"named_animations"
|
||||||
|
],
|
||||||
|
"short_name": "InstanceAnimations",
|
||||||
|
"title": "bevy_gltf_blueprints::animation::InstanceAnimations",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"typeInfo": "Struct"
|
"typeInfo": "Struct"
|
||||||
},
|
},
|
||||||
|
Binary file not shown.
@ -3,7 +3,7 @@ use std::{
|
|||||||
collections::HashMap, fs, time::Duration
|
collections::HashMap, fs, time::Duration
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy_gltf_blueprints::{Animated, AnimationPlayerLink, Animations, BlueprintName, BlueprintsList, GltfBlueprintsSet};
|
use bevy_gltf_blueprints::{Animated, BlueprintAnimationPlayerLink, BlueprintAnimations, InstanceAnimationPlayerLink, InstanceAnimations, BlueprintName, BlueprintsList, GltfBlueprintsSet};
|
||||||
pub use in_game::*;
|
pub use in_game::*;
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
@ -29,7 +29,7 @@ fn validate_export(
|
|||||||
children: Query<&Children>,
|
children: Query<&Children>,
|
||||||
names: Query<&Name>,
|
names: Query<&Name>,
|
||||||
blueprints: Query<(Entity, &Name, &BlueprintName)>,
|
blueprints: Query<(Entity, &Name, &BlueprintName)>,
|
||||||
animation_player_links: Query<(Entity, &AnimationPlayerLink)>,
|
animation_player_links: Query<(Entity, &BlueprintAnimationPlayerLink)>,
|
||||||
empties_candidates: Query<(Entity, &Name, &GlobalTransform)>,
|
empties_candidates: Query<(Entity, &Name, &GlobalTransform)>,
|
||||||
|
|
||||||
blueprints_list: Query<(Entity, &BlueprintsList)>,
|
blueprints_list: Query<(Entity, &BlueprintsList)>,
|
||||||
@ -141,7 +141,7 @@ fn setup_main_scene_animations(
|
|||||||
|
|
||||||
fn animations(
|
fn animations(
|
||||||
added_animation_players:Query<(Entity, &Name, &AnimationPlayer)>,
|
added_animation_players:Query<(Entity, &Name, &AnimationPlayer)>,
|
||||||
mut addded_animateds:Query<(Entity, &Name, &Animated),(Added<Animated>, Without<AnimationPlayerLink>)>,
|
addded_animateds:Query<(Entity, &Name, &Animated),(Added<Animated>)>,
|
||||||
|
|
||||||
animtest: Res<AnimTest>,
|
animtest: Res<AnimTest>,
|
||||||
|
|
||||||
@ -171,7 +171,7 @@ fn animations(
|
|||||||
// FIXME: for some reason this does NOT overwrite the component ??
|
// FIXME: for some reason this does NOT overwrite the component ??
|
||||||
|
|
||||||
commands.entity(entity).insert(
|
commands.entity(entity).insert(
|
||||||
Animations {
|
InstanceAnimations {
|
||||||
named_animations: gltf.named_animations.clone(),
|
named_animations: gltf.named_animations.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -183,7 +183,7 @@ fn animations(
|
|||||||
for ancestor in parents.iter_ancestors(entity) {
|
for ancestor in parents.iter_ancestors(entity) {
|
||||||
if added_animation_players.contains(ancestor) {
|
if added_animation_players.contains(ancestor) {
|
||||||
println!("found match with animationPlayer !! {:?}",names.get(ancestor));
|
println!("found match with animationPlayer !! {:?}",names.get(ancestor));
|
||||||
commands.entity(entity).insert(AnimationPlayerLink(ancestor));
|
commands.entity(entity).insert(InstanceAnimationPlayerLink(ancestor));
|
||||||
}
|
}
|
||||||
// info!("{:?} is an ancestor of {:?}", ancestor, player);
|
// info!("{:?} is an ancestor of {:?}", ancestor, player);
|
||||||
}
|
}
|
||||||
@ -192,8 +192,9 @@ fn animations(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn play_animations(
|
fn play_animations(
|
||||||
animated_marker1: Query<(&AnimationPlayerLink, &Animations), (With<Animated>, With<Marker1>)>,
|
animated_marker1: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations), (With<Animated>, With<Marker1>)>,
|
||||||
animated_marker2: Query<(&AnimationPlayerLink, &Animations), (With<Animated>, With<Marker2>)>,
|
animated_marker2: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations), (With<Animated>, With<Marker2>)>,
|
||||||
|
animated_marker3: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations, &BlueprintAnimationPlayerLink, &BlueprintAnimations), (With<Animated>, With<Marker3>)>,
|
||||||
|
|
||||||
mut animation_players: Query<&mut AnimationPlayer>,
|
mut animation_players: Query<&mut AnimationPlayer>,
|
||||||
keycode: Res<ButtonInput<KeyCode>>,
|
keycode: Res<ButtonInput<KeyCode>>,
|
||||||
@ -268,6 +269,43 @@ fn play_animations(
|
|||||||
.repeat();
|
.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
@ -280,11 +318,18 @@ pub struct Marker1;
|
|||||||
/// flag component for testing
|
/// flag component for testing
|
||||||
pub struct Marker2;
|
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) {
|
||||||
app.register_type::<Marker1>()
|
app.register_type::<Marker1>()
|
||||||
.register_type::<Marker2>()
|
.register_type::<Marker2>()
|
||||||
|
.register_type::<Marker3>()
|
||||||
|
|
||||||
.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)
|
||||||
.add_systems(OnEnter(AppState::MenuRunning), start_game)
|
.add_systems(OnEnter(AppState::MenuRunning), start_game)
|
||||||
|
Loading…
Reference in New Issue
Block a user