Repository: playcanvas/engine (js)

Issue:
AnimEvents not dispatched after activeStateCurrentTime updated
Project : https://playcanvas.com/project/970223/overview/debug-seeking-animation

I want to be able to seek inside an animation. For now i just use activeStateCurrentTime.

For the use-case :
- i have added manually 3 AnimEvents of type "trick_step" with code in anim.js file
- an AnimEvent of type "animation_frame" is added for each 0.1s of the duration of the animation
- a final AnimEvent for the end of the animation (type "animation_end")

Steps to reproduce the working use-case :

- open JS console
- click on "load ollie"
- wait a little, you should see logs of AnimEvents frames, and the animation will stop on event "trick_step" with status "step_0"
- click on play, animation continue to event "trick_step" with status "step_0b"
- click play , and animation continue to event "trick_step" with status "step_1"
- click on play and after the animation ends.

Steps to reproduce the issue :

- click on "load ollie"
- wait a little, the animation will stop on event "trick_step" with status "step_0"
- click on "Next 2.7", the animation move to event "trick_step" with status "step_0b "(i just updated activeStateCurrentTime)
- click on play and after the animation ends but no logs for AnimEvents. But it is strange, they restarted a few seconds later...



Pull request description:
AnimClip event cursor fix
Currently the AnimClip's eventCursor property is only aligned to the clip's current time when it is initialised. It should also be aligned whenever the clip's time property is set.

Fixes #4546

I confirm I have read the [contributing guidelines](https://github.com/playcanvas/engine/blob/master/.github/CONTRIBUTING.md) and signed the [Contributor License Agreement](https://docs.google.com/a/playcanvas.com/forms/d/1Ih69zQfJG-QDLIEpHr6CsaAs6fPORNOVnMv5nuo0cjk/viewform).


Patch:
diff --git a/src/framework/anim/evaluator/anim-clip.js b/src/framework/anim/evaluator/anim-clip.js
index 234dca846..379a66957 100644
--- a/src/framework/anim/evaluator/anim-clip.js
+++ b/src/framework/anim/evaluator/anim-clip.js
@@ -33,11 +33,7 @@ class AnimClip {
         this._blendWeight = 1.0;        // blend weight 0..1
         this._blendOrder = 0.0;         // blend order relative to other clips
         this._eventHandler = eventHandler;
-        this._eventCursor = 0;
-        // move the event cursor to an event that should fire after the starting time
-        while (this._track.events[this._eventCursor] && this._track.events[this._eventCursor].time < this.time) {
-            this._eventCursor++;
-        }
+        this.alignCursorToCurrentTime();
     }
 
     set name(name) {
@@ -58,6 +54,7 @@ class AnimClip {
 
     set time(time) {
         this._time = time;
+        this.alignCursorToCurrentTime();
     }
 
     get time() {
@@ -104,6 +101,14 @@ class AnimClip {
         return this._eventCursor;
     }
 
+    alignCursorToCurrentTime() {
+        this._eventCursor = 0;
+        // move the event cursor to the event that should fire after the current time
+        while (this._track.events[this._eventCursor] && this._track.events[this._eventCursor].time < this.time) {
+            this._eventCursor++;
+        }
+    }
+
     activeEventsForFrame(frameStartTime, frameEndTime) {
         if (frameStartTime === 0) {
             this.eventCursor = 0;


Test patch:
diff --git a/test/framework/anim/evaluator/anim-clip.test.mjs b/test/framework/anim/evaluator/anim-clip.test.mjs
index cbe066c22..075c239a5 100644
--- a/test/framework/anim/evaluator/anim-clip.test.mjs
+++ b/test/framework/anim/evaluator/anim-clip.test.mjs
@@ -2,6 +2,7 @@ import { AnimTrack } from '../../../../src/framework/anim/evaluator/anim-track.j
 import { AnimData } from '../../../../src/framework/anim/evaluator/anim-data.js';
 import { AnimCurve } from '../../../../src/framework/anim/evaluator/anim-curve.js';
 import { AnimClip } from '../../../../src/framework/anim/evaluator/anim-clip.js';
+import { AnimEvents } from '../../../../src/framework/anim/evaluator/anim-events.js';
 import { INTERPOLATION_LINEAR } from '../../../../src/framework/anim/constants.js';
 import { expect } from 'chai';
 
@@ -12,7 +13,12 @@ describe('AnimClip', function () {
         const curves = [new AnimCurve(['path/to/entity'], 0, 0, INTERPOLATION_LINEAR)];
         const inputs = [new AnimData(1, [0, 1, 2])];
         const outputs = [new AnimData(3, [0, 0, 0, 1, 2, 3, 2, 4, 6])];
-        const animTrack = new AnimTrack('track', 2, inputs, outputs, curves);
+        const animEvents = new AnimEvents([
+            { name: 'event1', time: 0.5 },
+            { name: 'event2', time: 1.0 },
+            { name: 'event3', time: 1.5 }
+        ]);
+        const animTrack = new AnimTrack('track', 2, inputs, outputs, curves, animEvents);
         animClip = new AnimClip(animTrack, 0, 1, true, false);
     });
 
@@ -25,6 +31,7 @@ describe('AnimClip', function () {
             expect(animClip.snapshot._name).to.equal('trackSnapshot');
             expect(animClip.time).to.equal(0);
             expect(animClip.loop).to.equal(false);
+            expect(animClip.eventCursor).to.equal(0);
         });
 
     });
@@ -93,4 +100,18 @@ describe('AnimClip', function () {
 
     });
 
+    describe('#time', function () {
+
+        it('updates the clips eventCursor property', function () {
+            expect(animClip.eventCursor).to.equal(0);
+            animClip.time = 1.1;
+            expect(animClip.eventCursor).to.equal(2);
+            animClip.time = 0.6;
+            expect(animClip.eventCursor).to.equal(1);
+            animClip.time = 0.1;
+            expect(animClip.eventCursor).to.equal(0);
+        });
+
+    });
+
 });