0
\$\begingroup\$

I have a NPC which has a strict match logic (bot player), and I am moving it on FixedUpdate(), However, on certain events in the game I am activating animations which are controlled by the Animator - animations that are not very feasible to script.

So, when I enable the Animator, the position of my character "jumps" to the animation frame position, which causes a very jittery/unsmooth behavior. In order to move between the animations themselves, I use Animator.Crossfade which works great, but does not work on the first animation I play when I enable the Animator.

I am trying to find a way to smooth the transition, e.g. using MoveTowards() to the first frame position of the animation right before it starts playing, but I couldn't find any workaround that solves this case.

Here's an example of the character and the positions - the character has 3 Rigidbodies that I am moving independently.

Origin position when moving with FixedUpdate() (no Animator enabled): enter image description here

Animation 1st frame position: enter image description here

P.S: Try setting the animation first frame to the (approximately) last FixedUpdate() cycle is not really an option, since the position is subject to change, significantly.

Any ideas or ways of implementation would be highly appreciated.

\$\endgroup\$
2
  • \$\begingroup\$ Have you taken a look at Animator.MatchTarget? It's normally used for the opposite problem "How can I blend into a set pose by a certain frame" rather than "How can I blend from a set pose beginning at a certain (first) frame" - but it might be worth testing whether it will work with a normalisedEndTime less than the normalisedStartTime or even zero, since all the internal math should be the same. \$\endgroup\$
    – DMGregory
    Sep 25, 2022 at 13:08
  • \$\begingroup\$ I am trying, however my character does not originally contains avatar (which seems to be mandatory for the MatchTarget to work) since it's very abstract, and I will have to change a few things and ask the designers to work on that in order to make that work, as far as I understand. I'd rather try something else, unless it is indeed the only way.. \$\endgroup\$ Sep 25, 2022 at 13:55

1 Answer 1

0
\$\begingroup\$

Does something like this work? (On mobile atm, so this is a shot in the dark)

float _blendSpeed;
Vector3 _preBlendPosition;
Quaternion _preBlendOrientation;
float _blendInProgress = 1f;


void LateUpdate() {
    if (_blendInProgress >= 1f) return;

    _blendInProgress += Time.deltaTime * blendSpeed;

    float t = Mathf.SmoothStep(0f, 1f, _blendInProgress);

    transform.localPosition = Vector3.Lerp(
         _preBlendPosition, 
         transform.localPositiom, 
         t
    );

    transform.localRotation = Quaternion.Slerp(
        _preBlendOrientation, 
        transform.localRotation, 
        t
    );
}

public void StartBlend(float durationSeconds) {
    _blendSpeed = 1f/durationSeconds;
    _preBlendPosition = transform.localPosition;
    _preBlendOrientation = transform.localRotation;
    _blendInProgress = 0f;
    // Trigger your animation here.
}

The idea is to save a snapshot of your object's position/orientation just before triggering the animation.

Then, each LateUpdate, after the animation has overwritten our position and orientation with the animated values, we blend back toward our snapshot. (This doesn't affect the animated value you get next frame, since it doesn't read back "how successful" its last animated update was)

By varying the blend weight over the chosen duration, we smoothly reduce the influence of the snapshot and let the animation take over, easing at both ends to avoid a harsh snap.

(If you had a script/physics-driven velocity/angular velocity at the moment of transition, you may want to integrate your snapshot forward along that velocity each frame to give it some follow-through and blend-out, rather than a hard stop as the transition starts)

Repeat this for each bone transform that needs to be blended. For objects that have rigid body components, you may want to useMovePosition and MoveRotation instead, with world positions/orientations rather than local, to play nicer with the physics.

\$\endgroup\$
3
  • \$\begingroup\$ Trying to implement - however, at least as far as I'm experiencing, when the Animator is enabled, it overrides all the animated properties, meaning no matter which properties I would change, as long as they're animated, my code won't have any effect on those properties. What that means is that I have to enable/disable the Animator every cycle in order to make this code affect the actual position, but in that case, the animation will start over and over again, instead of continuing from the last frame it played in the previous cycle. I think the best solution for this case (check next comment) \$\endgroup\$ Sep 26, 2022 at 11:38
  • \$\begingroup\$ Would be to probably have a reset origin position, and set all the animations to start from the same position, then just move the animated object towards the origin position, then once it reached this position, trigger the animation. The main con with this method is obviously that if I change the origin position, I will have to go back to the animation and change the 1st frame of the animation as well, and it won't react dynamically, as opposed to the approach you were suggesting here above. \$\endgroup\$ Sep 26, 2022 at 11:40
  • \$\begingroup\$ "it overrides all the animated properties" correct, and it does so in the Animation Update step, which occurs before LateUpdate (that's why LateUpdate exists, so you can apply last-chance fix-ups to the animated poses before they're rendered). \$\endgroup\$
    – DMGregory
    Sep 26, 2022 at 12:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .