Appendix: Custom Spawning

In the 3rd workshop, we spawned two types of enemies but ended up using the same values for health, speed, and size for them. That greatly limited what could be done gameplay wise, in this appendix we will revisit the spawning code and take a deeper dive into what can be done.

Spawner.cs

using UnityEngine;
using System.Collections;

public class Spawner : MonoBehaviour
{
    // Local Variables
    [System.Serializable]
    public class EnemyType
    {
        public string m_strFriendlyName = "None";
        public string m_strEnemyType = "None";
        public int m_nEnemyHealth = 5;
        public float m_fEnemySpeed = 0.1f;
        public float m_fEnemyScale = 2.0f;
    }

    public float m_fSpawnRadius = 20.0f;
    public float m_fSpawnDelay = 3.0f;
    public EnemyType[] m_aEnemyTypes;
    float m_fAccumulatedSpawnTime = 0.0f;

    // Use this for initialization
    void Start ()
    {

    }

    // Update is called once per frame
    void Update ()
    {
        // Keep track of how much time has passed
        m_fAccumulatedSpawnTime += Time.deltaTime;
        while(m_fAccumulatedSpawnTime > m_fSpawnDelay)
        {
            // Decrement the time
            m_fAccumulatedSpawnTime -= m_fSpawnDelay;

            // Create a new enemy
            GameObject pNewObject = GameObject.CreatePrimitive(PrimitiveType.Cube);

            // Match the spawner's rotation and position
            pNewObject.transform.rotation = transform.rotation;
            pNewObject.transform.position = transform.position;
            // Rotate the new object randomly
            pNewObject.transform.RotateAround(Vector3.forward, Random.Range(0.0f, 360.0f));
            // Then push the object back the spawn offset distance
            pNewObject.transform.Translate(m_fSpawnRadius, 0.0f, 0.0f);

            // Set the behaviour on this new enemy
            int nEnemyType = Random.Range(0, m_aEnemyTypes.Length);
            Enemy pEnemy = (Enemy)pNewObject.AddComponent(m_aEnemyTypes[nEnemyType].m_strEnemyType);
            pEnemy.Init(m_aEnemyTypes[nEnemyType].m_nEnemyHealth, m_aEnemyTypes[nEnemyType].m_fEnemySpeed, m_aEnemyTypes[nEnemyType].m_fEnemyScale);
        }
    }
}

Two areas have changed here, looking at the top first, you will see that the variables that were shared by the enemies have been grouped together into a new class ‘EnemyType’. This is a subclass, that exists within the ‘Spawner’ class, it is not a MonoBehavior like all of the previous classes up until this point. It is being used basically to group a collection of data elements. Subclasses are often interchanged with the ‘struct‘ type depending on the situation and code language, however in our case here using subclasses are easier with the Unity object inspector and will be our default choice.

     // Local Variables
    [System.Serializable]
    public class EnemyType
    {
        public string m_strFriendlyName = "None";
        public string m_strEnemyType = "None";
        public int m_nEnemyHealth = 5;
        public float m_fEnemySpeed = 0.1f;
        public float m_fEnemyScale = 2.0f;
    }

    public float m_fSpawnRadius = 20.0f;
    public float m_fSpawnDelay = 3.0f;
    public EnemyType[] m_aEnemyTypes;
    float m_fAccumulatedSpawnTime = 0.0f;

This class is prefixed with the label ‘[System.Serializable]’, ‘Serialization‘ is a fancy way of saying the code can be saved. A new variable was added the ‘m_aEnemyTypes’ which is an array of enemies for the spawner to pick from.

             // Set the behaviour on this new enemy
            int nEnemyType = Random.Range(0, m_aEnemyTypes.Length);
            Enemy pEnemy = (Enemy)pNewObject.AddComponent(m_aEnemyTypes[nEnemyType].m_strEnemyType);
            pEnemy.Init(m_aEnemyTypes[nEnemyType].m_nEnemyHealth, m_aEnemyTypes[nEnemyType].m_fEnemySpeed, m_aEnemyTypes[nEnemyType].m_fEnemyScale);

Then at the bottom of the code a random element from the array is picked and all the data from that element is used to craft the enemy. Notice that the ‘Random.Range’ function works different with integer values than with floats, by being ‘exclusive’ with the max value which means it won’t be selected random. This is very handy with arrays because the length is always one higher than the indexing would allow.

Take a look at it in the editor to see the difference:

Super Space Pigeon is beyond compare

Here we have created our two enemy types from before, the Space Saw and the Space Pigeon, however a third type was setup ‘Super Space Pigeon’ that reuses the normal Space Pigeon behavior but has different configuration data. Give it a try right here.