Skip to content

Animation root observer for Tween events - #98

Open
Just-Helpful wants to merge 34 commits into
Multirious:mainfrom
Just-Helpful:main
Open

Just-Helpful wants to merge 34 commits into
Multirious:mainfrom
Just-Helpful:main

Conversation

@Just-Helpful

@Just-Helpful Just-Helpful commented Oct 23, 2026 •

Copy link
Copy Markdown

Objective

Whilst TweenEvents are EntityEvents, they do not propagate to the animation root.
This makes it difficult to register entity observers on animated entities.

Solution

Adds automatic propagation to an entities animation root
as defined by the bevy TimeRunner used to drive animation.

Warning

This change does result in firing a global observer more than once (often twice).
Whilst this is a breaking change, it is possible to filter these events by event.entity.

It's up to you whether you're okay with this...

Testing

I added an entity observer on the existing entity_event.rs example
and confirmed that the corresponding entity observer was fired.

I also performed a visual comparison of the existing event.rs and confirmed minimal visual changes.


Showcase

Enabled behaviour:

commands
    .animation(...)
    .observe(|event: On<TweenEvent<&'static str>>| {
        println!("TweenEvent fired for this animation {:?}", event.data)
    })

No change seen in the event.rs example:

https://github.com/user-attachments/assets/35880219-5ff8-4858-be57-f7862856f2cf
https://github.com/user-attachments/assets/0ab2982b-3805-47d6-9e96-0d9369ac0440

Updates made to entity_event.rs:

commands
    .animation()
    .insert(sequence((
        forward(Duration::from_secs_f32(3.)),
        event("tween"),
        forward(Duration::from_secs_f32(0.5)),
        event("event"),
    )))
    // + entity observer added
    .observe(|trigger: On<TweenEvent<&'static str>>| {
        println!("TweenEvent on root: {}", trigger.data)
    });
Screencast.From.2026-10-23.21-16-38.mp4

To-Do Checklist

  • Write CHANGELOG.md

This lets us listen to said events on the entity that we
inserted the animation on

i.e. we can use:
```
commands
  .animation(...)
  .observe(...)
```
@Just-Helpful Just-Helpful changed the title Support entity observers for Tween events Entity observer propagation for Tween events Oct 23, 2026
@Multirious

Multirious commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Thanks for making this PR. Although I think we might be having this struct doing too much by default. Originally it's just doing a single thing, which was Message (or event in old bevy, hence TweenEvent). Then we added observer later on. So, I think I want it to be some what configurable, mainly to reduce cloning and adding choices.

I believe user should be able to choose between the following:

  • Global event
  • Entity event
  • Propagated entity event
  • Message

We could have this as an option inside current TweenEvent, or split all of them into different struct and their data component counterparts, or something in between. I'm not entirely sure what's the best practical choice here but the crate has always been "composition-first", have a single component do a single work then we compose them together, so maybe we should do the separated struct design.

If we're going with the separated struct design, I propose the names:

  • TweenEvent
  • TweenEntityEvent
  • TweenRootEvent
  • TweenMessage
  • and their Tween*Data counterparts.

@Just-Helpful

Copy link
Copy Markdown
Author

I'd be up for that. I assume the TweenEvent in this case is intended to only have the global listener?

@Just-Helpful

Copy link
Copy Markdown
Author

My only concern would be with the combinators that trigger those Events / Messages.
The TweenMessage is easy enough to handle, we just a separate message combinator or rename the existing combinators to make it clear they only send messages and not events. The others are a little harder to distinguish...

@Just-Helpful

Copy link
Copy Markdown
Author

I think it would also still be useful to include a generic send (name not concrete) combinator that triggers all, as I suspect some people don't care about how much stuff gets triggered, they'd just want to handle it in their own flexible way.

@Just-Helpful

Copy link
Copy Markdown
Author

I'll make the changes and just let me know if there's anything blindingly wrong I guess.

@Multirious

Multirious commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Names could be message, global_event, self_event, and root_event? I think it'd be fine as long as we have extensive documentation. Maybe also change from TweenEntityEvent to TweenSelfEvent and TweenEvent to TweenGlobalEvent for consistency.

@Just-Helpful

Just-Helpful commented Oct 24, 2026 •

Copy link
Copy Markdown
Author

Okay, after implementing this a bit:

It's difficult to avoid bevy pinging global event listeners when we fire an entity event (not unless we want to pick apart bevy's internals and hand roll the unsafe parts of that...). This kinda means that there's very little distinction between global_event, self_event and root_event. Therefore I've decided to cross off global_event as a combinator.

Whilst I've left an implementation for self_event around, I'm not fully sure it's actually that useful?
It ends up being quite difficult to attach observers to the entity that has TweenEventData short of iterating through animation root's children.
If you're up for it, I'll add another commit to fully eliminate self_event and rename root_event -> event.

@Just-Helpful

Just-Helpful commented Oct 24, 2026 •

Copy link
Copy Markdown
Author

In this implementation we could also maybe (?) make the generic dispatch methods pub...
It'd take a fair bit of documentation to actually explain the intended use case.

@Just-Helpful Just-Helpful changed the title Entity observer propagation for Tween events Animation root observer for Tween events Oct 24, 2026
@Multirious

Multirious commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Whilst I've left an implementation for self_event around, I'm not fully sure it's actually that useful?
It ends up being quite difficult to attach observers to the entity that has TweenEventData short of iterating through animation root's children.
If you're up for it, I'll add another commit to fully eliminate self_event and rename root_event -> event.

Yeah, let's just do that then. Since the global observer could be ran multiple times, stated in the original problem, we should add like root: bool field to the event. Checking the entity field directly seems rather more involved by the user otherwise.

I don't think we should have dispatch method pub. It's has a rather specific uses and simple to reimplement.

The way these combinators send events is a difficult
to observe with entity events, so I'm discarding them in favor of `root`
event combinators
Also cleans up some left over references to `self_event`s
This makes it a little easier to understand how dispatch works
and also allows for potentially naughty stuff like attaching an
dispatch marker and an observer component at the same time.
@Just-Helpful

Copy link
Copy Markdown
Author

Okay, that change has been made.
Looking at the examples again, it looks like we've actually resolved the double firing problem (I suspect it was due to using event propagation in the original draft of this PR). Hence I'm not fully sure about the root: bool field.

Comment thread src/tween_event.rs
Comment thread src/tween_event.rs
Comment thread src/tween_event.rs Outdated
Just-Helpful and others added 4 commits October 25, 2026 10:08
Co-authored-by: Multirious <77918086+Multirious@users.noreply.github.com>
Fundamentally all that matters about this query data *is* that it can
produce a `TweenEvent` for a given entity, not *how* it does that.
@Just-Helpful

Just-Helpful commented Oct 25, 2026 •

Copy link
Copy Markdown
Author

Okay, those have both been updated. The only real bit I'm unsure about is logging a warn! message in a library.
I doubt we'll ever hit that case except in extreme misuse but given that we're emphasising composability here,
we probably need to prep for that. I'll see if there's a way I can integrate a toggle for this warning message into the debug plugins.

Comment thread src/tween_event.rs
@Multirious

Multirious commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Okay, those have both been updated. The only real bit I'm unsure about is logging a warn! message in a library. I doubt we'll ever hit that case except in extreme misuse but given that we're emphasising composability here, we probably need to prep for that. I'll see if there's a way I can integrate a toggle for this warning message into the debug plugins.

I think it's fine. I can't imagine where other place TweenEvent can be composed in, given the constraint we declared on the component. And user can always just not register this system.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants