Customizable Navigation Bar

Sunday, April 3, 2011

Building A Game In Unity Part 5:Creating Enemies and What the Animation Editor Is Capable Of



Alright so this is the 5th part of the building a game in Unity series. These next few posts will be lacking video to go along with them for a little while, only because there is too much background noise and distractions going on.

This tutorial will be going through how to make a simple Enemy class for your top down shooter, and how to use the animation editor.

So first step is to create a new C# script and name it Enemy. Once that is completed add the following code to it:

using UnityEngine;
using System.Collections;

/*We want to extend BaseEntity because it already contains a lot of the functionality that we need*/

public class Enemy : BaseEntity
{
        /*Next we will set it's life span to 15 so that the enemy will automatically die after 15 seconds. This is so that we don't have random enemies off screen and not doing anything other than taking up memory*/

         protected float mLifeSpan = 15.0f;


        /*We also want each enemy to have a score value, because high scores are awesome*/

         protected int mScoreValue;


        /*And we should have a reference to the player. Keep it static to save memory and make it easier to change for each enemy*/

         static private Player mainPlayer;


        /*Something I did just for this case.  I made a public enum for the enemy type instead of creating a new class for them. My enemies didn't do anything different, they just had different stats and meshes*/
       
        public enum EnemyType{Green, Purple, Red};


        /*[SerializeField] will make this private variable show up in the editor so that you can change its properties without making the variable public. Useful for when you don't want to accidentally change something in code, but want to be able to change it in editor*/

         [SerializeField]
         private EnemyType enemyType = EnemyType.Purple;

        /*The next variable we create is a boolean variable. Now what we do with this boolean variable is check to see if the enemy should be destroyed while off screen. Since the enemies spawn off screen, and we want to destroy them as soon as they are finished their animation and  off the screen, we need this bool value to tell us when it is time*/

         private bool bDestroyIfOffScreen = false;


        /*As soon as this object spawns we want to make sure that our static reference to the main player has a value in it*/


         void Awake()

         {
                  if(mainPlayer == null)
                 {

                          if((mainPlayer = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>()) == null)
                          {
                                  if((mainPlayer = GameObject.FindObjectOfType(typeof(Player)) as Player) == null)
                                  {
                                          Debug.LogError("Cannot locate main player");
                                  }
                          }
                  }
         }

         void Start ()
         {


                /*After we have the main player's value set we want to initialize the variables depending on the enemy type we have*/

                 initEnemy(enemyType);


                /* We then set the score value using some random formula, or you can just use the enemy's health if you want to */

                  mScoreValue = (int)(mMaxHealth * 2.0f) * Application.loadedLevel;

                /* After we have everything initialized, we start a coroutine. This coroutine will check to see if the enemy should be destroyed when it goes off screen. */

                 StartCoroutine(autoKill());

         }


        /* This is our Coroutine to check if the enemy should be destroyed off screen, because we don't want it to get destroyed before we even see it */

         protected IEnumerator autoKill()
         {

                  //wait for a bit so that we know that the animation is set. We want this function to happen after the
                  //animation has been set by the spawnNode
                  yield return new WaitForSeconds(mLifeSpan);

                  bDestroyIfOffScreen = true;
         }


        /* Our update function checks to see if we should destroy the object. The destroy has to be called in the update function instead of the coroutine. You cannot destroy the object calling the coroutine from within that coroutine, because it just simply won't do anything */


         void Update()
         {

                  if(bDestroyIfOffScreen)
                 {


                        /* renderer.isVisible checks to see if the camera can see the object. So if the camera can't see it, it's offscreen and we can destroy it. Note: may work differently in editor than built. Editor cameras are taken into account for this value */

                          if(!renderer.isVisible)
                          {
                                  Destroy(gameObject);
                          }
                 }

         }

        /*We then override the die() function from BaseEntity to add score to the main player.*/


         protected override void die ()
         {

                 mainPlayer.killedEnemy(mScoreValue);

                  base.die ();
         }

        /*Our initEnemy function just puts the enemy type into a switch statement and determines the amount of health that the enemy should have*/

         public void initEnemy(EnemyType newEnemyType)
         {
                  switch(newEnemyType)
                  {
                  case EnemyType.Purple:
                          mHealth = 15.0f;
                          mMaxHealth = 15.0f;
                          break;
                 case EnemyType.Green:

                          mHealth = 20.0f;
                          mMaxHealth = 20.0f;
                         break;

                 case EnemyType.Red:

                          mHealth = 10.0f;
                          mMaxHealth = 10.0f;
                         break;

                 }

         }
}


That's it for the Enemy class. This class is good for three different types of enemies if you do it this way.

Now for the animation editor.

In case you didn't know where it was located or what it looked like, go to Window >> Animation or press Ctrl + 6 and a window will pop up that looks like this:

Now the first thing you will see is the top bar, with a record button, a play button and a few others:

In order to do any sort of animation you must hit record, otherwise you are just moving stuff around in the world.

The play button can be pressed to test out your animation to see how it looks, and to see if you need to tweak a few key frames here and there.

The next two buttons, the ones that look like |< or >| are used to jump from key frame to key frame.

The number next to it tells you what frame you are currently on. You can enter a number to jump to that frame if you want to also.

The next button that looks like a diamond with a + next to it is used to add a new key frame. Key frames are the points in your animation that you put in manually. The animation curves take care of most of the rest of the work for you.

The button to the right of it is the add event button. What you can do with this animation editor is call certain functions from within an animation with these events. Very handy if your character is supposed to do something in code halfway through an animation, or you have some scripted events that need to be called part way through a cutscene.

The next large chunk is the timeline, and it just shows you what time the frames are on in your animation.

The bar on your left shows you which object you have selected. This is where you can input coordinates manually for your character's animation, or see where your character is going to be travelling in local space. You can also enable and disable individual components within the animation editor, such as a rigidbody or GUI layers. 


The main curve editor looks like the picture below


In this curve editor you can insert new key frames, drag the curves around and completely change the way an animation looks with just a few clicks. You also have complete control over the tangents of the curves, so you can change the acceleration of an object extremely easy. Right clicking in the curve editor brings up a menu to give you control over this.