At first we wanted to see our game as unhurried experience to push our player for some exploration. For the first year of development, we followed this path, simultaneously creating various situations inside the game. This gives us an opportunity to understand what kind of gameplay we want to give.
However with each new mechanic applied, the pace of the gameplay accelerated proportionately. We have a need to make changes to the main character animations. The character was changing and his visual appearance/behavior began to affect literally everything.
Following the hero, enemies began to receive visible changes too. It gives more organic movements for them and visible attacks, so player will be able to recognize them faster. But we will tell about this approach in another issue of our diary.
This once again shows the importance of pre-production and a clear vision of the gameplay.
Now let's talk about animator
Starring: Unity Animator, Fair Pixel Programmers.
Behind the scenes of character evolution there is a little technical part of the project history. Since each of our animations consists fully rendered frames, and there are no object rotations in animations, we decided that it would be easy af to manage them. After all, there is an Animator and all sorts of connections, transitions, calls to class methods. I think you know how it works.
The first Pipeline was: the artist draw frames and gave them to programmer as it is, or as assembled animation in Unity (just a set of consecutive frames, sometimes with different frame intervals), and the programmer added animation to the animator and set up connections.
The number of animations grew. The states became more complicated. The number of transitions increased. This led us to the animation frames becoming animations in animator. And then you probably start thinking "What the hell are they talking about?".
Maybe cause we have a lack of experience, or because of fear of what we saw previously, we decided that the standard Unity Animator was too gorgeous (hard) for us.
First, we started to spend a lot of time on it. Setting up, debugging is still half the trouble. Expanding animations, logic, code calls, more complex things - that was a pain.
Secondly, we decided to separate the arms with firearms from the body. All because we don't wanna draw the entire character with weapons for each type of condition. Imagine that a character with a pistol can aim while standing, sitting, in motion. And the weapon does not spin like an object. A separate position of the weapon is a specific shot. True pixel bois! And then again get the gun, hide, reload and again...standing, sitting, in motion...progression!
Therefore, it was decided to write my own solution. Which would be easier in the settings and would solve everything written above. As a result, information about animation is stored in a container (thanks to ScriptableObject) and is described by three fields: identifier, frame spacing and a set of sprites.
In the beginning, there was one class for managing animations. It's task was to take information about animation and just switch sprites in time.
With the emergence of new requirements, the architecture changed. At the moment, part of the architecture looks like this:
AnimationManager is in every character. It stores and manages animation layers. The LayerController contains a list of all animations layer. Animation is controlled by AnimationController. Thus, the animation data from the ScriptableObject turns into an AnimationController, which contains a set of commands and events.
Let's have an example on code imitation.
The CharacterExample class shows the initialization of the animator. The AnimationManager accesses the object's component (SpriteRenderer) and animation data. We will not discuss the StateManager today, let's briefly say that this is a state machine for various living objects in the game.
The IdleState class demonstrates a simple call to change the animation when the Idle state starts.
The JumpState class demonstrates call options and animation events. When the state starts, the animator will switch to JUMP animation. It will then switch to JUMP_MOTION animation when the condition is triggered in the FrameHandler event. Upon completion of animation, transition to the IDLE state will be triggered. Once again, this is a pseudocode ... A simple demonstration of some possibilities.
Thus, we managed to get full animations control, their display and gameplay adjustments which means there were no “breaks” and other unpleasant things.
We've won. It was such a small for someone maybe, but such an important thing for us.
It will be very interesting for us to hear your examples of solving our own bicycles. Or maybe you have some tips on how to solve our problem.
We will be glad to hear from everyone!