Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
f5bfb0e
example init
atornity Sep 30, 2024
fa01cd1
Update Cargo.toml
atornity Sep 30, 2024
f970677
implement animation triggers
atornity Sep 30, 2024
3d067cc
make target_id optional + rename example
atornity Sep 30, 2024
a34e47a
add animation events to animated_fox
atornity Sep 30, 2024
6df2bf7
new add_trigger method variant
atornity Sep 30, 2024
44a228f
rename trigger -> event
atornity Sep 30, 2024
578f579
add some docs
atornity Sep 30, 2024
e9c5af1
update examples readme
atornity Sep 30, 2024
463a3b0
edit comments
atornity Sep 30, 2024
841e517
use Box<dyn AnimationEvent>
atornity Sep 30, 2024
fce8da2
module doc
atornity Sep 30, 2024
c292343
use core instead of std
atornity Sep 30, 2024
6dee939
use core instead of std (again)
atornity Sep 30, 2024
8a6aabf
add `AnimationTargetId::from_str`
atornity Sep 30, 2024
c544f7a
remove dbg
atornity Sep 30, 2024
c0e6d11
update examples
atornity Sep 30, 2024
a07df83
Merge remote-tracking branch 'upstream/main' into animation-triggers-…
atornity Sep 30, 2024
4e14010
Merge branch 'main' into animation-triggers-experiments
atornity Sep 30, 2024
d2f1848
cleanup use
atornity Sep 30, 2024
1ff5b54
fix typo
atornity Sep 30, 2024
305deb8
rename events -> animation_events
atornity Sep 30, 2024
ba14f36
from_str -> from_iter
atornity Sep 30, 2024
d2ed656
rename animation_events -> animation_event
atornity Sep 30, 2024
b5d9a87
reflect comments
atornity Sep 30, 2024
da91ee3
Merge branch 'main' into animation-triggers-experiments
atornity Sep 30, 2024
1e2ea51
reverse animation
atornity Sep 30, 2024
3419bca
Merge branch 'animation-triggers-experiments' of https://github.com/a…
atornity Sep 30, 2024
a02cd8c
example comments
atornity Sep 30, 2024
ebb29f5
fix typo
atornity Sep 30, 2024
06535ff
rename add_event_with_id -> add_event_to_target
atornity Sep 30, 2024
a5a5642
docs/cleanup add_event
atornity Sep 30, 2024
db74d8f
fix logic error
atornity Oct 1, 2024
ab10ea1
Update animation_events.rs
atornity Oct 1, 2024
e4f0ea4
create AnimationTriggerIter
atornity Oct 1, 2024
49f9692
Update animation_events.rs
atornity Oct 1, 2024
7f97667
remove unwrap
atornity Oct 1, 2024
5a42a67
raname trigger -> events
atornity Oct 1, 2024
30f146b
add `time` to `AnimationEvent::trigger`
atornity Oct 1, 2024
09b6842
fix examples
atornity Oct 1, 2024
fd86ce5
include player entity in `AnimationEvent::trigger`
atornity Oct 1, 2024
45b7b79
bunny detective example
atornity Oct 1, 2024
12b0dcc
reset animated fox example
atornity Oct 1, 2024
4118846
print example controls
atornity Oct 1, 2024
5c17423
Merge branch 'main' into animation-triggers-experiments
atornity Oct 1, 2024
c5725fb
Merge branch 'animation-triggers-experiments' of https://github.com/a…
atornity Oct 1, 2024
e1cc404
don't use deprecated bundle
atornity Oct 1, 2024
ab599a6
Update README.md
atornity Oct 1, 2024
d77beab
fix bug
atornity Oct 1, 2024
c74da72
Update bunny_detective.rs
atornity Oct 1, 2024
8636ea6
remove unwrap
atornity Oct 1, 2024
7bffccc
ugh
atornity Oct 1, 2024
86eee63
Merge branch 'main' into animation-triggers-experiments
atornity Oct 1, 2024
ec12ab9
Merge branch 'animation-triggers-experiments' of https://github.com/a…
atornity Oct 1, 2024
2148f76
fix bugs + add tests + renames
atornity Oct 2, 2024
47f525a
clippy + comments
atornity Oct 2, 2024
511b0c8
Merge branch 'main' into animation-triggers-experiments
atornity Oct 2, 2024
a7931d6
Update lib.rs
atornity Oct 2, 2024
7ef25ce
Merge branch 'animation-triggers-experiments' of https://github.com/a…
atornity Oct 2, 2024
f8fd00b
use required components
atornity Oct 2, 2024
6b8665a
Update lib.rs
atornity Oct 2, 2024
dfd422f
fix iterator
atornity Oct 2, 2024
1c41b98
Update animation_events.rs
atornity Oct 2, 2024
5decc0e
remove `player_entity` from `AnimationEvent`
atornity Oct 2, 2024
fd8953a
fix trigger order
atornity Oct 2, 2024
932ad5b
typos
atornity Oct 2, 2024
17fb536
refactor
atornity Oct 3, 2024
665d4b8
Merge remote-tracking branch 'upstream/main' into animation-triggers-…
atornity Oct 3, 2024
65703dd
tests + fix bug
atornity Oct 3, 2024
d0e4b38
clippy
atornity Oct 3, 2024
7afc9d5
remove sort
atornity Oct 3, 2024
6ba9b71
Update animation_events.rs
atornity Oct 3, 2024
b0532d1
derive macro i guess
atornity Oct 3, 2024
4f88b02
add instructions ui to example
atornity Oct 3, 2024
e763cb4
remove unused dependencies
atornity Oct 4, 2024
d9359eb
remove comments
atornity Oct 4, 2024
a244b4a
rename root bone + remove non-deform bones
atornity Oct 4, 2024
710498e
add `CloneableAnimationEvent`
atornity Oct 4, 2024
0165c29
update examples
atornity Oct 4, 2024
7708aa1
typos
atornity Oct 4, 2024
0aef794
add `weight` to `AnimationEvent`
atornity Oct 4, 2024
dbb4a60
move feet up from ground
atornity Oct 4, 2024
b7a559d
Update bunny_detective.rs
atornity Oct 4, 2024
48964ec
Update crates/bevy_animation/src/animation_event.rs
atornity Oct 4, 2024
a13a094
implement suggestions
atornity Oct 4, 2024
6d8703c
remove pub(crate)
atornity Oct 4, 2024
1439455
Merge branch 'animation-triggers-experiments' of https://github.com/a…
atornity Oct 4, 2024
f8c2de0
typo
atornity Oct 4, 2024
299355c
disable jumping input while jumping
atornity Oct 4, 2024
e0203d3
Update crates/bevy_animation/src/lib.rs
atornity Oct 4, 2024
14a3302
add data to event in `animation_events.rs`
atornity Oct 4, 2024
249d49b
rip buntective :(
atornity Oct 5, 2024
7f8e504
Merge branch 'main' into animation-triggers-experiments
atornity Oct 5, 2024
a1b1f19
ci fix
atornity Oct 5, 2024
e0f0071
Update examples/animation/animated_fox.rs
atornity Oct 5, 2024
805c4fb
explain how to calculate event time
atornity Oct 5, 2024
92ee184
Merge branch 'main' into animation-triggers-experiments
atornity Oct 5, 2024
2c4d578
Merge branch 'animation-triggers-experiments' of https://github.com/a…
atornity Oct 5, 2024
1b5b903
observe_on_step
atornity Oct 5, 2024
8e1d6ef
use required components
atornity Oct 5, 2024
f00700a
clarify that `time` is in seconds
atornity Oct 5, 2024
a192f67
runs -> triggered
atornity Oct 5, 2024
ea02940
`AnimationEvent` doc example
atornity Oct 5, 2024
83a219e
Update examples/animation/animated_fox.rs
mockersf Oct 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,17 @@ doc-scrape-examples = true
hidden = true

# Animation
[[example]]
name = "animation_events"
path = "examples/animation/animation_events.rs"
doc-scrape-examples = true

[package.metadata.example.animation_events]
name = "Animation Events"
description = "Demonstrate how to use animation events"
category = "Animation"
wasm = true

[[example]]
name = "animated_fox"
path = "examples/animation/animated_fox.rs"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_animation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ keywords = ["bevy"]

[dependencies]
# bevy
bevy_animation_derive = { path = "derive", version = "0.15.0-dev" }
bevy_app = { path = "../bevy_app", version = "0.15.0-dev" }
bevy_asset = { path = "../bevy_asset", version = "0.15.0-dev" }
bevy_color = { path = "../bevy_color", version = "0.15.0-dev" }
Expand Down
25 changes: 25 additions & 0 deletions crates/bevy_animation/derive/Cargo.toml
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
29 changes: 29 additions & 0 deletions crates/bevy_animation/derive/src/lib.rs
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()
}
281 changes: 281 additions & 0 deletions crates/bevy_animation/src/animation_event.rs
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())
}
}
Loading