Gamasutra: The Art & Business of Making Gamesspacer
View All     RSS
July 24, 2014
arrowPress Releases
July 24, 2014
PR Newswire
View All
View All     Submit Event





If you enjoy reading this site, you might also want to check out these UBM Tech sites:


 
Animation in 2D Unity Games: In-Depth Starter Guide
by Alex Rose on 09/05/13 10:12:00 am   Featured Blogs

The following blog post, unless otherwise noted, was written by a member of Gamasutra’s community.
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.

 

Animation in 2D Unity Games: In-Depth Starter Guide

So, Unity recently announced extra 2D game support, with the addition of Box 2D physics and a sprite manager.

But there’s a few tricks you still need to keep in mind. Changing the images frame by frame is just the tip of the iceberg for animation; to really make your game run beautifully, you have to understand how to use translation and rotation to your advantage.

We’ll start with the basics for now though:

Frame Changing

So, you have your textures ready for animation. You may be using the publicly available SpriteManager script, the paid version, or Unity’s own new version, in which case frame advances should be pretty second nature. Let’s say you’re using 2D planes and textures for now though. It’s an inefficient method, but if you’re doing a game jam for instance, you might want to throw together something that’s functional and good looking, but not necessarily efficient. It’s also a fairly comprehensive method that covers all steps, some of which are cut out by sprite managers.

First of all, you’re going to want a public Texture[] array, so you can drag your textures into the object from Unity’s editor and an integer currentTexture initialised to 0 in Start(). Next you want a NextTexture() function that works like this:

 
NextTexture(){
currentTexture++;
if(currentTexture>=textureArray.Length) currentTexture=0;
AnimatedPlane.renderer.material.mainTexture = textureArray[currentTexture];}

This will change the plane’s texture to the next frame in the animation.

There are two easy ways to call this function: Coroutine recursion and fixed intervals.

Using fixed intervals is the quickest (but less precise) method. You’re going to need an int counter, initialised to 0 in your Start() function, and a FixedUpdate() function, which updates every Time.deltaTime (you can vary this yourself in Unity’s Time Manager).

Inside FixedUpdate(), place your conditional (e.g. if(walking)), and inside it increment your counter with counter++. Then set the following statement:

 
if(counter>=animationDelay){
counter=0;
NextTexture();
}

Where animationDelay is an arbitrary value of your own choosing. This will advance the frames at a constant rate (depending on the rate you set in Unity’s Time Manager.

The second method is to use recursion. The downside to this is that it’s clumsier to deal with conditionals, but you’ll get the exact time delay you want. This is especially useful if you want a certain frame to be of a longer or shorter length. You’ll need an IEnumerator TextureChanger() and to StartCoroutine(TextureChanger()) in Start().

 
IEnumerator TextureChanger(){
yield return new WaitForSeconds(timeInterval);
if([conditions]) NextTexture();
}

Where timeInterval is a float of your choosing. With these functions, you can drag any number of textures onto your GameObject and it’ll animate correctly, provided you give the right conditions.

Now let’s move on to something more interesting.

Smooth Movement to a Point

The following formula is the holy grail of animation:

The holy grail of 2D animation in Unity

where 0 < slidespeed < 1. I recommend 0.1f as a good slidespeed value.

This formula will allow you to animate your objects beautifully to a point. This is extremely
useful in sliding GUIs, character control, level spawning, camera following, colour fading/shifting etc.

Descending 2D Animation from Rotation Station in Unity

Here’s an advanced version of it from my upcoming game Rotation Station that moves towards a lower point first and then back to a higher point to make the little bob at the end. Every tile moves down according to that formula, although each one has a random delay, and a random initial rotation, which also uses this formula to rotate it into its desired orientation.

An example of using this for character controls is in my recent Ludum Dare entry Rude Bear Radio. In this case, the formula is applied to make fluent mouse controls.

2D Animation Towards the Mouse from Rude Bear Radio in Unity

So, let’s look at how to apply it in the above example (a 2D GameObject following the mouse).

First of all, we need to know where the mouse is on the 2D area. In order to find that, we first place this code into the FixedUpdate() function of the sliding GameObject:

 
Vector3 MousePosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,
Input.mousePosition.y, transform.position.z-Camera.main.transform.position.z));

So, this uses the mouse’s x and y position, and the distance from the camera to the sliding GameObject to determine the Mouse’s 2d position in 3d coordinates. Now we’re going to adapt the formula from the start of this section. So, remember,

2D Step animation in Unity

 
transform.position += new Vector3((MousePosition.x-transform.position.x),
(MousePosition.y-transform.position.y),0)*slidespeed;

And there you are – done! Two lines of code. For something like a GUI, you would then write a statement saying:

if(Mathf.Abs(finalvalue-currentvalue)

Let’s look at another example, which you can see in Rude Bear Radio’s hard mode Mario stage.

2D Animation of Background Colour from Rude Bear Radio in Unity

The background animates from black to white slowly, using this formula:

 
background.renderer.material.color =
(1-factor)*background.renderer.material.color+factor*desiredcolor;

You can see here that it follows the basic form. Next = current+(final-current)*factor, just via shorthand. The factor starts low. The code checks whether the R value is within a certain range of the color, and if it is, it shifts up the factor so it fades quicker. Once the R value gets really close to 1, it sets the desired colour to black. You could also check for R, G and B, and advance the colours in an array (similar to our NextTexture() function from before). You can see an example of this constantly in the background of my second LD entry Rude Bear Rising (which is a buggy mess, but a good example of this feature, and also uses this formula to focus the camera on the player once they move too far from the screen).

Okay, so this has all been easy so far, you just stick in a formula and it’s done. The next (and possibly most important principle of all) requires a bit more thought than that.

Trigonometry and You (Or: Why you should’ve been
paying attention in Maths)

Trigonometry is crucial to animation. Frames are all well and good, but they won’t make things look truly beautiful, and sometimes you can do without the frames at all.

e.g. The first time I entered Ludum Dare, my housemate hand drew the pictures for me. I had one still image for each character. The solution? Use puppets on sticks.

2D Animation Puppet Style from Rude Bear in Unity

Now, the way to achieve motion like this is very simple. Sin for translation, cos for rotation.

To create animation like this, you’re going to want the waves to stop and continue when you let go and input again, otherwise the motion will be extremely sporadic.

So you want an overarching variable (which I called walkbob), which adds Time.deltaTime onto it in FixedUpdate as long as the object is moving. Then you make your functions.

 
translation = maxHeight*Mathf.Sin(speed*walkbob);
rotation =  maxRoll*Mathf.Cos(speed*walkbob/2);

Then you simply set the position and rotation to these values (e.g. transform.position = new Vector3(transform.position.x,translation,transform.position.y)).

This will handle motion like that. However, a kind of animation that requires a bit more thought is what I like to call trig dancing. It’s useful for making cute characters dance to the music. You can see it here in Rude Bear Radio.

2D Animation of a Dancing Radio from Rude Bear Radio in Unity

So, here’s how it works. First of all, as soon as you intend to start moving your object, you want to take a float initialtime = Time.time. This is so that your object starts in the correct position and orientation and doesn’t suddenly leap into action.

Next, we’re going to think properly about trig functions and what they mean.

We’re using simple harmonic motion, and this follows the form:

Sin formula for 2D animation in Unity.

where y is the current value, A is the amplitude, f the frequency, t is the elapsed time and phi is the phase. First of all, the amplitude is easy to determine. That’s the maximum height or rotation we want our object to have.

Next is the elapsed time and phase. We’re going to handle both at once easily by replacing t by (Time.time-initialtime). This reduces φ to 0. So, finally, we just need our frequency. I would heavily recommend fitting the frequency of this to the frequency of your music (which is especially easy if you wrote it yourself).

If you don’t already know the BPM of your music, go here and tap every beat until you know it. If you have rhythm it’ll take no time. If you don’t, no worries, we’ll just take advantage of the central limit theorem. Just keep tapping for the whole duration of your song. The error on the average value will decrease with every tap.

So now you know it in beats per minute. You’ll need to divide this value by 60 to find out how many beats per second. Then you may want to divide it by 2 or 4 (or even more), if you only want to use motion on half a bar, or a full bar. This value is the frequency, and you can get pi from Mathf.PI. So now you just want to set the object’s position to that value. So, say you’re only modifying the height:

 
transform.position = new Vector3(transform.position.x,maxheight*
Mathf.Sin(2*Mathf.PI*frequency*(Time.
time-initialtime)),transform.position.z);

But this isn’t good enough. First of all, we want the object to arrive on the beat, so it should start at its maximum amplitude. We want to use cos in this case. But more importantly, it should be leaping from side to side, so it shouldn’t just slide up and down like a wave. It needs to use cos^2, so it abruptly stops at the 0 mark and becomes positive again. Therefore:

 
transform.position = new Vector3(transform.position.x,maxheight*Mathf.Pow(Mathf.Cos(
2*Mathf.PI*frequency*(Time.
time-initialtime)),2),transform.position.z)

This takes care of the dancing height. Finally the rotation should use sin, such that the rotation and translation are out of phase. So:

 
transform.rotation = Quaternion.EulerAngles(0, maxRotation*
Mathf.Cos(2*Mathf.PI*frequency*Mathf.Sin(Time.
time-initialtime)), 0);

Here there are two things to keep in mind: if you’re using a plane and you want it to face the camera, those values aren’t going to be 0 and 0, but pi/2 and –pi/2 respectively. But also of great importance is that I’ve used EulerAngles rather than Euler here. EulerAngles is deprecated in favour of Euler, because Euler uses degrees (which Unity usually handles) and EulerAngles uses radians. We’re doing proper maths – we’re working in radians, so use EulerAngles! Otherwise you’re going to have to put in a conversion factor too.

Here you can see a similar kind of animation from the title screen of my upcoming game how you can change the scale instead of the position in the same way. You’ll have to use half the period and -cos^2 for this.

2D Animation of Rotation Station's title screen in Unity

This brings us to the final category of animation I’ll discuss today:

Texture Offsets

You can use everything you’ve learnt so far to manipulate 2d texture offsets to make cool animated backgrounds. You can see this in my VVVVVV parody in Rude Bear Radio, and on its main screen. I’ll leave this one as an exercise. Grab a plane, stick a repeating texture on it, write up a FixedUpdate() function, and fiddle trigonometrically with these properties:

renderer.material.mainTextureOffset
renderer.material.color

This will cause the wall to slide around and change colour. Finally, if you want things to look really interesting, you can also play with renderer.material.mainTextureScale. It makes a really interesting looking effect, but it is rather distracting, and you don’t want this to detract from your main gameplay.

Finally, you may want to look into lerp and slerp. These can make animation easier, but personally, I find it nicer to stick to the formula in the second section.


Enjoy!

Originally posted on my dev blog.
@Vorpal_Games


Related Jobs

Raven Software / Activision
Raven Software / Activision — Madison, Wisconsin, United States
[07.24.14]

Senior UI Engineer
Disney Consumer Products
Disney Consumer Products — Glendale, California, United States
[07.23.14]

Contract Game Programmer
Zindagi Games
Zindagi Games — Camarillo, California, United States
[07.23.14]

Software Engineer
Telltale Games
Telltale Games — San Rafael, California, United States
[07.23.14]

Core Technology – Client Network Engineer






Comments


Jeff Postma
profile image
Thanks so much for this! Been diving into Unity and still trying to figure this kind of stuff out.

Alex Rose
profile image
No problem, I've been there!

Leandro Vian
profile image
Nice one Alex, like Jeff, i'm trying some 2D prototypes on Unity and your tips will be of great help.

Alex Rose
profile image
No problem! Glad I could help.

Brian Wolf
profile image
Wow, I wish I had gotten these lessons in my high-school math classes. Brilliant stuff.

Alex Rose
profile image
Truth! I've become more comfortable with trigonometry from a year of messing around in Unity than I did in 5 years of higher level maths education. It's amazing what you learn when you're motivating yourself.

Arturo Nereu
profile image
Amazing work Alex, also thanks for sharing this!

My recommendation, also take a look at iTween: http://itween.pixelplacement.com/documentation.php Tons of pre-coded functions for interpolation.

Best.

Igor Hatakeyama
profile image
Thanks for that man!
The code you posted is C#? I know basic C# programming on Unity. But it was great to see some info on 2D development with Unity. Seems that all we usually see is 3D tutorials!
Anyway, good job on the blog post!

Alex Rose
profile image
Yep, I always use C#, gives me a lot more control over what I want to do. A bit more anal to use at the very beginning, but well worth learning over javascript. And yeah, I can't model 3D so I use Unity primarily for 2D. I'd happily do more tutorials on it, especially if there's anything specific you guys want to see.

nicholas ralabate
profile image
This article is great, much more general purpose than just Unity2D... reminds me of the "JUICE YOUR GAME" talks from GDC. In terms of color cycling, do you know of any resources for finding nicely repeating color shifts? It seems like the 80s Midway guys and all the demoscene had this down pat but it's pretty hard to find a good tutorial.

nicholas ralabate
profile image
Also, I think there's an extraneous SINE in the middle of your rotation code for a dancing boombox. It looks pretty awesome actually, but doesn't match up with the screenie!

Alex Rose
profile image
That's just poor GIF execution, the exact code is:

Radio.transform.rotation = Quaternion.EulerAngles(Mathf.PI / 2 - 0.3f * Mathf.Sin(frequency *(2 * Mathf.PI) *(Time.time - speakertimer)), Mathf.PI / 2, -Mathf.PI / 2);
Radio.transform.position = new Vector3(0, -1f + 1.5f * Mathf.Pow(Mathf.Cos((2 * Mathf.PI* frequency) * (Time.time - speakertimer)), 2), 0);

The eyes also pop up using IEnumerators.


IEnumerator Radioplay()
{
Radio.renderer.material.mainTexture = Radio2;
yield return new WaitForSeconds(0.2f);
Radio.renderer.material.mainTexture = Radio1;
}

And then a longer IEnumerator that launches that one (because its eyes don't change on the eighth beat, and it depends on the bpm of the song, which depends on the difficulty).

Alex Rose
profile image
Unfortunately I don't know any resources; if I don't know how to do something I start coding until I do.

But, as I said, in Rude Bear Rising what I used was a Color[] array with maybe 10 colours in, used the colour fading script and set it so it'd fade towards Color[currentcolour] as described above.

Once it gets there, increment currentcolour (and set to 0 if it was greater than the array length), turn off a bool that allows it to colour fade, wait X seconds, turn the bool back on, and it'd then fade to the next colour in the array.

And if you just want to make the colours shift constantly like the wall in the background up above, just stick trig functions on your R G B values and toy with the parameters until you find one you like.

nicholas ralabate
profile image
Got it, thanks! I didn't even notice the eyes bugging out until your comment, nice touch.


none
 
Comment: