-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Add Support for Triggering Events via AnimationEvents
#15538
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
mockersf
merged 104 commits into
bevyengine:main
from
atornity:animation-triggers-experiments
Oct 6, 2024
Merged
Changes from all commits
Commits
Show all changes
104 commits
Select commit
Hold shift + click to select a range
f5bfb0e
example init
atornity fa01cd1
Update Cargo.toml
atornity f970677
implement animation triggers
atornity 3d067cc
make target_id optional + rename example
atornity a34e47a
add animation events to animated_fox
atornity 6df2bf7
new add_trigger method variant
atornity 44a228f
rename trigger -> event
atornity 578f579
add some docs
atornity e9c5af1
update examples readme
atornity 463a3b0
edit comments
atornity 841e517
use Box<dyn AnimationEvent>
atornity fce8da2
module doc
atornity c292343
use core instead of std
atornity 6dee939
use core instead of std (again)
atornity 8a6aabf
add `AnimationTargetId::from_str`
atornity c544f7a
remove dbg
atornity c0e6d11
update examples
atornity a07df83
Merge remote-tracking branch 'upstream/main' into animation-triggers-…
atornity 4e14010
Merge branch 'main' into animation-triggers-experiments
atornity d2f1848
cleanup use
atornity 1ff5b54
fix typo
atornity 305deb8
rename events -> animation_events
atornity ba14f36
from_str -> from_iter
atornity d2ed656
rename animation_events -> animation_event
atornity b5d9a87
reflect comments
atornity da91ee3
Merge branch 'main' into animation-triggers-experiments
atornity 1e2ea51
reverse animation
atornity 3419bca
Merge branch 'animation-triggers-experiments' of https://github.com/a…
atornity a02cd8c
example comments
atornity ebb29f5
fix typo
atornity 06535ff
rename add_event_with_id -> add_event_to_target
atornity a5a5642
docs/cleanup add_event
atornity db74d8f
fix logic error
atornity ab10ea1
Update animation_events.rs
atornity e4f0ea4
create AnimationTriggerIter
atornity 49f9692
Update animation_events.rs
atornity 7f97667
remove unwrap
atornity 5a42a67
raname trigger -> events
atornity 30f146b
add `time` to `AnimationEvent::trigger`
atornity 09b6842
fix examples
atornity fd86ce5
include player entity in `AnimationEvent::trigger`
atornity 45b7b79
bunny detective example
atornity 12b0dcc
reset animated fox example
atornity 4118846
print example controls
atornity 5c17423
Merge branch 'main' into animation-triggers-experiments
atornity c5725fb
Merge branch 'animation-triggers-experiments' of https://github.com/a…
atornity e1cc404
don't use deprecated bundle
atornity ab599a6
Update README.md
atornity d77beab
fix bug
atornity c74da72
Update bunny_detective.rs
atornity 8636ea6
remove unwrap
atornity 7bffccc
ugh
atornity 86eee63
Merge branch 'main' into animation-triggers-experiments
atornity ec12ab9
Merge branch 'animation-triggers-experiments' of https://github.com/a…
atornity 2148f76
fix bugs + add tests + renames
atornity 47f525a
clippy + comments
atornity 511b0c8
Merge branch 'main' into animation-triggers-experiments
atornity a7931d6
Update lib.rs
atornity 7ef25ce
Merge branch 'animation-triggers-experiments' of https://github.com/a…
atornity f8fd00b
use required components
atornity 6b8665a
Update lib.rs
atornity dfd422f
fix iterator
atornity 1c41b98
Update animation_events.rs
atornity 5decc0e
remove `player_entity` from `AnimationEvent`
atornity fd8953a
fix trigger order
atornity 932ad5b
typos
atornity 17fb536
refactor
atornity 665d4b8
Merge remote-tracking branch 'upstream/main' into animation-triggers-…
atornity 65703dd
tests + fix bug
atornity d0e4b38
clippy
atornity 7afc9d5
remove sort
atornity 6ba9b71
Update animation_events.rs
atornity b0532d1
derive macro i guess
atornity 4f88b02
add instructions ui to example
atornity e763cb4
remove unused dependencies
atornity d9359eb
remove comments
atornity a244b4a
rename root bone + remove non-deform bones
atornity 710498e
add `CloneableAnimationEvent`
atornity 0165c29
update examples
atornity 7708aa1
typos
atornity 0aef794
add `weight` to `AnimationEvent`
atornity dbb4a60
move feet up from ground
atornity b7a559d
Update bunny_detective.rs
atornity 48964ec
Update crates/bevy_animation/src/animation_event.rs
atornity a13a094
implement suggestions
atornity 6d8703c
remove pub(crate)
atornity 1439455
Merge branch 'animation-triggers-experiments' of https://github.com/a…
atornity f8c2de0
typo
atornity 299355c
disable jumping input while jumping
atornity e0203d3
Update crates/bevy_animation/src/lib.rs
atornity 14a3302
add data to event in `animation_events.rs`
atornity 249d49b
rip buntective :(
atornity 7f8e504
Merge branch 'main' into animation-triggers-experiments
atornity a1b1f19
ci fix
atornity e0f0071
Update examples/animation/animated_fox.rs
atornity 805c4fb
explain how to calculate event time
atornity 92ee184
Merge branch 'main' into animation-triggers-experiments
atornity 2c4d578
Merge branch 'animation-triggers-experiments' of https://github.com/a…
atornity 1b5b903
observe_on_step
atornity 8e1d6ef
use required components
atornity f00700a
clarify that `time` is in seconds
atornity a192f67
runs -> triggered
atornity ea02940
`AnimationEvent` doc example
atornity 83a219e
Update examples/animation/animated_fox.rs
mockersf File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| [package] | ||
| name = "bevy_animation_derive" | ||
| version = "0.15.0-dev" | ||
| edition = "2021" | ||
| description = "Derive implementations for bevy_animation" | ||
| homepage = "https://bevyengine.org" | ||
| repository = "https://github.com/bevyengine/bevy" | ||
| license = "MIT OR Apache-2.0" | ||
| keywords = ["bevy"] | ||
|
|
||
| [lib] | ||
| proc-macro = true | ||
|
|
||
| [dependencies] | ||
| bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.15.0-dev" } | ||
| proc-macro2 = "1.0" | ||
| quote = "1.0" | ||
| syn = { version = "2.0", features = ["full"] } | ||
|
|
||
| [lints] | ||
| workspace = true | ||
|
|
||
| [package.metadata.docs.rs] | ||
| rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"] | ||
| all-features = true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| //! Derive macros for `bevy_animation`. | ||
|
|
||
| extern crate proc_macro; | ||
|
|
||
| use bevy_macro_utils::BevyManifest; | ||
| use proc_macro::TokenStream; | ||
| use quote::quote; | ||
| use syn::{parse_macro_input, DeriveInput}; | ||
|
|
||
| /// Used to derive `AnimationEvent` for a type. | ||
| #[proc_macro_derive(AnimationEvent)] | ||
| pub fn derive_animation_event(input: TokenStream) -> TokenStream { | ||
| let ast = parse_macro_input!(input as DeriveInput); | ||
| let name = ast.ident; | ||
| let manifest = BevyManifest::default(); | ||
| let bevy_animation_path = manifest.get_path("bevy_animation"); | ||
| let bevy_ecs_path = manifest.get_path("bevy_ecs"); | ||
| let animation_event_path = quote! { #bevy_animation_path::animation_event }; | ||
| let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); | ||
| // TODO: This could derive Event as well. | ||
| quote! { | ||
| impl #impl_generics #animation_event_path::AnimationEvent for #name #ty_generics #where_clause { | ||
| fn trigger(&self, _time: f32, _weight: f32, entity: #bevy_ecs_path::entity::Entity, world: &mut #bevy_ecs_path::world::World) { | ||
| world.entity_mut(entity).trigger(Clone::clone(self)); | ||
| } | ||
| } | ||
| } | ||
| .into() | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,281 @@ | ||
| //! Traits and types for triggering events from animations. | ||
|
|
||
| use core::{any::Any, fmt::Debug}; | ||
|
|
||
| use bevy_ecs::prelude::*; | ||
| use bevy_reflect::{ | ||
| prelude::*, utility::NonGenericTypeInfoCell, ApplyError, DynamicTupleStruct, FromType, | ||
| GetTypeRegistration, ReflectFromPtr, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, | ||
| TupleStructFieldIter, TupleStructInfo, TypeInfo, TypeRegistration, Typed, UnnamedField, | ||
| }; | ||
|
|
||
| pub use bevy_animation_derive::AnimationEvent; | ||
|
|
||
| pub(crate) fn trigger_animation_event( | ||
| entity: Entity, | ||
| time: f32, | ||
| weight: f32, | ||
| event: Box<dyn AnimationEvent>, | ||
| ) -> impl Command { | ||
| move |world: &mut World| { | ||
| event.trigger(time, weight, entity, world); | ||
| } | ||
| } | ||
|
|
||
| /// An event that can be used with animations. | ||
| /// It can be derived to trigger as an observer event, | ||
| /// if you need more complex behaviour, consider | ||
| /// a manual implementation. | ||
| /// | ||
| /// # Example | ||
| /// | ||
| /// ```rust | ||
| /// # use bevy_animation::prelude::*; | ||
| /// # use bevy_ecs::prelude::*; | ||
| /// # use bevy_reflect::prelude::*; | ||
| /// # use bevy_asset::prelude::*; | ||
| /// # | ||
| /// #[derive(Event, AnimationEvent, Reflect, Clone)] | ||
| /// struct Say(String); | ||
| /// | ||
| /// fn on_say(trigger: Trigger<Say>) { | ||
| /// println!("{}", trigger.event().0); | ||
| /// } | ||
| /// | ||
| /// fn setup_animation( | ||
| /// mut commands: Commands, | ||
| /// mut animations: ResMut<Assets<AnimationClip>>, | ||
| /// mut graphs: ResMut<Assets<AnimationGraph>>, | ||
| /// ) { | ||
| /// // Create a new animation and add an event at 1.0s. | ||
| /// let mut animation = AnimationClip::default(); | ||
| /// animation.add_event(1.0, Say("Hello".into())); | ||
| /// | ||
| /// // Create an animation graph. | ||
| /// let (graph, animation_index) = AnimationGraph::from_clip(animations.add(animation)); | ||
| /// | ||
| /// // Start playing the animation. | ||
| /// let mut player = AnimationPlayer::default(); | ||
| /// player.play(animation_index).repeat(); | ||
| /// | ||
| /// commands.spawn((graphs.add(graph), player)); | ||
| /// } | ||
| /// # | ||
| /// # bevy_ecs::system::assert_is_system(setup_animation); | ||
| /// ``` | ||
| #[reflect_trait] | ||
| pub trait AnimationEvent: CloneableAnimationEvent + Reflect + Send + Sync { | ||
| /// Trigger the event, targeting `entity`. | ||
| fn trigger(&self, time: f32, weight: f32, entity: Entity, world: &mut World); | ||
| } | ||
|
|
||
| /// This trait exist so that manual implementors of [`AnimationEvent`] | ||
| /// do not have to implement `clone_value`. | ||
| #[diagnostic::on_unimplemented( | ||
| message = "`{Self}` does not implement `Clone`", | ||
| note = "consider annotating `{Self}` with `#[derive(Clone)]`" | ||
| )] | ||
| pub trait CloneableAnimationEvent { | ||
| /// Clone this value into a new `Box<dyn AnimationEvent>` | ||
| fn clone_value(&self) -> Box<dyn AnimationEvent>; | ||
| } | ||
|
|
||
| impl<T: AnimationEvent + Clone> CloneableAnimationEvent for T { | ||
| fn clone_value(&self) -> Box<dyn AnimationEvent> { | ||
| Box::new(self.clone()) | ||
| } | ||
| } | ||
|
|
||
| /// The data that will be used to trigger an animation event. | ||
| #[derive(TypePath)] | ||
| pub(crate) struct AnimationEventData(pub(crate) Box<dyn AnimationEvent>); | ||
|
|
||
| impl AnimationEventData { | ||
| pub(crate) fn new(event: impl AnimationEvent) -> Self { | ||
| Self(Box::new(event)) | ||
| } | ||
| } | ||
|
|
||
| impl Debug for AnimationEventData { | ||
| fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| f.write_str("AnimationEventData(")?; | ||
| PartialReflect::debug(self.0.as_ref(), f)?; | ||
| f.write_str(")")?; | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| impl Clone for AnimationEventData { | ||
| fn clone(&self) -> Self { | ||
| Self(CloneableAnimationEvent::clone_value(self.0.as_ref())) | ||
| } | ||
| } | ||
|
|
||
| // We have to implement `GetTypeRegistration` manually because of the embedded | ||
| // `Box<dyn AnimationEvent>`, which can't be automatically derived yet. | ||
| impl GetTypeRegistration for AnimationEventData { | ||
| fn get_type_registration() -> TypeRegistration { | ||
| let mut registration = TypeRegistration::of::<Self>(); | ||
| registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type()); | ||
| registration | ||
| } | ||
| } | ||
|
|
||
| // We have to implement `Typed` manually because of the embedded | ||
| // `Box<dyn AnimationEvent>`, which can't be automatically derived yet. | ||
| impl Typed for AnimationEventData { | ||
| fn type_info() -> &'static TypeInfo { | ||
| static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); | ||
| CELL.get_or_set(|| { | ||
| TypeInfo::TupleStruct(TupleStructInfo::new::<Self>(&[UnnamedField::new::<()>(0)])) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // We have to implement `TupleStruct` manually because of the embedded | ||
| // `Box<dyn AnimationEvent>`, which can't be automatically derived yet. | ||
| impl TupleStruct for AnimationEventData { | ||
| fn field(&self, index: usize) -> Option<&dyn PartialReflect> { | ||
| match index { | ||
| 0 => Some(self.0.as_partial_reflect()), | ||
| _ => None, | ||
| } | ||
| } | ||
|
|
||
| fn field_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect> { | ||
| match index { | ||
| 0 => Some(self.0.as_partial_reflect_mut()), | ||
| _ => None, | ||
| } | ||
| } | ||
|
|
||
| fn field_len(&self) -> usize { | ||
| 1 | ||
| } | ||
|
|
||
| fn iter_fields(&self) -> TupleStructFieldIter { | ||
| TupleStructFieldIter::new(self) | ||
| } | ||
|
|
||
| fn clone_dynamic(&self) -> DynamicTupleStruct { | ||
| DynamicTupleStruct::from_iter([PartialReflect::clone_value(&*self.0)]) | ||
| } | ||
| } | ||
|
|
||
| // We have to implement `PartialReflect` manually because of the embedded | ||
| // `Box<dyn AnimationEvent>`, which can't be automatically derived yet. | ||
| impl PartialReflect for AnimationEventData { | ||
| #[inline] | ||
| fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { | ||
| Some(<Self as Typed>::type_info()) | ||
| } | ||
|
|
||
| #[inline] | ||
| fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> { | ||
| self | ||
| } | ||
|
|
||
| #[inline] | ||
| fn as_partial_reflect(&self) -> &dyn PartialReflect { | ||
| self | ||
| } | ||
|
|
||
| #[inline] | ||
| fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect { | ||
| self | ||
| } | ||
|
|
||
| fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> { | ||
| Ok(self) | ||
| } | ||
|
|
||
| #[inline] | ||
| fn try_as_reflect(&self) -> Option<&dyn Reflect> { | ||
| Some(self) | ||
| } | ||
|
|
||
| #[inline] | ||
| fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> { | ||
| Some(self) | ||
| } | ||
|
|
||
| fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> { | ||
| if let ReflectRef::TupleStruct(struct_value) = value.reflect_ref() { | ||
| for (i, value) in struct_value.iter_fields().enumerate() { | ||
| if let Some(v) = self.field_mut(i) { | ||
| v.try_apply(value)?; | ||
| } | ||
| } | ||
| } else { | ||
| return Err(ApplyError::MismatchedKinds { | ||
| from_kind: value.reflect_kind(), | ||
| to_kind: ReflectKind::TupleStruct, | ||
| }); | ||
| } | ||
| Ok(()) | ||
| } | ||
|
|
||
| fn reflect_ref(&self) -> ReflectRef { | ||
| ReflectRef::TupleStruct(self) | ||
| } | ||
|
|
||
| fn reflect_mut(&mut self) -> ReflectMut { | ||
| ReflectMut::TupleStruct(self) | ||
| } | ||
|
|
||
| fn reflect_owned(self: Box<Self>) -> ReflectOwned { | ||
| ReflectOwned::TupleStruct(self) | ||
| } | ||
|
|
||
| fn clone_value(&self) -> Box<dyn PartialReflect> { | ||
| Box::new(Clone::clone(self)) | ||
| } | ||
| } | ||
|
|
||
| // We have to implement `Reflect` manually because of the embedded | ||
| // `Box<dyn AnimationEvent>`, which can't be automatically derived yet. | ||
| impl Reflect for AnimationEventData { | ||
| #[inline] | ||
| fn into_any(self: Box<Self>) -> Box<dyn Any> { | ||
| self | ||
| } | ||
|
|
||
| #[inline] | ||
| fn as_any(&self) -> &dyn Any { | ||
| self | ||
| } | ||
|
|
||
| #[inline] | ||
| fn as_any_mut(&mut self) -> &mut dyn Any { | ||
| self | ||
| } | ||
|
|
||
| #[inline] | ||
| fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> { | ||
| self | ||
| } | ||
|
|
||
| #[inline] | ||
| fn as_reflect(&self) -> &dyn Reflect { | ||
| self | ||
| } | ||
|
|
||
| #[inline] | ||
| fn as_reflect_mut(&mut self) -> &mut dyn Reflect { | ||
| self | ||
| } | ||
|
|
||
| #[inline] | ||
| fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> { | ||
| *self = value.take()?; | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| // We have to implement `FromReflect` manually because of the embedded | ||
| // `Box<dyn AnimationEvent>`, which can't be automatically derived yet. | ||
| impl FromReflect for AnimationEventData { | ||
| fn from_reflect(reflect: &dyn PartialReflect) -> Option<Self> { | ||
| Some(reflect.try_downcast_ref::<AnimationEventData>()?.clone()) | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.