-
-
Notifications
You must be signed in to change notification settings - Fork 21.6k
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
Add node_notify signal for animation trees #102165
base: master
Are you sure you want to change the base?
Conversation
See also:
As I have pointed out in the past with those, it should be implemented so that the ancestor StateMachine returns the nested paths when in Grouped mode (https://godotengine.org/article/migrating-animations-from-godot-4-0-to-4-3/#grouped). In other words, it must be implemented in such a way that the signal is propagated to the ancestor State as special case for the Grouped mode. |
Thanks for the links, I hadn't seen those PRs. Do you think it would be worth adding
I'll work on adding this. Just to be sure I understand correctly, the changes I'll make would change the example root sm
Also regarding a comment you left on another PR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the links, I hadn't seen those PRs. Do you think it would be worth adding fading_from_state_changed to this pr?
I think so.
As for the StateMachinePlayback
object, it should be a unique resource for each AnimationTree, so there should be no problem to fire a signal there.
However, if you want to fire a signal in the AnimationNode, signals should not be registered with AnimationNode as explained in the review below.
The simplest implementation would be to fire signal from the AnimationTree with its own BlendTree path. In this case, you can refer to AnimationTree::set_parameter()
to get the path. At this point, it may be better to separate the method for obtaining its own path from it and make it reusable.
But then, my concern is that each time we have more signals that we want to fire in the AnimationNode, we need to register all of them in the AnimationTree.
So, instead of adding a signal called oneshot_finished()
to the AnimationTree, we should add a generic abstract signal such as node_notified(animation_node_path, signal_name, signal_variants)
.
However, that PR should be a separate PR from the StateMachinePlayback
signal addition.
06a3c9d
to
89dbc1c
Compare
I've updated the pr to have Edit: One thing I'd like feedback on with the current code is if the signal emission should be put behind a call_deferred in all cases? I noticed it was like that for animation_started/animation_finished but wasn't sure if it should also be the case for the new signals. Easy change, just unsure what the preferred behavior would be. |
89dbc1c
to
4b9850a
Compare
This pr is currently sitting at +124 -11 which imo is a good size but if you'd prefer I can break this up into two PRs (maybe a third to rename |
4b9850a
to
e09355a
Compare
2c7c814
to
4059858
Compare
4059858
to
df98559
Compare
doc/classes/AnimationNode.xml
Outdated
<constant name="NOTIFY_STARTED" value="0" enum="NotifyReason"> | ||
The node has started playback. | ||
</constant> | ||
<constant name="NOTIFY_FINISHED" value="1" enum="NotifyReason"> | ||
The node has finished playback. | ||
</constant> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using ENUM is the right direction to go. However, for consistency with something similar to this that already exists in Godot, we recommend the signal name NOTIFICATION_XXX instead of NOTIFY_XXX.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, would NOTIFICATION_XYZ
cause confusion with the existing notification stuff? I preemptively changed the enum name as well but that causes the c# build to complain about hiding Notification
so that at least can't be shared. Maybe EVENT_STARTED
etc with enum name Event
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about
enum AnimationNodeNotification {
ANIMATION_NODE_NOTIFICATION_XXX,
ANIMATION_NODE_NOTIFICATION_YYY,
}
?
@@ -411,6 +411,7 @@ class AnimationNodeBlendTree : public AnimationRootNode { | |||
}; | |||
|
|||
RBMap<StringName, Node, StringName::AlphCompare> nodes; | |||
RBMap<AnimationNode *, StringName> node_to_name; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this approach is redundant. At the very least, storing the same String in two places is not the right direction, and we must do indirect mapping, such as storing only the index, and the entity as a String must be only one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't StringName only storing the unified data pointer within either map, so there is just one backing string? this is just to avoid the linear cost of only having nodes
to determine a name in the current context. ideally there would just be a bimap template available, but alternatively the name could also be copied to NodeState
at the same time that the base path and parent are set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am saying that the concatnated path to the AnimationNode already exists in the property_reference_map, so you don't need to create a new map. I assume you can just retrieve it from property_reference_map.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think it would be better to parse out the final part of the path or emit the full concatenated path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The signal must pass the full path to the AnimationNode. As I have said many times, since the AnimationNode is a Resource, the same reference AnimationNodes can be placed in the same hierarchy. To distinguish between them, the user must know the full path.
For example, if you have two BlendTrees with the same reference that have “OneShotA” as a child, you cannot determine which BlendTree's "OneShotA" if the only notification is "OneShotA".
BlendTree.tres
[Anim1]
|
[OneShotA]-[Output]
|
[Anim2]
---
RootAnimationNode
[BlendTree1(BlendTree.tres)]
|
[Blend2]-[Output]
|
[BlendTree2(BlendTree.tres)]
In the case above, we should distinguish between BlendTree1/OneShotA
and BlendTree2/OneShotA
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes that makes sense. I was only considering blend trees as roots but of course the names alone would break if they were nested.
Edit: Although outside of this use case, it still might be worthwhile to split the current map into StringName -> Ref<AnimationNode>
and AnimationNode * -> AnimationNodeBlendTree::Node
so that blend_input
can lookup connections in constant time. I won't do that in this pr though.
Edit 2: Also maybe worth mentioning that although it's not a necessary part of the example, get_node_name
unfortunately will not be reliable in either case with the tree you've described as both names map to a valid value and it'll return whichever it finds first.
df98559
to
e13e26a
Compare
Closes godotengine/godot-proposals#10253.
Related to #102398
This adds
node_notify(StringName, NotifyReason)
toAnimationTree
as a generic signal used for node state changes during processing/playback. CurrentlyNotifyReason
can be eitherNOTIFY_STARTED
orNOTIFY_FINISHED
and is implemented for one shot and state machine nodes. For example in the following blend tree:The new signal fires with the following values: