Customizable Navigation Bar

Saturday, March 5, 2011

Building A Game In Unity Part 3: Weapons and Gizmos in Unity

This week's tutorial covers how to create a weapon and using something called gizmos in Unity. We also set up some base scripts for later tutorials. There is a video that goes along with this tutorial posted here:

Also, follow me on twitter at http://twitter.com/#!/purdyjo!



The first script that we are going to make is called BaseEntity. This script will be the building blocks for our enemies, the main character and any other characters that you wish to add. It contains the properties that all of the objects will share.


using UnityEngine;
using System.Collections;

public class BaseEntity : MonoBehaviour
{

/*Here we make protected variables for health and make them read only, so that this is the only class that can change their values. This just keeps us from accidentally making a change somewhere and killing the player with an unrelated script.*/

        protected float mHealth = 100.0f;
        public float currentHealth{get{return mHealth;}}

        protected float mMaxHealth = 100.0f;
        public float maxHealth{get{return mMaxHealth;}}

/*Make a generic take damage function for when this object gets attacked etc*/

        public virtual void takeDamage(float amount)
        {
                mHealth -= amount;

                if(mHealth <= 0)
                {
                        die();
                }
        }

/*We should also make a healing function for potions or powerups etc*/

        public virtual void healDamage(float amount)
        {
                mHealth = Mathf.Min(mHealth + amount, mMaxHealth);
        }

/*We also need some sort of die function, so that we can have this object do something before it is destroyed*/

        protected virtual void die()
        {
                Destroy(gameObject);
        }
}


Our next script is a base class for all projectiles, because we need something for the weapon to shoot. This class also checks to see if what it collides with is friendly or not.


using UnityEngine;
using System.Collections;

/*We are also going to include System since we need to use Types for this class*/

using System;


/*This also requires a sphere collider to work, so we force unity to add one when this script is attached*/

[RequireComponent(typeof(SphereCollider))]
public class Projectile : MonoBehaviour
 {

/*Our generic variables that all projectiles will be using*/

        protected float mSpeed = 20.0f;
        public float speed{get{return mSpeed;}}
        protected float mMaxSpeed = 25.0f;
        public float maxSpeed{get{return mMaxSpeed;}}
        protected Vector3 mMovementDirection;
        public Vector3 movementDirection{get{return mMovementDirection;}}
        protected float mAcceleration = 1.0f;
        public float acceleration{get{return mAcceleration;}}
        protected float mDamage = 10.0f;
        public float damage{get{return mDamage;}}
        protected float mLifeSpan = 10.0f;
        public float lifeSpan{get{return mLifeSpan;}}
/*The instigator type is the type of the object that fired this projectile. Useful for checking if the player shot an enemy or not*/

        protected Type mInstigatorType;
        public Type instigatorType{get{return mInstigatorType;}}
        void Start () 
        {
                mMovementDirection = transform.forward;
                gameObject.GetComponent<SphereCollider>().isTrigger = true;
                StartCoroutine(kill());
        }
        void Update () 
        {

/*In the update function we just want our projectiles to move forward. Nothing fancy here*/

                transform.position += (mMovementDirection * mSpeed * Time.deltaTime);
                if(mSpeed < mMaxSpeed)
                {
                        mSpeed += mAcceleration * Time.deltaTime;
                }
/*We should also check to see if the object is on the screen or not. There is no reason to have this object still active if it's not on the screen*/

                if(!renderer.isVisible)
                {
                        Destroy(gameObject);
                }
        }
/*We should also kill the projectile after the lifespan, so that it isn't constantly taking up memory*/

        protected IEnumerator kill()
        {
                yield return new WaitForSeconds(mLifeSpan);
                Destroy(gameObject);
        }
/*This is to set the instigator of this projectile. The weapon firing this calls this function*/

        public void setInstigator(BaseEntity instigator)
        {
                mInstigatorType = instigator.GetType();
        }
/*Here we check to see if a base entity was hit, and if it was we check the type against the instigator type and do damage if applicable*/

        void OnTriggerEnter(Collider other)
        {
                BaseEntity otherObject = other.gameObject.GetComponent<BaseEntity>();
                if(otherObject != null)
                {
                        if(otherObject.GetType() != mInstigatorType)
                        {
                                otherObject.takeDamage(mDamage);
                                Destroy(gameObject);
                        }
                }
        }
}


So the projectile class is fairly simple, and it gets the job done. Now that we have a base entity class and a projectile class, lets get to the weapon class. Keep in mind that a weapon is just a point in world space that projectiles spawn from


using UnityEngine;
using System.Collections;

public class Weapon : MonoBehaviour 
{

/*here we have a public Projectile so that we can choose the projectile type that will be fired in the editor*/

        public Projectile bulletType;
        public float fireRate = 1.0f;
       
/*here we have a bool to check if the weapon's rotation should remain constant. This is to keep the projectiles moving in the same direction no matter which way the enemy or player rotates during animations*/

        public bool lockRotation;
        private Quaternion mStartingRotation;

/*then we create a variable to hold the owner of the weapon*/

        private BaseEntity mOwner;
/*Now since the weapon is an empty object, we need some easy way to see the weapon in the editor. Gizmos solve this problem. In the OnDrawGizmos function we can tell unity to draw spheres, lines, icons etc that will only appear in the editor.*/

        void OnDrawGizmos()
        {

/*Do show where the weapon is we draw a sphere at it's position and a line showing you what direction it is facing*/

                Gizmos.DrawSphere(transform.position, 0.1f);
                Gizmos.DrawLine(transform.position, transform.position + transform.forward * 0.5f);
        }
        void Start () 
        {

/*In start we more or less just check if bulletType has been set to something. If not we throw an error and break*/

                if(bulletType == null)
                {
                        Debug.LogError("bulletType is set to null");
                        Debug.Break();
                }
                mStartingRotation = transform.rotation;
                enableWeapon();
        }
/*Our fire weapon function is a coroutine that gets called once every few seconds depending on the fire rate chosen*/

        IEnumerator fireWeapon()
        {
                mOwner = transform.parent.gameObject.GetComponent<BaseEntity>();

                while(true)
                {

/*We also only fire the weapon if the object is on the screen, theres no point on firing if the projectiles get destroyed off screen*/

                        if(mOwner.renderer.isVisible)
                        {
                                if(lockRotation)
                                {
                                        transform.rotation = mStartingRotation;
                                }
                                Projectile newBullet = (Projectile)GameObject.Instantiate(bulletType, transform.position, transform.rotation);
                                newBullet.setInstigator(mOwner);
                                yield return new WaitForSeconds(fireRate);
                        }
                        else
                        {

/*if the weapon is off screen we just wait until the end of the frame and check again*/

                                yield return new WaitForEndOfFrame();
                        }
                }
        }
/*Now that we've completed this much we can create public functions for enabling and disabling the weapon*/

        public void enableWeapon()
        {
                StartCoroutine(fireWeapon());
                this.enabled = true;
        }
        public void disableWeapon()
        {
                StopCoroutine("fireWeapon");
                this.enabled = false;
        }
}

Alright, so now we have a working weapon and some base scripts for the player, enemies and projectiles, and this tutorial is complete. The next part in this series will be covering the particle effect editor and we will be filling in more projectile classes. So have a good one guys and keep coding!