AnimationCurve as fixed time rather than portion of time?

Sorry for the title gore, but I’m new to using Animation Curves and I wasn’t sure how best to weird the issue.

I’m using an AnimationCurve variable to animate the size of a bubble shield type object. On activation, the shield will grow from a scale of 0 to slightly greater than its intended scale and then quickly revert back to its intended size for the duration of the shield, just before the shield has reached its duration the animation will perform in reverse. The best example of what I’m after can be found in Animal Crossing: Pocket Camp, when dialogue is initiated with an NPC the text bubble animates in a similar way; though slightly more subtle.

The problem I’m having is that I want the bubble’s opening and closing animation to take the same amount of time regardless of how long the bubble’s duration is, but the AnimationCurve works on a scale of 0 to 1. This means that if the curve is set from a time of 0 to 0.1, and the bubble duration is 5, then the opening animation will last 0.5 seconds whereas I might want it to last o.1 seconds regardless of duration.

The curve and coroutine for the animation are as follows:

	private IEnumerator ActivateShield() {
		m_DurationElapsed = false;
		m_CooldownElapsed = false;

		m_ThisTransform.localScale = Vector2.zero;

		for (float x = 0f; x < m_ShieldCooldown; x += Time.deltaTime) {

			if (x <= m_ShieldDuration) {
				float y = m_ShieldCurve.Evaluate(x / m_ShieldDuration);
				m_ThisGameObject.transform.localScale = new Vector2(y, y);
			}
			else if (!m_DurationElapsed) {
				m_DurationElapsed = true;
				m_ThisSprite.enabled = false;
				m_ThisCollider.enabled = false;
			}

			yield return null;
		}

		m_CooldownElapsed = true;
		m_ThisGameObject.SetActive(false);
	}

Well, like Rob said you want to handle the growing / shrinking seperately from the actual duration. If the collapsing animation should always look exactly like the growing animation you can just create a curve for growth and play it in reverse when collapsing. If you want to allow a seperate grow / collapse animation you can either use two seperate curves or just “split” the curve into two logical parts. For example having the time 0 - 1 represent the growing animation and 1 - 2 the collapsing animation. You can simply do:

private IEnumerator ActivateShield()
{
    m_DurationElapsed = false;
    m_CooldownElapsed = false;
    m_ThisTransform.localScale = Vector2.zero;
    Transform trans = m_ThisGameObject.transform;
    // growth
    for (float t = 0f; t <= 1f; t += Time.deltaTime/m_GrowthTime)
    {
        trans.localScale = Vector2.one * m_ShieldCurve.Evaluate(t);
        yield return null;
    }
    
    // duration
    yield return new WaitForSeconds(m_ShieldDuration - m_GrowthTime - m_ShrinkTime);
    
    // shrinking
    for (float t = 1f; t >= 0f; t -= Time.deltaTime/m_ShrinkTime)
    {
        trans.localScale = Vector2.one * m_ShieldCurve.Evaluate(t);
        yield return null;
    }
    
    m_DurationElapsed = true;
    m_ThisSprite.enabled = false;
    m_ThisCollider.enabled = false;
    m_CooldownElapsed = true;
    m_ThisGameObject.SetActive(false);
}

In this case we just play the same curve “backwards” (from 1 back to 0). Note that the time inside the curve should have a fix range from 0 to 1. The actual speed of the growing / shrinking is controlled by the “m_GrowthTime” and “m_ShrinkTime” variable. For the duration we just wait for the remaining amount of time. Though if you want the animation to be interruptable you can replace the WaitForSeconds also with a loop like the other two.

If you want both animations within the same curve you would replace the last loop with something like this:

    // shrinking
    for (float t = 1f; t <= 2f; t += Time.deltaTime/m_ShrinkTime)
    {
        trans.localScale = Vector2.one * m_ShieldCurve.Evaluate(t);
        yield return null;
    }

So this would just “play” the curve from 1 to 2.