Animation current frame

I’m not sure if the conversation has moved on from this point, but would using speedRatio be an option?

Load Animated Character | Babylon.js Playground (babylonjs.com)

After clicking on the canvas, press (or repeatedly press) the spacebar to see the animation get “wound up,” then slow to a stop again once you stop pressing. I didn’t do it with scrolling in this case (I think the Playground was eating my scroll, so I just switched to keydown to save time), but the idea is the same. If you look at lines 64 through 75, I essentially tether the animation’s speedRatio to a variable that decays to zero (line 71), then boost that value whenever I receive a certain event (in my case keydown, but you could use scroll). This is tantamount to the “winding it up” operation you mentioned, and by changing the increment and decay rate you can change the behavior by which the animation slows down or speeds up. You can even go backwards, I think, by setting your speedRatio negative. Not sure if this does quite what you’re looking for or if you’ve already solved it a way you’re happy with, but this might at least give another possibility. :slight_smile:

1 Like

Thank you @syntheticmagus

// let camPosTime = { pos: 0 };
let nextInc = 2;
let frmStart = 0;
let frmEnd = nextInc;
let scrollMovement = false;
let durAniFrms = 30;
let keyList = [0, 2, 4, 6, 8, 10, 12, 14, 16]; // n = 8
let pos = 0;
let forward = true;

let nextKey = function (forward: boolean) {
    let from: number = pos;
    let to: number = 0;
    if (forward) {
        if (pos == keyList.length - 1) {
            from = 0;
            to = 1;
            pos = to;
        } else {
            to = pos + 1;
            pos++;
        }
    } else {
        if (pos == 0) {
            from = keyList.length - 1;
            to = keyList.length - 2;
            pos = to;
        } else {
            to = pos - 1;
            pos--;
        }
    }
    return [from, to];
};

let camTrans = function (scene: Scene, options: { from?: number; to?: number }, forward: boolean = true, aniGroup?: undefined) {
    const animationGroup = scene.animationGroups[0];
    animationGroup.pause();
    let frmMax = animationGroup.to;

    let transFrom = typeof options.from != 'undefined' ? options.from : 0;
    let transTo = typeof options.to != 'undefined' ? options.to : frmMax;
    let bezierEase = new BezierCurveEase(0.5, 1.15, 0.18, 0.99); // https://cubic-bezier.com/#1,1,.5,1

    let [from, to] = nextKey(forward);

    Animation.CreateAndStartAnimation(
        'camAnimation',
        animationGroup,
        'currentFrame',
        30,
        durAniFrms,
        keyList[from],
        keyList[to],
        Animation.ANIMATIONLOOPMODE_CONSTANT,
        bezierEase,
        () => {
            scrollMovement = false;
        },
        scene,
    );
};

Initiated by:

camTrans(scene, { from: frmStart, to: frmEnd }, forward);

This runs part of the camera animation from position pos > pos + 1 in the array, which can be set from blender/max. This uses a fixed curve to control the animation speed.

The dimming out of your approach looks really interesting to me.
I’ll give it a try when I have the chance, and will let you know how it went.

Hi @Blake, can you give me some advice on how to implement this setup on multiple animation groups?

let agSetup: Array<AnimationGroup> = [];
const agAttachCF = function (scene: Scene, AG: AnimationGroup, cam?: Camera) {
    if (!agSetup.includes(AG)) {
        if (!AnimationGroup.prototype.hasOwnProperty('currentFrame')) {
            Object.defineProperty(AnimationGroup.prototype, 'currentFrame', {
                get() {
                    return this.targetedAnimations[0]?.animation.runtimeAnimations[0]?.currentFrame ?? 0;
                    // return AG.targetedAnimations[0]?.animation.runtimeAnimations[0]?.currentFrame ?? 0;
                },
                set(frame) {
                    this.goToFrame(frame);
                    // AG.goToFrame(frame);
                },
            });
        }
        agSetup.push(AG);
    } else {
        console.log('AG currentFrame already setup');
    }
};
agAttachCF(scene, activeAnimationGroup)

On which animation does this apply?
Does the this apply on the animationGroup itself?
Applying the action to the supplied AG didn’t do the trick.

Thanks!

Oh sorry, I forgot to explain that part. The if check was only for the playground in case it’s run more than once. For local dev thou, you can just put the code in the if block into a script and add it to your html file, after the babylon.js script. It extends the AnimationGroup class so that all AnimationGroup objects will have that property available. :slight_smile:

EDIT: well it will be a little different if you aren’t using simple index.html and js files setup, but still the code just needs to be run once, not for each animation group.

Hi @Pieter_van_Stee just checking in, do you have any further questions? :smiley:

No @carolhmj at the moment this works as expected (mostly).
I can scroll back and forth through the animation which is imported from blender.
Only the smoothness could be refined in the future.

Thanks for the feedback everyone!

1 Like