Closed WeaselOnaStick closed 5 months ago
I'm not sure I fully understand the problem but since the latest version 0.13.0 fully automatic transitions are actually possible. So you can make a transition which has an empty event
property and just an expression guard. This transition will be evaluated under the following conditions:
If this is not what you need, could you maybe give a concrete example of what you are trying to implement?
I'll try to give a simple example. I have a character with following state chart:
And an animation tree that has path between tree states that makes the character stand up and sit down before doing looping Sit_Floor_Idle
or Walking
animations.
I have an event "start_walking"
that is, (let's say) sent via button, that triggers both ToLookout
and ToWalkingAnim
transitions. But I also want for ToLookout
to wait until the getting up animation is finished. (animation(s) between Sit_Floor_Idle
and Walking
states inside my tree)
Currently I work around that by sending a separate "anim_finished_getting_up" event, that's sent by animation tree signal. It works as intended, but I'm afraid this could get cluttered quickly.
If my proposed feature were to be implemented I could only need to check single expression property that holds current animation with something like this:
func _process(_delta):
state_chart.set_expression_property("anim_cur",(animation_tree["parameters/playback"] as AnimationNodeStateMachinePlayback).get_current_node())
Have a gate on ToLookout
transition with the "retry" feature enabled and with following expression:
anim_cur == "Walking"
and ToLookout
would be triggered again once the animation is finished.
Another way to fix my issue, would be to have a toggle option (let's call it "check gate after delay") which would be available if transition delay is > 0. But I imagine that would be much less flexible, and much harder to implement.
I understand this might be a niche request, maybe better implemented just for animationtree states, but I can think of a few other examples where this feature might be useful. Once again, let me know if there's an easier way and if my proposal isn't practical enough.
The thing with the "retry" is that it would get in the way of some invariants that currently hold true (e.g. that only one transition can be pending at any given time and that a transition will never be taken if its guards are not evaluating to true), so this would open up the possibility for all kinds of edge cases. Also when you think about this, if your character is currently getting up or sitting down, he is neither in lookout nor resting state. So let's make it explicit:
Now you have a nice sequence of states. When the player gets up, it gets into "Getting Up state" which will trigger playing the animation and will go into Lookout state automatically after 1 second (or however long the animation takes). And for sitting down it pretty much works the same way. Also now you have the possibility to abort getting up/sitting down (i have added two more transitions "through the middle"). As an additional bonus you need no sync back from the animation player to your state chart.
Yeah I was mainly asking because I had experience in programming where "gates" meant slightly different mechanism, and I was wondering if it would make sense to have that behavior here.
I ended up changing Lookout(Atomic)
state into a compound chain of states (getting_up→rotate_to_outpost→go_to_outpost→lookout→rotate_to_rest_place→go_to_rest_place) and it didn't make sense to hardcode animation length values (since most animations were procedural), so I just used the gate I mentioned.
It's just that I didn't want to add these intermediate states, because it's not obvious if they should be inside Lookout or Resting.
Hmm okay, I think i have an idea now how this could work. Basically we have two parts in a transition:
Now the delay currently can only be a timer, but if there was an option to use an expression as a delay, this could nicely solve the issue without introducing a lot of edge cases (except for the migration headache as this would be a breaking change... ). So this would look like:
And in code:
# called when the unit should travel somewhere
func travel_to(target):
_state_chart.set_expression_property("target", target)
_state_chart.send_event("begin_travel")
func on_orienting_physics_processing(delta):
# code here to orient the unit
...
if [unit is oriented]:
_state_chart.send_event("oriented")
func on_moving_physics_processing(delta):
# code here to move the unit where it needs to be
...
if [unit has arrived at target]:
_state_chart.send_event("arrived")
What do you think?
Yeah, I think this makes much more sense than my initial approach. I think when I started I just looked at the platformer example where animation states and logic states are parallel/independent of each other and assumed that was the only right way to do it. Where as in my example animations do in fact affect the logic.
With the new automatic transitions, you can actually do this without any custom delays. If you write a bit of code like this:
@onready var _animation_player:AnimationPlayer = %AnimationPlayer
@onready var _state_chart:StateChart = %StateChart
func _ready():
animation_player.animation_changed.connect(_on_animation_changed)
func _on_animation_changed(_old_animation:StringName, new_animation:StringName):
# set the name of the currently running animation as expression property.
_state_chart.set_expression_property("animation", new_animation)
And then you can use automatic transitions from Arriving at home , Arriving at lookout and Preparing for travel based on the current animation.
I want to propose a feature for state transitions described in the title.
Basically, we have a transition with both an event and a closed gate. I want there to be a toggle that makes the transition happen when a gate opens even after the event has been sent. That is without sending the event again, only sending it once before the gate opens.
I think the most optimal way would be is for a gate to somehow hook to relevant expression properties -> re-evaluate and send an internal transition event if gate is satisfied. Feel free to let me know if there's an easy way around with current tools (I tried transition delay but it only evaluates the gate at the start, not after delay) and if my proposed feature has any practical value