Interpolactic: A Behind-the-Tweens Look
How we animated our boilerplate from “cray” to “YAY!”
A few weeks ago, thirteen23 made its long-awaited debut in the Unity Asset Store. We’d like to introduce you to Interpolactic, available for free because there’s no money in video games anyway. Like any milestone of invention (see: penicillin, Juul) Interpolactic is a library born of dire necessity: in this case, the necessity to stop writing redundant, error-prone code when scripting stupidly simple animations.
Out with the old
Let’s run through the previous convention. I’d like to introduce you to a seemingly innocuous block of code that should be familiar to most Unity developers. It’s a Coroutine that animates an object’s position from Point A to Point B, and invokes a callback upon completion:
Eighteen lines, just nine if you discount whitespace and brackets. It’s not awful to write, but when you need to repeat this or something similar every time an object is to be moved, it gets old fast. Here’s why:
- It’s easy to screw up
Several pitfalls for careless devs here. We have to keep track of elapsed time in an accumulator. We set the final position after the loop. We have to remember to call the completion block, but not before we do a quick null-check. Nine lines of code means at least nine points of failure, and debugging this block is especially hard because…
- It’s a maintenance liability
Each of these blocks is just 20 more lines of text you have to pour through whenever there’s a bug in your animation. It’s honestly tiring to manage and, at best, acts as clutter around more meaningful code.
- It’s hard to generalize
Want to selectively apply an easing function? Better add another parameter to this function’s signature. What about a delay? That’s a parameter, too. What if you want a delay and easing, AND you want this motion to ping-pong back and forth indefinitely? Calling this function balloons in complexity with each new way it can be tweaked.
- It’s coupled to type
Suppose this Coroutine was used to slide a button out when a user hovers over it, for emphasis. What if we change our mind, and want to animate the button’s color instead? Already on the first line, the function’s signature has three parameters (one Transform and two Vector3s) that don’t belong in this new context. We’d have to replace practically this whole function with something that makes sense for changing color. If you’re calling this guy often, you could be looking at a painful refactor.
I don’t want put Unity down too hard, because to be fair, there already exists an alternative solution to animations, in the form of the built-in animation engine:
On my most recent project, Minotauroid, I encountered a number of use cases in which Unity animations were heavy-handed at best, and ineffective otherwise. Largely, these cases were small visual effects and movements with dynamic stop and start points, like when moving pieces about a grid.
Don’t get me wrong: for math-intensive tasks or for animating from a third-party source like Blender, Unity’s built-in editor is the way to go. However, small animations such as fade-ins and transitions in the UI are often so minor and plentiful that creating Animation files for them is time-consuming and cluttering.
So, we’ve got a choice: We can roll with the low-level verbosity of Coroutines, or the wholesale abstraction of Unity’s tank-like animation engine. I was reeeeally hurting for a middle ground in which I could quickly script animations and fire them off.
Improving on the convention
Take another peek at that Coroutine block at the top. There’s only one interesting line of code here. It’s this one:
toMove.position = Vector3.Lerp(startPosition, endPosition, elapsedTime / moveDuration);
Nearly everything else in that function is there to facilitate this line. Crazy, right? 90% of that block is incidental to the thing we actually want to do. It’s like spending ten minutes putting on winter gear to walk to your mailbox.
Moreover, a lot of this code doesn’t change between different animations. The accumulator for elapsed time? Setting the property to its final value after the loop? The onComplete method? All staples of Coroutine-based animations.
The fluff is not only boring, but redundant. It’s the textbook definition of boilerplate, and it sucks to write.
In with the new
Harnessing the power of Interpolactic, we can perform this same animation in a couple of lines:
Gorgeous, right? Let’s break down this constructor:
new Interpolation(t =>
toMove.position = Vector3.Lerp(startPosition, endPosition, t)
One thing gets passed to create the interpolation: a function that acts on a float, t, that ranges from 0 to 1. This is what’s getting called at every frame of execution.
It’s identical to the only interesting line in our original animation, with one difference: we’re calling that Lerp with a normalized t, rather than doing some math with the elapsed vs. total time. Interpolactic’s removed the need for any of that extra stuff.
What if we want to scrap that onComplete, and instead have the object move back and forth in this animation indefinitely? Easy:
Speaking of easy, how about some easing? For this, Interpolactic only needs to be pointed to a function. SmoothStep is okay, but I’m fond of the LeanTween plugin, and its associated easing utilities:
Isn’t that cool? Obviously these two plugins aren’t bundled together. Instead, because its easing function is injected, Interpolactic can be seamlessly extended with the capabilities of other libraries, such as LeanTween.
Beyond the Transform
So, Interpolactic handles the timing boilerplate from our Coroutines, and asks us to define what happens at each step. This relationship is really cool, because Interpolactic doesn’t have to care about anything other than spitting out that normalized t value.
This means animations can be more easily swapped out for each other. Remember that example earlier about needing to change a position animation to a color animation? Now, we’ve just gotta change line 2:
Personally, my favorite thing about Interpolactic’s agnostic take on animation is that we can use the t value to do some unconventional stuff.
Here’s a snippet from the Interpolactic demo that simulates a typewriter effect on a Text UI object:
Our SetString function just sets the Text component’s contents to a proportion of our message, starting with nothing and ending with the full string. If we can make something act on a ratio, you bet we can animate it.
Look, animations are important in digital interaction, especially in video games. They help to sell your game’s world as dynamic and reactive, and fulfill players’ expectations of how objects behave in real life.
We don’t just want to save you time, but to help encourage experimentation by lowering the barrier to draft animations. What I love about making games is the immense visual expression afforded by the medium, but no developer knows for sure what works until they try it.
So, take this as a challenge: whether or not Interpolactic is your cup of tea, find your workflow for crafting animation, go a little nuts, and see what sticks. There’s a lot of potential at your fingertips, and it’s time to get moving.
In the meantime, Interpolactic is open-source, so feel free to give it a fork or hit us up for a feature request.