One thing I always considered good practice is “If you know what you need, and ‘the wheel’ is over-bloated, it’s OK to reinvent.” I looked at animation systems involving many awesome features, but I wanted something really, really simple. Named after my room mate, here is the Super Simple Animation System (SSAS).
So, let me start by saying this isn’t a complete system. This is extremely basic, and has much room for improvement. Anything you want to add or change, feel free. But if you need something to animate, and animate now, go for it.
Features:
- Pro: Pretty fast!
- Pro: Easy to implement and modify.
- Pro: Supports switching animations at runtime.
- Pro: Supports different sized textures.
- Con: A bit memory heavy.
- Con: Replaces Textures in the materials.
We get it, get on with it!
Geez, bossy, bossy…
So, for the noobies out there, you’re probably wondering “How do animations work?” Well, I’ll tell you.
So you know movies? Like, watch it in a theater? Moving pictures? So one single picture in the movie is called a frame. To make the movie, the frames flip by really fast. This is called Animation. That’s why you’re here. Good.
So, here is my full SpriteAnimation
class. Just, gonna toss it at you, and we’ll break it down. Real simple-like.
SpriteAnimation
using UnityEngine; using System.Collections; public class SpriteAnimation : MonoBehaviour { [System.Serializable] public class Animation { public string Name; public Texture2D[] Frames; public int currentFrame = 0; public float frameTime = 0.5f; private float curTime = 0.0f; public void Reset() { curTime = 0.0f; currentFrame = 0; } public Texture2D UpdateAnimation() { if(curTime >= frameTime) { ++currentFrame; if (currentFrame >= Frames.Length) currentFrame = 0; curTime = 0.0f; } else { curTime += Time.deltaTime; } return Frames[currentFrame]; } public Texture2D GetCurrentFrame() { return Frames[currentFrame]; } } public Animation[] Animations; private Animation currentAnimation; public string DefaultAnimationName; private int DefaultAnimationIdx = 0; public Texture2D currentFrame; void Awake() { if(DefaultAnimationName.Length == 0) { DefaultAnimationName = Animations[0].Name; } else { DefaultAnimationIdx = GetAnimationByName(DefaultAnimationName); } currentFrame = Animations[DefaultAnimationIdx].GetCurrentFrame(); } // Use this for initialization void Start () { currentAnimation = Animations[DefaultAnimationIdx]; } // Update is called once per frame void Update () { currentFrame = currentAnimation.UpdateAnimation(); } public int GetAnimationByName(string Name) { for (int i = 0; i < Animations.Length; ++i) { if(Animations[i].Name == Name) { return i; } } return 0; } public string GetCurrentName() { return currentAnimation.Name; } public void SetAnimation(string Name) { if (currentAnimation.Name == Name) return; foreach (Animation a in Animations) { if(a.Name == Name) { currentAnimation = a; currentAnimation.Reset(); return; } } } }
OK, take a few breaths, before you blow your mind. As you see, I have a nested class, Animation
, inside my SpriteAnimation
class. Doing this allows me to keep an array of Animations
in my SpriteAnimation
, and keep a multitude of variables for each individual object. And using the [System.Serializable]
attribute allows it to appear correctly in the Unity3D inspector.
So lets start with the Animation class:
public string Name; public Texture2D[] Frames; public int currentFrame = 0; public float frameTime = 0.5f; private float curTime = 0.0f;
Here are our variables in Animation.
Name
– Our most important variable here. This is how we identify and look up the animations. If you can’t get this system to work, come to me asking for help, and I find out it’s because you didn’t name your variable – I will find you.Frames
– Here are our frames. Every individual frame that makes up this animation goes in here*.currentFrame
– This is the iterator for our animation. This will increment after frameTime elapses.frameTime
– The duration of a single frame. After 0.5 seconds, it will iterate to the next frame (I just explained this – pay attention!)**.curTime
– How much time elapsed on the current frame.
* – This method switches out the texture in the material, which, in some cases, can create a massive drop in performance. You may want to switch to an array of Materials, if one was so inclined.
* – This is a slight disadvantage, because if you wanted a particular frame to last longer/shorter than frameTime, you’re SOL. You could take this class a bit forward and make a nested Frame class. Has a ton of advantages, but then you’ll need to drop an ‘S’ in our SSAS.
Now that we have our members, it’s time to get down to the methods:
Reset
– Resets the Animation back to its initial frame, and resets the curTime to 0.UpdateAnimation
– Adds deltaTime to curTime, and when curTime >= frameTime, it iterates the frame. Returns the current frame.GetCurrentFrame
– What do you think?
Next, we look at the SpriteAnimation
Class
Here we have our members:
public Animation[] Animations; private Animation currentAnimation; public string DefaultAnimationName; private int DefaultAnimationIdx = 0; public Texture2D currentFrame;
And now we break it down:
Animations
– an Array of all our animations for thisSpriteAnimation
. This could be “Jump,” and “Walk,” and “Fire” and so on and so forth…currentAnimation
– the Animation that’s currently… Animating… yeah…DefaultAnimationName
– This is the default animation to start on when our game starts.DefaultAnimationIdx
– It’s easier to search by index than by name.currentFrame
– The current frame to render.
And now that we have our members, lets take a look at all the methods:
Awake
– Runs when the object is first created in the scene. Sets up the default animation and default frame.Start
– Runs before the first update. Sets the current animation.Update
– Runs once every update frame in our game. Calls UpdateAnimation on the current animation and sets the current frame.GetAnimationByName
– Searches the list of animations and returns the index animation who’s Name is equal to the parameter (This is why Name is important).GetCurrentName
– Obvious, isn’t it? It gets the Name back from the current animation.SetAnimation
– Sets the current playing animation to the animation who’s Name is equal to the parameter. This is what we use to switch animations on the fly.
The SpriteAnimation
class does the meat of the work in terms of animation. What we need now is a way set the current frame to our object.
Enter SpriteManager
using UnityEngine; using System.Collections; public class SpriteManager : MonoBehaviour { SpriteAnimation animator = null; MeshRenderer mesh; // Use this for initialization void Start () { if (animator == null) { animator = GetComponent(); } mesh = GetComponent(); } // Update is called once per frame void Update () { mesh.material.mainTexture = animator.currentFrame; } public void ChangeAnimation(string Name) { animator.SetAnimation(Name); } public string GetCurrentAnimationName() { return animator.GetCurrentName(); } }
First thing right off the bat you should think “Hey, why do I need 2 components to do the same thing?” Short answer is shut up. Longer answer is by having the SpriteAnimation
class and a SpriteManager
class separate, this allows us to do interesting things. The SpriteManager
is what puts the frame of animation onto the object. We can have multiple SpriteManagers
using the same SpriteAnimation
and synchronize all their animations. The SpriteManager
is also the façade class that we will use to change animations from other objects.
Lets break it down. Here are the members:
animator
– This is theSpriteAnimation
class that is linked up to thisSpriteManager
.mesh
– This is the object we will be plastering the game object onto. We can also make another SpriteManager that deals with GUI textures, or whatever. Changes to this Animation System are Super Simple (See what I did there?).
And here are the simple methods:
Update
– Applies the current frame in the animation to the mesh.ChangeAnimation
– Call this function from other scripts to change the animation.
So, that’s pretty much it! The SpriteAnimation
class can go anywhere that updates, but the SpriteManager
has to go on something with a mesh.
Use these to your wee little hearts delight. Some form of credit would be appreciated.
Any questions or problems, just leave a comment below and I’ll do my best to help. Till next time, take care, programmers!
Related articles
- Wrap texture adressing within a sprite sheet or atlas (theinstructionlimit.com)