Skip to main content

Creating 2D Animation For A Side Scroller In Unity3D

Update 3-13-14
Unity kinda released some really nice 2D tools, I really don't know how relevant or helpful this is now.

I've been working on a 2D side scroller. Being my cheap self I'm not going to bother buying some helpful tool that would probably speed up everything a substantial amount, and created my own method of 2D animation for things in Unity3D. This code does not work with a sprite sheet, but I hope it makes it easy enough for you to incorporate one if you wanted to.

Warnings:
 The animation is displayed by updating the meshes material's texture each frame (this can mess up at high frame speeds).
This code is assuming the 2D sprites are moving along the X and Y axis.
When changing directions, it is scaling the transform negatively across it's x axis.
Its assuming default direction facing is right.


 using UnityEngine;  
 using System.Collections;  
   
 public enum Directions{left,right}  
 public class FlatCharacterAnimation : MonoBehaviour {  
        
      public FlatAnimation[] animations;  
        
      public string defaultAnimation;  
        
        
      FlatAnimation currentAnimationPlaying;  
        
      string animationCurrentlyPlaying = "";  
      Texture2D[] framesToPlay;  
      float frameSpeed;  
      int currentFrame = 0;  
      Directions directionCharactersFacing = Directions.right;  
      bool animationIsPlaying;  
        
      #region FlatAnimationClass  
      [System.Serializable]  
      public class FlatAnimation{  
           public string name ="";       
           public Texture2D[] frames;  
           public float frameSpeed;  
             
           public Texture2D[] GetFrames(){  
                return frames;  
           }  
        
             
      }  
      #endregion  
        
      public Directions GetDirectionFacing(){  
           return directionCharactersFacing;       
      }  
        
        
      public void Play(string animation){  
           if(animation != animationCurrentlyPlaying){  
                ChangeAnimation(GetAnimation(animation));  
                animationIsPlaying = true;  
           }  
      }  
        
      public void Play(string animation, Directions direction){  
           if(animation != animationCurrentlyPlaying){  
                ChangeAnimation(GetAnimation(animation));  
                animationIsPlaying = true;  
           }  
             
           if(direction != directionCharactersFacing)               //make sure players facing right direction, if not, changes direction  
                ChangeDirectionPlayersFacing(direction);  
      }  
        
      FlatAnimation GetAnimation(string animationName){  
           for(int i = 0; i < animations.Length; i++){  
                  
                if(animations[i].name == animationName){  
                     return animations[i];  
                }   
           }  
           Debug.LogWarning("The animation you are trying to play does not exist! Try checking spelling and capitalization");  
   
           return null;  
      }  
        
   
        
      void ChangeAnimation(FlatAnimation newAnimation){  
           animationCurrentlyPlaying = newAnimation.name;  
           framesToPlay = newAnimation.frames;  
           frameSpeed = newAnimation.frameSpeed;  
           currentFrame = 0;  
      }  
        
      void ChangeDirectionPlayersFacing(Directions direction){  
           Vector3 scale;  
           directionCharactersFacing = direction;  
           switch(direction){  
                case Directions.left:  
                     //Debug.Log("Left Direction");  
                     scale = gameObject.transform.localScale;  
                     scale.x *= -1;  
                     gameObject.transform.localScale = scale;  
                break;  
                  
                case Directions.right:  
                     //Debug.Log("Left Direction");  
                     scale = gameObject.transform.localScale;  
                     scale.x *= -1;  
                     gameObject.transform.localScale = scale;  
                break;  
                  
           }  
      }  
        
      float nextFrameTime;  
      void UpdateAnimation(){  
             
           if(nextFrameTime < Time.time &&framesToPlay != null){  
                //Debug.Log ("current frame: " + currentFrame + "currentAnimationFrames Legth: "+currentAnimationFrames.Length);  
                nextFrameTime = frameSpeed+Time.time;  
                if(currentFrame == framesToPlay.Length-1){  
                     currentFrame = 0;  
                } else if(currentFrame < framesToPlay.Length){  
                     currentFrame += 1;  
                }  
                gameObject.GetComponent<MeshRenderer>().material.mainTexture = framesToPlay[currentFrame];  
                  
           }  
      }  
        
      // Use this for initialization  
      void Start () {  
        
      }  
        
      // Update is called once per frame  
      void Update () {  
           if (animationIsPlaying){  
                UpdateAnimation();       
           }  
      }  
 }  


Setting this up:

Its more than just dragging and dropping the script, but it doesn't take much time. To begin, in case any of you still have graphics and code on the same Game Object, stop it, please. To create your graphics object for this, you can do it three ways.
  1. Create a Primitive Cube. Use this method if your shader for the material is going to ignore light. Scale one of the axis to 0. Reasons for creating a cube over a plane is that a cube has fewer faces than the default plane for some reason in unity. The shader you would use for this on the material would be Unlit>Transparent.
  2. Create a Primitive  Plane. Use this method if your material will not ignore light. This method will have more faces, but in most cases it will not be considered to serious. Make sure the y axis stays 1. The shader you would use on the material for this option would be Transparent/Cutout/YourOption
  3. Create Your Own Object To Render It. Takes time, more faces than a cube usually, but can be less faces that a plane.
If your developing for a mobile device, consider using option 1. Not so much for the poly count, but for the shader. Unlit shaders take less power to process.

In my example I'm going to be using option 2, and these are the steps.
  1. Create an Empty Game Object (Cntrl+Shift+N), and rename Enemy
  2. Create a Primitive Plane and parent it to Enemy. Rename Plane to graphics.
  3. Set rotations on plane to (90, 180, 0),  
  4. Add Flat Character Animation (Script Above) to graphics object.
  5. Re-size Animations Size to however animations you need.
  6. For each animation, set the name(IMPORTANT), frames, and Frame Speed.
Frame speed works in reverse to frame rate, don't get these confused. If you want the frame rate to be 30 seconds, set the frame speed to .033 (1/30).

Frames are the individual pictures you want to play in sequence for the animation.

To play the animation, grab the instance of the flat character animation, and call either Play(string nameOfAnimation); or Player(string nameOfAnimation, Directions, directionToFace);

Example of the Inspector


Example of the Object in the scene


Check out the game this code was made for!
Treva's Adventure Web game

Comments

Popular posts from this blog

How To Import and Use Fonts

-Made by Chrome Fx Films

To use different fonts in your game, your going to have to acquire some fonts. You can go somewhere like 1001 free fonts and download ones you like and want to use.

When you download the file, you'll need to extract the file if its in  a .zip

The text file should have the extension .ttf. If you text files have a FFIL extension, simply rename the font file (yourname.ttf).
Drag the text file into unity to import. By default the Character variable should be labeled Dynamic.

The Dynamic setting means  that Unity won't pre-generate the texture, so if you look at your material containing your font, It probably will be blank. (read more here)
Now your over all objective should be getting your font to look like this (unless you prefer dynamic):
Where the characters of the font are not jumbled up and visible.
Now usually all you have to do to achieve this is change the Character variable from Dynamic to Unicode.
and that should work.
Now what some people over…

Handling Music and Sound Effects In Your Games

Initiative  While developing Treva's Adventure I had to figure out a way to handle multiple music tracks and sound effects in a clean manner or suffer horribly.  What was going to help me achieve a simple solution was taking all the different sounds and centralizing them in a single class in order to black box them.  Any other code trying to play a sound wouldn't even know the sound file's name.  All code trying to play a music track would reference a enum that defines all the track names.
Defining The Class Creating The Enum When I first started defining types in my enumeration,  I was naming the types to be exactly like the file name.  For a scary sound effect I had found a file named "ghost breath".  So around my code would be scattered lines like SoundManager.Play(SoundEffectType.GhostBreath);  This was fine until I found a sound that better fit the situation it was being used in,  and decided to use "ghost breath" for a different situation like a …

Don't Destroy On Load..

So if you want to keep an object or script that keeps up variables (or for any other reason) when you go from scene to scene, you need to attach a don't destroy on load static function, which goes something like this:

function Awake () {
    DontDestroyOnLoad (transform.gameObject);
}

The Awake function is call only once, when all the objects in the scene have been created. Read more about it here.

DontDestroyOnLoad has what ever is in the ( ) to not be destroyed when creating a new scene.

(transform.gameObject) is what will not be destroyed when the new scene is loaded, in this case, it will be the game object and all it's children the script is attached to.