Skip to content

Commit

Permalink
Enable AnimationGraphPlayer to pass parameters (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbrea-c committed Dec 13, 2023
1 parent d7742dd commit 92b6405
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 32 deletions.
23 changes: 6 additions & 17 deletions examples/human.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,17 @@ fn setup(
},
Human,
));

println!("Controls:");
println!("\tSPACE: Play/Pause animation");
println!("\tR: Reset animation");
println!("\tUp/Down: Increase/decrease movement speed");
}

fn keyboard_animation_control(
keyboard_input: Res<Input<KeyCode>>,
human_character: Query<&AnimatedSceneInstance, With<Human>>,
mut animation_players: Query<&mut AnimationGraphPlayer>,
mut animation_graphs: ResMut<Assets<AnimationGraph>>,
mut velocity: Local<f32>,
time: Res<Time>,
) {
Expand All @@ -94,29 +98,14 @@ fn keyboard_animation_control(
player.reset();
}

let Some(graph_handle) = player.get_animation_graph() else {
return;
};

let Some(graph) = animation_graphs.get_mut(graph_handle) else {
return;
};

let mut velocity_changed = false;
if keyboard_input.pressed(KeyCode::Up) {
*velocity += 0.5 * time.delta_seconds();
velocity_changed = true;
}
if keyboard_input.pressed(KeyCode::Down) {
*velocity -= 0.5 * time.delta_seconds();
velocity_changed = true;
}

*velocity = velocity.max(0.);

if velocity_changed {
println!("velocity: {}", *velocity);
}

graph.set_input_parameter("Target Speed", (*velocity).into());
player.set_input_parameter("Target Speed", (*velocity).into());
}
5 changes: 1 addition & 4 deletions src/core/animated_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,7 @@ pub(crate) fn process_animated_scenes(
commands
.entity(next_entity)
.remove::<AnimationPlayer>()
.insert(AnimationGraphPlayer {
animation: Some(animscn.animation_graph.clone()),
..Default::default()
});
.insert(AnimationGraphPlayer::new().with_graph(animscn.animation_graph.clone()));
commands
.entity(animscn_entity)
.insert(AnimatedSceneInstance {
Expand Down
21 changes: 14 additions & 7 deletions src/core/animation_graph/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -716,22 +716,29 @@ impl AnimationGraph {
time_update: TimeUpdate,
context: &mut GraphContext,
context_tmp: &mut GraphContextTmp,
) -> Pose {
self.query_with_overlay(time_update, context, context_tmp, &HashMap::new())
}

pub fn query_with_overlay(
&self,
time_update: TimeUpdate,
context: &mut GraphContext,
context_tmp: &mut GraphContextTmp,
overlay: &HashMap<String, AnimationNode>,
) -> Pose {
context.push_caches();
let overlay = HashMap::new();
self.parameter_pass(Self::OUTPUT_NODE, vec![], context, context_tmp, &overlay);
// self.dot_to_tmp_file(Some(context)).unwrap();
self.duration_pass(Self::OUTPUT_NODE, vec![], context, context_tmp, &overlay);
self.parameter_pass(Self::OUTPUT_NODE, vec![], context, context_tmp, overlay);
self.duration_pass(Self::OUTPUT_NODE, vec![], context, context_tmp, overlay);
self.time_pass(
Self::OUTPUT_NODE,
vec![],
time_update,
context,
context_tmp,
&overlay,
overlay,
);
self.time_dependent_pass(Self::OUTPUT_NODE, vec![], context, context_tmp, &overlay);
// self.dot_to_tmp_file(Some(context)).unwrap();
self.time_dependent_pass(Self::OUTPUT_NODE, vec![], context, context_tmp, overlay);

let output = context
.get_time_dependent(Self::OUTPUT_NODE, &vec![])
Expand Down
68 changes: 66 additions & 2 deletions src/core/animation_graph_player.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::{prelude::GraphContextTmp, utils::hash_map_join::HashMapOpsExt};

use super::{
animation_graph::{AnimationGraph, TimeState, TimeUpdate},
animation_graph::{AnimationGraph, EdgeValue, TimeState, TimeUpdate},
graph_context::GraphContext,
pose::Pose,
};
use bevy::{asset::prelude::*, ecs::prelude::*, reflect::prelude::*};
use bevy::{asset::prelude::*, ecs::prelude::*, reflect::prelude::*, utils::HashMap};

/// Animation controls
#[derive(Component, Default, Reflect)]
Expand All @@ -13,9 +16,45 @@ pub struct AnimationGraphPlayer {
pub(crate) elapsed: TimeState,
pub(crate) pending_update: Option<TimeUpdate>,
pub(crate) context: GraphContext,

input_parameters: HashMap<String, EdgeValue>,
}

impl AnimationGraphPlayer {
/// Create a new animation graph player, with no graph playing
pub fn new() -> Self {
Self {
paused: false,
animation: None,
elapsed: TimeState::default(),
pending_update: None,
context: GraphContext::default(),

input_parameters: HashMap::new(),
}
}

/// Set the animation graph to play
pub fn with_graph(mut self, animation: Handle<AnimationGraph>) -> Self {
self.animation = Some(animation);
self
}

/// Clear all input parameters for the animation graph
pub fn clear_input_parameter(&mut self) {
self.input_parameters.clear();
}

/// Configure an input parameter for the animation graph
pub fn set_input_parameter(&mut self, parameter_name: impl Into<String>, value: EdgeValue) {
self.input_parameters.insert(parameter_name.into(), value);
}

/// Return an input parameter for the animation graph
pub fn get_input_parameter(&self, parameter_name: &str) -> Option<EdgeValue> {
self.input_parameters.get(parameter_name).cloned()
}

/// Start playing an animation, resetting state of the player.
/// This will use a linear blending between the previous and the new animation to make a smooth transition.
pub fn start(&mut self, handle: Handle<AnimationGraph>) -> &mut Self {
Expand All @@ -25,6 +64,31 @@ impl AnimationGraphPlayer {
self
}

/// Query the animation graph with the latest time update and inputs
pub(crate) fn query(&mut self, context_tmp: &mut GraphContextTmp) -> Option<Pose> {
let Some(graph_handle) = &self.animation else {
return None;
};

let Some(graph) = context_tmp.animation_graph_assets.get(graph_handle) else {
return None;
};

let mut overlay_input_node = graph.nodes.get(AnimationGraph::INPUT_NODE).unwrap().clone();
overlay_input_node
.node
.unwrap_input_mut()
.parameters
.extend_replacing_owned(self.input_parameters.clone());

Some(graph.query_with_overlay(
self.elapsed.update,
&mut self.context,
context_tmp,
&HashMap::from([(AnimationGraph::INPUT_NODE.into(), overlay_input_node)]),
))
}

pub fn pause(&mut self) -> &mut Self {
self.paused = true;
self
Expand Down
4 changes: 2 additions & 2 deletions src/core/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,13 @@ pub fn run_animation_player(
animation_graph_assets: graphs,
};

let Some(graph) = graphs.get(player.animation.as_ref().unwrap()) else {
let Some(out_pose) = player.query(&mut context_tmp) else {
return;
};

// Apply the main animation
apply_pose(
&graph.query(player.elapsed.update, &mut player.context, &mut context_tmp),
&out_pose,
root,
names,
transforms,
Expand Down

0 comments on commit 92b6405

Please sign in to comment.