📜 ⬆️ ⬇️

Making the game right before your eyes - part 7: 2D animations in Unity (“as in a flash”)

This article will talk about 2D animations in Unity. I will talk about my experience of working with my family’s animations in a unit, about how timelines are similar to the Flash ones, about managing animations, events, nesting, and how the artist copes with animation.

For a start, a little theory.

There are two entities in Unity:
')
1. Animation (what is displayed in the "Animation" window)
2. Mechanim animation tree (that is displayed in the window "Animator").



Below, I will tell you a little about what it is and how we may have to (or not be useful).

Animation


So, the animation. In essence, this is a timeline with keyframes. Here you can move, rotate, scale your objects. Naturally, you can draw curves and use different isings. And even manage any (including self-written) their properties. That is, it is possible to write a component with a float public-value "brightness" and animate this very "brightness" along with x, y, z by standard means. Sprites support frame-by-frame animation.



By the way, despite the fact that each animation has an FPS (“sample” field), the animations themselves are not attached to the FPS. They are tied to time. Those. If you make an animation with 5 FPS, where your object moves from point A to point B by setting two keyframes at the beginning and end, then in the game this object will not move with 5 FPS steps. The 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 part frames.

Animator


This is a large and complex system that directly controls the animations. That is, the animation is just a file (resource) with the settings of key frames and by itself can not do anything. That's exactly the component "Animator" - this is what can play these animations.

In addition, you can create a tree of these animations with morphing in between. Those. if you have a character that is animated with relays (when each part of the body is a separate spraytik that you rotate / move), then it is quite possible to animate the legs separately, animating the arms separately. And then (with the help of the mouse) adjust the condition that the speed of movement of your object, the mechanim animator will include either walking or running. A shoot your character will be a separate animation, which has nothing to do with the speed of rearranging the legs.

In the simplest case, your animator will look like this:



that is, contain one single animation and no links / transitions.

Begin to shaman.


So far, everything is clear. But let's think about how to do something a little more complicated?

My particular case is that we have a snow drift in which the hare sits. The drift itself moves:



Next, we want to make this animation:



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

3. snowdrift moves to the right

In principle, nothing complicated. We animate the ripple of a snowdrift inside the object, with the external animator we move it to the left, then we hide it, instead we show a frame-by-frame animation of a peeping hare, then back. And all this on one timeline (except for the “internal” animation of a snowdrift).

But this option I do not like its rigidity. First of all, I consider it wrong that in this version the frame-by-frame animation of a crawling rabbit turns out to be 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 path, we will have to re-animate the hare crawling out. And if we later want to correct this getting out, we will have to do it in all the animations where it is used.

I would like more flexibility.

There is another option. We animate the hare peeping in a separate object (just like we did with the movement of a snowdrift), but in the main time line we just turn on this object (active) at the right moment and the animation begins.

This is much better, but still not perfect. Indeed, in this case, we must in our main timeline know how long the animation of this climb is. To enable and disable it at the right time. But what if we again change this animation and the hare will look around longer? And indeed, in some more complicated cases it will be even more difficult for us to fit everything in one timeline.

It would be ideal to be able to pause the main timeline, start playing the nested animation and remove it from the pause after this nested animation is completed (or by some event in it).

That is, do so:



1. move left
2. hide the pulsating snowdrift, show the rabbit crawling animation, pause
3. hide the animation of a rabbit coming out, show a moving snowdrift, move to the right

What do we need for this? Unity allows you to add custom event event calls 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.

Let's write a method for pausing. By the way, in a unit there is no such direct possibility. In order to pause the animation, a slightly dirty hack is usually used with setting its speed to 0. It generally works, but there are some oddities (this is the last part of the article).

 public void Pause() { _animator.speed = 0; } protected void Resume() { _animator.speed = 1; } 

Where _animator is a variable in which we cached the " Animator " component:

 _animator = GetComponent<Animator>(); 


If you paid attention to the screen above, above the keyframe, which I marked with the number “2”, there is a small vertical bar. Behind it lies the challenge of the event (method) “Pause”:



It is worth noting that in such events you can even pass a parameter. String, float and object from the library are supported (not from the stage).

Ok, we paused. Now the task is to remove from the pause. Obviously, this should be done by nested animation. That is, the animation of the crawling out rabbit played to the end, and skipped upward the “go further” 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<GJAnim>(); if (a != null) { a.Resume(); return; } } } 


This method searches the parents for the component " GJAnim " and removes it from the pause. Accordingly, we set this event to end the animation of our rabbit:



Profit!


Actually, everything. We wrote a simple component that allows you to manage nested / parent animations and has enough flexibility. You may need another method of type ResumeByName(string) , which would remove a specific animation from the pause, 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 his hands of this tool, was already fully animated.

About Unity bugs and madness.


However, not everything is so smooth. At some point, after 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 its (nested) animation started playing, which removed the parent from the pause at the end. Then - the next object was shown, etc.

So, we noticed that frames sometimes skip.

Debugged for a long time, they wrote a lot to the log ... and this is what they found out:

Apparently, there is a stack of animation frames / events in the unit. And when the computer (the unity editor) slows down, it can put two frames on this stack at once, so that in the next iteration, they can both be executed.

This entails an almost completely unrecoverable file. We caught the situation when the animator performed all the actions with the frame and paused (this is ok), and then the next frame also performed the same frame. That is, for one frame I counted two animation frames at once. And the fact that in the 1st frame there was an event that sets the animation speed to 0 did not prevent him from calculating the next frame, which, apparently, was already on the stack.

And if nobody would have noticed this in the animation with the rabbit (the rabbit would have climbed to the pixel in the wrong place), then when you hide something every frame and show, there may be a file.

At the moment, the problem seems unrecoverable. How did we do it? They put FPS of such animations at 20. Apparently, on such FPS, there is no case when a unit wants to calculate two frames per iteration.

But still, the situation is not very. It turns out that with some friezes on the computer (or very slow), the player will still be able to catch the animation crashes.

What to do with it is not clear.

All articles in the series:
  1. Idea, vision, choice of setting, platform, distribution model, etc.
  2. Shaders for styling images under the CRT / LCD
  3. We fasten the scripting language to Unity (UniLua)
  4. Shader for fade in on the palette (a la NES)
  5. Subtotal (prototype)
  6. Let's talk about the indie games
  7. 2D animations in Unity ("as in flash")
  8. Visual scripting of cut scenes in Unity (uScript)

Source: https://habr.com/ru/post/235011/


All Articles