After much fiddling I've ended up discarding completely the AnimationNodeStateMachine.
Reason for that is that it has an internal State Machine that cannot be manipulated from code.
So, basically, I've created two independent state machines, with two AnimationPlayers: one for the whole body, the second one for the arms.
The change and synchronization of the animations happens in the root script (e.g. Player.gd, but this can be contained in a separate class, if you want).
These are the states for the scripted state machine:
- Body: Idle, Walk, Run, Jump
- Arms: None, Shoot, Reload
These are the states for the AnimationPlayers:
- Body: Idle, Walk, Run, Jump
- Arms: Idle, Walk, Run, Jump, Shoot, Reload
As you can see the two players share the same animations plus the two extra "shoot" and "reload".
Scenario 1 - top and bottom of the body needs to be in sync
The scripted state machines will be in the following state(s)
- Body: Idle | Walk | Run | Jump
- Arms: None
This means that the two AnimationPlayers should execute exactly the same animations:
- Body: Idle | Walk | Run | Jump
- Arms: Idle | Walk | Run | Jump
Scenario 2 - top and bottom of the body will diverge due to an action
The scripted state machines will be in the following state(s)
- Body: Idle | Walk | Run | Jump
- Arms: Shoot | Reload
Same goes for the AnimationPlayers:
- Body: Idle | Walk | Run | Jump
- Arms: Shoot | Reload
Once we're done with the "Shoot" or "Reload" animation, we would like to go back to Scenario 1, however, we need to synchronise the frames on the two players.
** Code **
Here are the utility functions I've written to handle all the above:
# These are two AnimationPlayers
onready var animation_body = $animation_body
onready var animation_arms = $animation_arms
# Sets the whole body animation (called from the state machine)
func set_body_animation(animation):
# Get the old animations
var arms_animation = animation_arms.current_animation
var body_animation = animation_body.current_animation
# Update body animation with the new one
animation_body.current_animation = animation
# If the arms animation is not set OR needs to be the same as the the body
# we synchronize them.
if arms_animation == null or arms_animation == body_animation:
_sync_animations()
# Sets the arms animation (called from the state machine)
func set_arms_animation(animation):
if animation == null:
# If the arms animation is null we synchronize them.
_sync_animations()
else:
# Otherwise body and arms animations are independent
animation_arms.current_animation = animation
# Utility function to synchronize animations
func _sync_animations():
# Get the current body animation
var animation = animation_body.current_animation
# Get the current position in playback of the body animation
var position = animation_body.current_animation_position
# Update the arms animation to match the one of the body
animation_arms.current_animation = animation
# Seek to the same position of the body animation
animation_arms.seek(position, false)