c-frame / aframe-extras

Add-ons and helpers for A-Frame VR.

Home Page:https://c-frame.github.io/aframe-extras/examples/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[animation-mixer] clips jumps and wait a random time before fading in and out

Wellan-Arthur opened this issue · comments

version :
I am using the latest version with mixer.setTime higher than action.setLoop

Context :
I am making a locomotion system that smoothly transition between a set of dynamic animations

Behavior :
When I send this

this.el.setAttribute('animation-mixer', {
                    clip: 'idle', //I just start switching between 2 animations atm, idle and walking
                    //loop: 'repeat',
                    crossFadeDuration: this.data.crossFadeDuration, // usually 0.4 in secondes
                })

The animation-mixer script happen instantly, I tested.
A few things happens (or not):

  • The movement seems to jump
  • The animation continue during a random set of seconds (from none to 5 secs) before actually transitioning with the correct cross-fade duration...

How can I prevent this behavior ?
I suspect startframe to be the issue, but I am not certain

If you want to be certain, comment the line
https://github.com/n5ro/aframe-extras/blob/5b1339d86f59e871931a7e94f51288f59364aa6a/src/loaders/animation-mixer.js#L121
do a build and please test again. Also we renamed startFrame to startAt recently.

Can you please provide a glitch example based on https://glitch.com/~aframe that uses latest aframe-extras master where you can reproduce the issue?
current build is https://cdn.jsdelivr.net/gh/n5ro/aframe-extras@09b8445/dist/aframe-extras.min.js

I confirm the issue. If you comment
this.mixer.setTime(data.startAt / 1000);
the issue goes away.
I looked a bit in https://github.com/mrdoob/three.js/blob/6cb753cd06b8f16238de23d4943eaf19e6a5a366/src/animation/AnimationAction.js
I don't quite understand the relation of all that.

Does anyone use startAt (previously startFrame) here? This feature was introduced initially by @netgfx
I'm leaning to just revert this feature before we release 7.0.0. What do you think?
If you use startAt and don't use cross fading in your project, you probably should override the animation-mixer component in your project.

The initial use case for startAt was to support animation in reverse by starting at the end animation and playing the animation backwards.
Another use case is for animations that are continuous and contain multiple animations, so the idea was to start from a specific frame /time.
Perhaps the startAt from threejs doesn't do exactly that anymore.

The mixer.setTime for threejs you mean. It probably does the use cases you mention, it just seems to interfere with the fadeIn logic here but I don't know enough to understand it.

I remixed https://gltfanimations-runtime.glitch.me (github: https://github.com/akbartus/AFrame-Runtime-GLTF-Animations)
and copied the current animation-mixer there to easily tweak it. See the weird behavior:
https://glitch.com/edit/#!/vfretin-gltfanimations-runtime
You can remix and change the code. If someone find a fix, let me know.

Had a look at this.

mixer.setTime looks like the wrong thing to use here.

action.startAt is probably going to do what you want.

This remix of @vincentfretin's glitch makes that change, and behaviour seems to be better.

I haven't yet got as far as testing different values for startAt to confirm it can be used as intended (playing clips backwards, with different offsets etc.). One observation is that this property really belongs at the scope of the clip/action, rather than the mixer, so a schema where this is specified at the animation mixer scope doesn't give all the flexibility one would ideally like.

One option might be to convert the startAt attribute into an array, so that you can specify a different startAt value for each clip being used by the mixer?
That said, the same considerations apply to timescale as well, and maybe loop/repetition. Tugging on this thread, we could easily get sucked into re-engineering the whole schema, so maybe not worth it at this point if the fix proposed above delivers the functionality suggested by the current schema...

Later today, I will try to create some explicit examples showing use of startAt & assuming those work, I'll make a PR.

The correct line of code seems to be somehing like this:

        action.startAt(this.mixer.time + data.startAt / 1000);

action.startAt takes an offset from the start of the animation, not the present moment, so if you don't include this.mixer.time you don't get consistent results.

action.startAt actually delays the animation, but you can use negative values to start mid-way through an animation.

I have an example here

For offset animations, try settings: clip: death; loops: once - with startsAt set to 0, 1000 & -1000.

To play an animation backwards, startAt doesn't seem to be useful. The settings that I have found to work are:

  • loops: repeat
  • repetitions: 1
  • timeScale: -1
    ( leave startAt at 0)

Try this with e.g. death (click the animation off/on to trigger each time).

It's interesting that backwards animations don't seem to work when loop is set to "once", but do work with loop: "repeat", repetitions: 1. Maybe that's a bug, but I didn't look into it yet.

I think this example + code fixes I've made are good enough for a PR. I'll raise that tomorrow.

Thanks for looking into it @diarmidmackenzie! It makes sense to use action.startAt now that you pointed it out, and indeed your remix seems to be perfectly working without including this.mixer.time. I think including this.mixer.time will actually introduce the delay if I understood the documentation?
When we renamed startFrame to startAt in the schema, I didn't even know about AnimationAction.startAt, so it's really a coincidence, and actually has a different meaning, it's the time in the animation we want here and not introducing a delay.
About using array for startAt to have different values for different animations, I don't think it's needed, as far as I understand the startAt option here was introduced to be able to run different animations from a single clip.

Note that in Three.js "startAt" doesn't actually start at a particular frame in the clip.

It starts the clip (from the beginning) at a particular point in time, measured since the start of the animation mixer's start time.

However, you can use this to play a clip starting at a particular frame by passing in:

(Time now - (time between 1st frame & desired start frame)).

I'll tidy up the docs in my PR, and we can discuss exactly we want startAt to function on the animation-mixer schema as part of that.

PR submitted. I eventually went for this line:

        action.startAt(this.mixer.time - data.startAt / 1000);

which preserves the directionality of animation-mixer.startAt.

without this.mixer.time results are variable (i.e. you get a different start-point each time). Not easy to notice with a looping animation like walk / run - much easier to see when you test with an animation like 'die' or 'jump'.

PR now merged
#418