Trigger action at end of another action in Python

Might seem obvious but I can’t figure it out. I have an Armature that plays an action when a key is pressed. I want another action to play when that action ends. How can I trigger an action to play when another action ends on the same Armature in Python. Thanks everyone.

if you need another action when the current action is completed or the frames of the current action have reproduced the animations you need, you can always switch to new ones - you need to get the armature frames and write conditions or logical bricks and compare the frame property -
arm[‘frame’] = arm.getActionFrame()
if arm[‘frame’] in range (50, 60):
arm.playAction(“New_Action”, 70, 100, blendin=5, layer =0)

Related: Getting the Length (or last frame) of an Action - #2 by Monster

I would advice getting the name of the currently playing action, than cross-check to see if the current frame has reached a stated frame value yet:


Imagine the demo has 2 animations, one called Anim_1, and another called Anim_2. When one action reaches it's stated end, Python will run the other action, and vice-versa in a infinite loop. (If so desired, you can tweak the code yourself)
import bge

END_FRAME_1 = 30
END_FRAME_2 = 60

def main(self):
    
    # Anim_1
    if self.owner.getActionName() == "Anim_1":
        if self.owner.getActionFrame() == 30:
            self.owner.playAction("Anim_2", 0, 60)      
            
    # Anim_2
    elif self.owner.getActionName() == "Anim_2":
        if self.owner.getActionFrame() == 60:
            self.owner.playAction("Anim_1", 0, 30)

Thanks for the answers so far. The script is like this. How could I make Animation4 play after Animation1 is completed?

import bge
from collections import OrderedDict
from mathutils import Vector

c = bge.logic.getCurrentController()
owner = c.owner

key = bge.logic.keyboard.events
ms = bge.logic.mouse.events

W = key[bge.events.WKEY]

LMB = ms[bge.events.LEFTMOUSE]

arm = owner.children.get(‘GameRig’)

frame = arm.getActionFrame()
action = arm.getActionName()

if W:
blendin = 2
arm.playAction(‘Animation1’, 1, 60, blendin = blendin, speed = 1.5)

elif LMB:
blendin = 2
arm.playAction(‘Animation2’, 1, 60, blendin = blendin)

else:
blendin = 2
arm.playAction(‘Animation3’, 1, 60, blendin = blendin, priority = 1)

Thanks for the help everyone.

you need get frames from animation number1 and get name animation1 and create a block of conditions that will check its completion
if arm.getActionName()==‘Animation1’ and arm[‘frames’] in range (59, 60):
arm.playAction(“Animation4”, 0, 60, blendin=5, layer=0)
I do not recommend setting specific numbers of animation frames and comparing them since these frames are skipped very often and it is better to compare the frame range since this always works. And remember one more thing - if you use animation layers, you need to specify the layer number at the end of the line in parentheses when receiving frames, otherwise it will be equal to 0 arm[‘frames’] = arm.getActionFrame(1) #1 for first layer frames if play more layers animation

Hey thanks. I know this is probably basic but I am a complete noob at scripting. I get the error
KeyError: ‘value = gameOb[key]: KX_GameObject, key “frames” does not exist’

you need create property - ‘frames’ for armature - for assign numbers in property

Ok. Thank you. I created a property called frames and if I put the property number in the range it skips the first animation and goes straight to the second. Any ideas?

You need setup range frame action - if you animation use 60 frame need stand range in 59-60, animation work to 59 and triggered new animation, if you select long range, trigger start not right. Remember - if all animations end need assign to frames property 0 because script save last play frame and this make errors

Thanks. Still not working for some reason. Not sure what I am missing. I tried a property on the armature and the controller each. I tried integer and float both set to 0. Animation is 60 frames, range is 59-60. I get the first animation but not the second. If I set the the property to 59 I get the second animation and not the first. Not sure what I am doing wrong. Thanks for the help.

I should probably also have mentioned I am using Blender 2.79 and not UPBGE if that makes a difference. Sorry for any confusion. Thanks

Guess I used up all my tech support lol. If I find a solution that works for me I will post up. Thanks to everyone who attempted to help.

You could play each animation on a different layer and use isPlayingAction(layer) to check if the action is being played.
You would need a property that you toggle when Animation1 is played.

Animation1_Layer = 1

if W:
    blendin = 2
    arm.playAction(‘Animation1’, 1, 60, layer = Animation1_Layer, blendin = blendin, speed = 1.5)
    arm['Animation1started'] = True

elif arm['Animation1started'] and not arm.isPlayingAction(Animation1_Layer):
    # Animation1started is true but no Action is being played on the Animation1 layer means that the action ended
    arm['Animation1started'] = False
    # play animation 4

getActionFramesExample.blend (101.2 KB)
maybe my small and primitive example will help you - it just takes animation frames and compares them to activate another animation

Thank you very much for the example. I will have a look and try to understand it.

Use it and good luck to you

Two quick questions. Does it make a difference to use modules over scripts? I am using scripts. Second, in your example there is a property on the object for anim_a, but where does anim_b come from? Its in the code but I don’t quite understand how it switches to that or where anim_b is. Thanks

the use of modules or scripts does not affect the code - just remove the extra indents, now about anim_a and anim_b these are animations and strings when in the first condition the number of frames falls into the specified range, the script will assign the anim_b property as a string and switch the animation, I very often use strings and string properties instead of states because they allow you to do concatenation-combine strings and this is very convenient-you can substitute a string for the animation name, or add an object by a string property or replace the grid

ActionTriggerScriptExample.zip (1.5 MB)

I didn’t even think to just upload the .blend until you did. That makes things a lot easier than people just shooting in the dark. This is a simplified version of the script I started with. Lets say in this example I wanted the Attack action to play right after the roll Action instead of the Attack action using keys W+Shift. Thanks for the help everyone.

This great root motion script was created by Company BR for anyone interested. You can find info about it here.

https://www.youtube.com/c/CompanyBR

edited to change the .blend to a .zip for smaller download