How can I mirror an AnimationCurve properly

Hey, I did some research, and it seems there isn’t really any solution for that.

I tried to do it like this :

private AnimationCurve GetMirrorCurve(AnimationCurve curve) {
    AnimationCurve mirror = new();

    for (int i = 0; i < curve.keys.Length; i++) {
      Keyframe key = curve.keys[i];
      Keyframe mirrorKey = new(key.value, key.time);

      // 89.999f instead of 90f because I got -90f as a result for the last key (the flattest one) for some reason...
      mirrorKey.inTangent = Mathf.Tan(Mathf.Deg2Rad * 89.999f - Mathf.Atan(key.outTangent));
      mirrorKey.outTangent = Mathf.Tan(Mathf.Deg2Rad * 89.999f - Mathf.Atan(key.inTangent));
      mirrorKey.weightedMode = key.weightedMode;
      mirrorKey.inWeight = key.inWeight;
      mirrorKey.outWeight = key.outWeight;

      mirror.AddKey(mirrorKey);
    }

    return mirror;
  }

But I get super weird results with the weights. Like, they are huge.

Here’s a comparison of the weight of first key (left) and its mirror (right). Look at those weights differences (look at the scale)

Changing the weightMode to None don’t seem to have any effect.

Can someone help ? I’ve been stuck on this for days.

Thanks.

On which axis are you trying to mirror? X, Y, or both?

I want to mirror it on the function f(x) = x.

9900888--1430049--upload_2024-6-21_1-6-14.png

That would be transposition. You would need to calculate the slope at each keyframe, transpose that, and then provide that as the new tangent for the new keyframes. But not every AnimationCurve can be transposed, as an AnimationCurve must be a simply-defined function in the key domain, but some curves would have transpositions that have multiple solutions or incomplete solutions.

An AnimationCurve cannot define the yellow curve, which is the transpose of the green curve, for example.

In your specific case, the function transposition glitches out because your curve has a local maximum before the right edge.

1 Like

So you mean my function is actually not a strictly increasing function as I thought it was ?

So is my code actually wrong, or is it right, but fails to work because of that local max ?

Another way of fixing my issue would be to find a time for a given value. AKA the opposite of the Evaluate method.
But it doesn’t seem to exist. Do you know any way to do this ? I couldn’t find any.

This still has the problem that not all functions can be transposed. Some values of Y may have multiple or no values for X. Again, in your case, the function does not appear to be “strictly increasing” or not increasing enough for the evaluation you’re doing at the right end of the domain.

So what’s a solution for this ? I know it’s possible, because I’ve seen it in a unity made game. UI there goes in and out the exact same way: fast at first and slow in the end.
I used Cheat Engine to slow the game time, and if I enter/cancel menu while the UI is still moving, it will go the other way, properly, while having the correct expected speed.

How could he have done it ?

In your case, have you noticed that your desired inverted curve function is basically (1f - curve.Evaluate(1f - x)) , assuming the diagonal is 0,0 to 1,1? In other words, it’s not just a transpose that is mirrored around the red line, but it is also a 180º flip of the whole diagram?

Here’s a video of the game in question, slowed down 20 times

9901689--1430265--UI-animation.gif

Yes the first and last keys are 0,0 and 1,1.

I’m not sure what to do with this calculation though ? How can I use it to animate my UI the same way as in the gif above ?

Thanks for the help !

If you ramped some value in with y = curve.Evaluate(x), then you can ramp it back out symmetrically with y = curve.Evaluate(1f - x), or flipped with y = 1f - curve.Evaluate(1f - x). If you didn’t like either one, then just draw a companion curve yourself manually instead of trying to find the elusive transposition.

Trying to find a mathematical transposition of your particular curve is not the only approach to this, and indeed not possible in the general case for all curves, and honestly if you’re ramping Time.timeScale nobody is going to notice the finer details.

Why not just… manually input two different animation curves?

I tried using 2 different curves but it’s super difficult to have them so close that for a given time/value, the UI would stay at the same place. For quickly increasing functions like this, a tiny error can immediately move the UI half of the way.
Even at normal speed, I can easily see it’s wrong :confused:

The movement is perfectly smooth in the video, when changing movement direction, we can clearly see no “UI jumps”. And he keeps the same curve apparently, fast then slow, regardless of direction.

How could he have done this ? It seems way to perfect for it being another manually made curve.

I even slowed down the game to 1000 times, and it has litterally no jump at all. It has to be the same curve, right ? So it’s gotta be this one, correct ?

y = 1f - curve.Evaluate(1f - x)

Ok so let’s see if I understand :

Currently I move my UI from point A to point B when it moves “forward” and from B to A when it moves backwards.
A and B are apart by a value of, say, 100, and UI only moves along the X axis.

That means I have the initial and final positions (which become the final and initial positions when moving backwards).

I have my one curve.

When UI moves forward, the distance between the initialPos and the current one should be 0 at t=0 and 100 at t=1
So say I want to go backwards when time is between 0 and 1.

I should set the initialPos to equal the finalPos, set the value to -100, and set the new time to :
newTime = 1f - curve.Evaluate(1f - curve.Evaluate(oldTime))

Because here x = curve.Evaluate(oldTime).

Is that correct ?

Ok other option : I found a package containing many drawn curves way better than mine and there are 2 that are the ones I need.

So here they are :

So for a given time/value (red) I need to find the corresponding yellow values. I will be travelling the red curve with increasing time, and the yellow curve with decreasing time.

I can’t seem to make any of your calculations work in a way that would give me the right results, maybe I’m using them wrong ?

I believe I should only be using the “flipped” equation in my case, though.

Thanks again for the help, it’s greatly appreciated.

I found it !

So I figured the curve I used was actually created from the function y = x^4.
Thanks to your formulas, I manage to make it work !!

Thanks guys !

This is not related to the original question, but found this while searching for a solution to achieve the effect shown on this GIF, hope it helps others here for the same reason.

You can create this effect by storing the current time value that is used to evaluate the curve, and start the opposite effect from that time.
This also has the upside of working with every animation curve, because you essentially store a 0 to 1 value, and can use it to evaluate anything with a duration.

Sometimes, you don’t need to solve an impossible math problem, to create a fancy effect in a game. :slight_smile: