Play one animation right after other animation ends

Hi, i’m new here, and trying to figure out how the animation works.
I’m trying to find a way to play animation ‘idle’ right after animation ‘punch’ ends.
For example, I handle Q key is up, punch animation starts, when the animation is over, animation ‘idle’ starts playing.

The only solution i see right now is something like this:

var other = this; setTimeout(function() { other.setState('idle') }, this.entity.animation.getAnimation('jump_attack.json').duration * 1000);

This method works fine, but i think it’s a bit tricky, perhaps there’s a more clean and simple solution?

Hi,

Currently the animation component does not fire events when an animation ends, which would really help for this kind of thing. We are aware of this and have plans to improve the animation system. Personally I have used the timeout method you showed before although it is a bit problematic because the timeout will fire no matter if the application is running or not. Ideally you want to be advancing a timer using the update loop’s delta time (dt) and when that timer exceeds the duration of the animation then play the other one.

Actually having a little Timer class that updates using the delta time and does something when the duration has elapsed is pretty handy :wink:

1 Like

I see. Thanks for your reply.

any progress since 2016 in the “animation events” triggering?
is could be so helpful…:disappointed:

so would you just start and animation and immediately call the next one with this time interval?
did it work for you smoothly?

You probably need a morph animation?

In a previous project, I polled the currentTime and duration properties of the Animation component to know if an animation has finished and start the next animation in the ‘queue’.

hey,
at the moment I am check if I found the “max current time of animation” and than I know that I am done, and can continue…
I am not sure if that is equivalent to checking if currentTime == 0 ?

If the animation is not set to loop, I don’t think currentTime resets to 0.

Looking at the code suggests that it is equal to duration when it finishes: https://github.com/playcanvas/engine/blob/master/src/anim/skeleton.js#L88

from what I checked it will never be equals to duration because the chances of you getting this exact moment when you check it in the update function are… none
(for example printing the currentTime in each update results in jumps of between 0.02 - 0.1~ )
you are right if its not looping we wouldnt get 0…

so my best solution is still looking for the max currentTime

Made a quick project that makes a cube appear when the animation has finished.
https://playcanvas.com/editor/scene/546901

When the animation is finished, currentTime should be exactly duration because of the line I linked above in the engine code caps the value if it’s over. https://github.com/playcanvas/engine/blob/master/src/anim/skeleton.js#L88

ok sorry I didnt make the situation clear: :slight_smile:
this check does work if I am not looping the animation,
if I am looping it, then it doesn’t (check it in your project, I made a copy and checked it : https://playcanvas.com/project/501091/overview/test_daphna)

thanks :slight_smile:

Oh, I see. Yeah, that is correct. When I had done this before, I kept track of the previous frames currentTime and just checking if this frame’s currentTime is less than that. eg

if (this.lastFrameCurrentTime > this.animation.currentTime) {
    // Animation has looped
}

this.lastFrameCurrentTime = this.animation.currentTime;

I’ve updated the project to handle both cases (looped and non-looped): https://playcanvas.com/editor/scene/546901

As a side question, if you are intending to play a new animation after the current one ends, why is the animation on loop?

I just bumped into this discussion :innocent: , so the original question is not what I am aiming for…
I am trying to do something every end of animation, while looping it
I think what I said about finding the max currentTime is more-less what you suggest… which is what I am doing :slight_smile:
I just thought maybe there is a cleaner solution…

Ah, I see… Not yet in terms of a cleaner solution. I do believe the animation system revamp is on the roadmap.

If you would like an event to be triggered, you can write a script that does the logic we have above and fires an event on the entity. That way, you can put the script on any animated entity and just write a callback for the event on a separate script.

e.g https://playcanvas.com/editor/scene/546901

var FireEventOnAnimationEnd = pc.createScript('fireEventOnAnimationEnd');

// initialize code called once per entity
FireEventOnAnimationEnd.prototype.initialize = function() {
    this.animation = this.entity.animation;    
    this.lastFrameCurrentTime = 0;
    
    this.canTriggerEvent = true;
};


FireEventOnAnimationEnd.prototype.update = function (dt) {   
    if (this.animation.loop) {
        this.canTriggerEvent = true; 
    }
    
    if (this.animation.currentTime == this.animation.duration || this.lastFrameCurrentTime > this.animation.currentTime) {
        if (this.canTriggerEvent) {
            this.entity.fire('animation:end');
        }
        
        this.canTriggerEvent = false;
    }
    
    this.lastFrameCurrentTime =  this.animation.currentTime;
};
var ShowCubeOnAnimationEnd = pc.createScript('showCubeOnAnimationEnd');

ShowCubeOnAnimationEnd.attributes.add('cubeEntity', {type: 'entity', title: 'Cube Entity'});

// initialize code called once per entity
ShowCubeOnAnimationEnd.prototype.initialize = function() {
    this.entity.on('animation:end', this.onAnimationEnd, this);
};


ShowCubeOnAnimationEnd.prototype.onAnimationEnd = function() {
    if (!this.cubeEntity.enabled) {
        console.log('animation ended');
        this.cubeEntity.enabled = true;
    }
};
2 Likes

again, thanks Steven!
I just changed: ShowCubeOnAnimationEnd.prototype.onAnimationEnd
to:

ShowCubeOnAnimationEnd.prototype.onAnimationEnd = function() {
        this.cubeEntity.enabled = !this.cubeEntity.enabled;
    
};

to make it more clear that it actually happens every loop end :slight_smile: