
Creating a game before your eyes - part 7: 2D animation in Unity (“like in flash”)
- Tutorial

For starters, a bit of theory.
There are two entities in Unity:
1. Animation (what appears in the Animation window)
2. Mechanim animation tree (what appears in the Animator window).

Below I will tell you a little what it is and how we may have to (or not come in handy).
Animation
So, the animation. In fact, it is a timeline with key frames. Here you can move, rotate, scale your objects. Naturally, you can draw curves and use different isings. And even manage any (including self-written) of their properties. That is, it is quite possible to write a component with a float public value “brightness” and animate this “brightness” along with x, y, z using standard means. Sprites support frame-by-frame animation.

By the way, despite the fact that each animation has an FPS (field “sample”), the animations themselves are not attached to the FPS. They are attached to time. Those. if you make an animation with 5 FPS, where your object moves from point A to point B by setting two key frames at the beginning and at the end, then in the game this object will not move in steps of 5 FPS. Animation is calculated every frame of the game, and the FPS inside the animation is made only for your convenience, so that you do not divide frames.
Animator
It is a large and complex system that directly manages animations. That is, animation is just a file (resource) with the settings of key frames and in itself does not know anything. That's the component "Animator" - this is what knows how to play these animations.
In addition, you can create a tree of these animations with morphing between them. Those. if you have a character animated by shifts (when each part of the body is a separate sprite that you rotate / move), then it’s quite possible to make an animation of the legs separately, animation of the arms separately. And then (with the help of the mouse) adjust the condition that, depending on the speed of your object, the mechanim animator will include either an animation of legs “walking” or “running”. And to shoot your character will be a separate animation, which is in no way connected with the speed of moving legs.
In the simplest case, your animator will look like this:

that is, contain one single animation and no connections / transitions.
We begin to shaman.
So far, everything is clear. But let's think about how to do something a little more complicated?
My specific case is that we have a snowdrift of snow in which a hare sits. The snowdrift itself moves:

Next, we want to make such an animation:

1. the snowdrift, moving, moves to the left
2. the hare peeps out of the snowdrift (the ripple animation stops):

3. the snowdrift moves to the right
In principle, nothing complicated. We animate the pulsation of the snowdrift inside the object, move it to the left by the external animator, then hide it, instead show the frame-by-frame animation of the looking hare, then back. And all this on one timeline (except for the "internal" snowdrift animation).
But I do not like this option because of its rigidity. First of all, I think it’s wrong that in this embodiment, the frame-by-frame animation of the crawling rabbit appears on the same timeline as the movement of the snowdrift. This means that if we want to make a variation of this animation, where the snowdrift will move along a different trajectory, we will have to animate the hare crawl out again. And if we then want to correct this crawl, we will have to do this in all the animations where it is used.
I would like more flexibility.
There is another option. We animate peeping of a hare in a separate object (just as we did with moving a snowdrift), and basically just turn on this object (active) at the right time and the animation starts.
This is already much better, but still not perfect. Indeed, in this case, we should know in our main timeline how long the animation of this crawl is. To enable and disable it at the right time. But what if we again change this animation and the hare will look longer on the sides? Anyway, in some more complicated cases it will be even more difficult for us to fit everything into one timeline.
It would be ideal to be able to pause the main timeline, start playing the attached animation and pause it at the end of this attached animation (or by some event in it).
That is, to do so:

1. move to the left
2. hide the pulsating snowdrift, show the animation of the rabbit crawling out, pause
3. hide the rabbit’s crawl animation, show the moving snowdrift, move to the right
What do we need for this? Unity allows you to add custom event events to the animation. This is exactly what we need! It remains only to write everything correctly.
The first thing we need is to write a simple component (in our case it is called
GJAnim
) and hang it on the same object on which our animator hangs. It is the methods of this component that we can call events from the timeline. We will write a method for pausing. By the way, in unity there is no such direct opportunity. In order to pause the animation, a slightly dirty hack is usually applied with setting its speed to 0. It generally works, though there are some oddities (more about that in the last part of the article).
public void Pause()
{
_animator.speed = 0;
}
protected void Resume()
{
_animator.speed = 1;
}
Where
_animator
is the variable in which we have cached the " Animator
" component :_animator = GetComponent();
If you noticed the screen above, there is a small vertical line above the key frame, which I marked with the number “2”. It is behind it that the “Pause” event (method) call is hidden:

It is worth noting that you can even pass a parameter to such events. Supported string, float and object from the library (not from the scene).
Ok, we paused. Now the task is to pause. Obviously, nested animation should do this. That is, the animation of the crawling rabbit played through to the end, and threw up the "go ahead" events.
public void ResumeParent()
{
Transform pr = transform;
while (true)
{
pr = pr.parent;
if (pr == null)
{
Debug.LogWarning("No GJAnim found in parents!");
return;
}
GJAnim a = pr.gameObject.GetComponent();
if (a != null)
{
a.Resume();
return;
}
}
}
This method searches for the "
GJAnim
" component among the parents and pauses it. Accordingly, we put this event at the end of the animation of our rabbit:
Profit!
Actually, that's all. We wrote a simple component that allows you to control nested / parent animations and has sufficient flexibility. Perhaps you will need another type method
ResumeByName(string)
that would pause a specific animation, and not the first parent. In addition, everything is done within the Unite UI and is transparent enough for any animator. Our artist, after an hour of falling into the hands of this instrument, has already animated.
About Unity bugs and craziness.
However, not everything is so smooth. At some point, creating the animation, we saw that it was behaving incorrectly.
We had a parent (main) animation that showed one object (hid all the others), paused, at this time our own (embedded) animation began to play in this object, which removed the parent from the pause at the end. Next - the next object was shown, etc.
So, we noticed that frames sometimes skip.
They debated for a long, long time, wrote a lot to the log ... and here's what they found out:
Apparently, there is some kind of stack of frames / events of animations in the unit. And when the computer (unity editor) slows down, it can put two frames on this stack at once in order to execute both of them in the next iteration.
This entails an almost completely incorrigible file. We caught the situation when the animator performed all actions with the frame and paused (this is ok), and then the next frame was also executed in the same frame. That is, two frames of animation counted at once in one frame. And the fact that in the 1st frame there was an event that set the animation speed to 0 did not stop him from calculating the next frame, which, apparently, was already on the stack.
And if no one would notice this in the animation with the rabbit (the rabbit would crawl out onto the pixel in the wrong place), then when you hide something and show every frame, there may be a file.
At the moment, the problem seems incorrigible. How did we do it? They put FPS of such animations at 20. Apparently, in such a FPS, the case when a unit wants to calculate two frames in one iteration does not happen.
But still, the situation is not very. It turns out that with some friezes on the computer (or on very slow ones), the player will still be able to catch the failure of the animations.
What to do with this is not clear.
All articles in the series:
- Idea, vision, choice of setting, platform, distribution model, etc.
- Shaders for styling pictures under CRT / LCD
- We fasten a scripting language to Unity (UniLua)
- Shader for fade in by palette (a la NES)
- Subtotal (prototype)
- Let's talk about PR indie games
- 2D animations in Unity ("like flash")
- Visual scripting of cut scenes in Unity (uScript)