I keep running into animation control problems, sometimes an animation does not fire.
I use a pattern at the moment,
if state == States.STATE_NAME:
if statename_timer == 0.0:
animtree["parameters/playback"].travel("state_name")
statename_timer = anim_timer(statename_timer,delta)
if (statename_timer > statename_max_time) or (state_change_condition):
state = States.OTHER_STATE
statename_timer = 0.0
but i think this is more difficult to read and more complicated to code. Is there an easy way to just run animations and expect them to finish? What functions can i use like await() or anim_tree.animation_finished, to make the process more solid?
The reason is that i have a lot of characters and animations, they are hard to manage. There are things that require stopping the animation and blending to another animation from a pose.
I could also benefit from running the animation in an offline capacity to measure positions.
Im sure this question is hard to answer completely, but help with how to manage animation code is always welcome.
You haven’t really given us that much information about your architecture, so I’m going to infer what I can. But if my conclusions about what you are doing are wrong, well I’m working with what I’ve got.
Your state machine is too simple, and based on a push model, which both have issues. The first, is that your state machine code is going to get REALLY BIG. As its complexity grows, it’s going to become harder and harder to add new states and the problem you are facing now is going to multiply.
You will make your life a lot easier by making each state a separate script, and decoupling it from other states as much as possible. Doing so will solidify your thinking about each state and over time you will figure out the patterns necessary to keep your code from becoming unmanageable.
You can take a look at my State Machine Plugin, which is only two files and is very lightweight. It is a pull state machine however, and takes some getting used to; but when used correctly, states can be added and removed - even during runtime - without affecting the integrity of the machine.
Additionally, for what you are doing, your AnimationTree’s base should be an AnimationBlendSpace2D, not an AnimationStateMachine. You should have multiple AnimationStateMachines inside and blend them as appropriate. Doing so will simplify your state code, because the attack state doesn’t need to know about the move state. When the player swings a sword, the attack state triggers a state animation and a blend.
Tracking the AnimationTreeanimation_finished signal is the way to go for timing. You can also get the state name from that signal, so you can check to make sure its the animation you’re expecting to finish. (It doesn’t get triggered if an animation is stopped before finishing.)
The state machine should grow yes, at the moment ive managed to keep the states at a refined level but the animation switching is different … its a mess because some are done one way, and others are done that way. For example one NPC uses the above style, but the other switches animation states in an animate function and sets variables like ‘attacking’ and ‘reacting’ in the timed states.
Another problem is that there are interupt conditions like when the NPC gets damaged or an area zone triggers it to target the player -theres no safety check to see whether the animation playing needs to finish (could step both feet to the floor) and the code doesnt have the architecture to finish running the current animation and then act on the state change caused by the interupt.
Thanks I will definitely try to make an NPC type with the plugin. I spent over 2 hours setting up a character yesterday and there were many weird bugs slowing me down towards the end, so if that makes things clearer ill try it out.
That is a great idea, i think you have exactly solved one of the problems. Ive been using state machines even in the player code and then the state machine switches between BlendTree’s, so each one has idle and that mixes with the states animation, so theres a state for attack with a blendtree, another for walk, etc.
The states cant blend with other states … I was even confused and thinking about seeking the to the animation frame of the previous states idle animation.
Yeah i could blend them all with the Blendtree. I was planning to overhaul / refactor the animation system at some stage anyway. The animation states were just good for stability as they can be switched easily.
Im going to try this tomorrow and see what happens.
Animation is a visual effect. It doesn’t need to correspond to the logical state in a rigid one to one fashion. That’s the whole premise behind the usage of animation blending. Are you blending the logical state when you blend animations?
Are you saying your state machines as capable of being e.g. 59.3% in the running state? And the rest distributed in a 3:2 ratio between idle and swimming states?
LOL More like 50% or maybe 33%. Lets take my Character 3D Plugin I’m currently revamping, which does both 1st person (FPS) and 3rd person (apparently TPS) controls.
As you can see, it currently has three StateMachines: Movement, Attack and Action. If the player is Jumping, Attacking, and Blocking, all three states are active - each in its own machine. All states are fully active, but each one makes up only ~33.33% of what the player is actually doing, and the animations are blended together.
The Jump controls the legs and feet, while the torso on up are taken over by the Attack animation, and then the left arm alone is controlled by the Block.
Thanks those controllers are interesting, there were a couple of problems with the y-bot animations … i am using Godot 4.62 stable … the basic locomotion FBX got a red X in the file view, and a couple of the other … ok heres a list of apparently broken/ incompatible animations:
There is an example of how I use the state machine in my Character 3D Plugin. Though like I said, I’ve been revamping it. I can push the current unfinished version if you’d like so you can pull it.
When I originally created it, Godot didn’t support root motion yet. So I haven’t had a chance to play around with root motion at all in Godot or figure out how it’s different.
The way I would do it though is create say a RootMotionMovePlayerState and then have it handle things.