Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

[Solved] How to have an abtract class work with interface? Null reference

Discussion in 'Scripting' started by Pixitales, Jul 1, 2019.

  1. Pixitales

    Pixitales

    Joined:
    Oct 24, 2018
    Posts:
    227
    Sorry, I am still a beginner. Codes are a mess and I shortened it. I am currently working on object pooling for my enemies. (Basically reusing gameObjects instead of destroying them). When an enemy dies, it SetActive(false). When I want to reuse it and set active, I want to restore their health back to full. I am using an interface because I want to do this for my allies also. But lets not worry about the allies part yet.

    My Enemy script inherits from my Character abstract class script, NPC script (no idea lol), and IPooledObject interface

    IPooledObject Interface:
    Code (CSharp):
    1. public interface IPooledObject
    2. {
    3.     void OnObjectSpawn();
    4. }
    5.  
    Enemy Script:
    Code (CSharp):
    1. public class Enemy : NPC, IPooledObject
    2. {
    3. //When enemy is dead
    4. private void HideGameObject()
    5.     {
    6.         gameObject.SetActive(false);
    7.     }
    8.  
    9. //Reset Enemy
    10.     public void OnObjectSpawn()
    11.     {
    12.         //Inherits from Character script
    13.         MySprite.material.color = Color.white;
    14.         this.MyHealth.MyCurrentValue = this.MyHealth.MyMaxValue;
    15.         MyBoxCollider.enabled = true;
    16.     }

    Object Pooler:
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class ObjectPooler : MonoBehaviour
    5. {
    6.     [System.Serializable]
    7.     public class Pool
    8.     {
    9.         public string tag;
    10.         public GameObject prefab;
    11.         public int size;
    12.     }
    13.  
    14.     #region Singleton
    15.     public static ObjectPooler Instance;
    16.  
    17.     private void Awake()
    18.     {
    19.         Instance = this;
    20.     }
    21.     #endregion
    22.  
    23.     public List<Pool> pools;
    24.     public Dictionary<string, Queue<GameObject>> poolDictionary;
    25.  
    26.     void Start()
    27.     {
    28.         poolDictionary = new Dictionary<string, Queue<GameObject>>();
    29.  
    30.         foreach (Pool pool in pools)
    31.         {
    32.             Queue<GameObject> objectPool = new Queue<GameObject>();
    33.  
    34.             for (int i = 0; i < pool.size; i++)
    35.             {
    36.                 GameObject obj = Instantiate(pool.prefab, transform);
    37.                 //transform.parent = obj.transform;
    38.                 obj.SetActive(false);
    39.                 objectPool.Enqueue(obj);
    40.             }
    41.  
    42.             poolDictionary.Add(pool.tag, objectPool);
    43.         }
    44.     }
    45.  
    46.     public GameObject SpawnFromPool (string tag, Vector2 position, Quaternion rotation)
    47.     {
    48.         if (!poolDictionary.ContainsKey(tag))
    49.         {
    50.             Debug.LogWarning("Pool with tag" + tag + " doesn't exist.");
    51.             return null;
    52.         }
    53.  
    54.         GameObject objectToSpawn = poolDictionary[tag].Dequeue();
    55.  
    56.         objectToSpawn.SetActive(true);
    57.         objectToSpawn.transform.position = position;
    58.         objectToSpawn.transform.rotation = rotation;
    59.  
    60.         if (objectToSpawn != null)
    61.         {
    62.             objectToSpawn.GetComponent<IPooledObject>().OnObjectSpawn();
    63.         }
    64.  
    65.         poolDictionary[tag].Enqueue(objectToSpawn);
    66.  
    67.         return objectToSpawn;
    68.     }
    69.  
    70. }

    Enemy wave Spawner Script:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class EnemyWaveSpawner : MonoBehaviour
    7. {
    8.     public enum SpawnState { Spawning, Waiting, Counting };
    9.  
    10.     [System.Serializable]
    11.     public class Wave
    12.     {
    13.         public string name;
    14.         public Transform enemy;
    15.         public int count;
    16.         public float rate;
    17.     }
    18.  
    19.     public Wave[] waves;
    20.     private int nextWave = 0;
    21.  
    22.     public Transform[] spawnPoints;
    23.  
    24.     public float timeBetweenWaves = 5f;
    25.     private float waveCountdown;
    26.  
    27.     private float searchCountdown = 1f;
    28.  
    29.     public SpawnState state = SpawnState.Counting;
    30.  
    31.     public bool Spawn = false;
    32.     public CharacterSelection characterSelected;
    33.  
    34.     public int totalEnemies;
    35.     public int enemiesKill;
    36.  
    37.     public Text currentWaveText;
    38.  
    39.     ObjectPooler objectPooler;
    40.  
    41.     private void Start()
    42.     {
    43.         //if (spawnPoints.Length == 0)
    44.         //{
    45.         //    Debug.LogError("No spawn points referenced.");
    46.         //}
    47.  
    48.         waveCountdown = timeBetweenWaves;
    49.  
    50.         objectPooler = ObjectPooler.Instance;
    51.     }
    52.  
    53.     void Update()
    54.     {
    55.         if (GameManager.MyInstance.startPlaying && !Spawn)
    56.         {
    57.             Spawn = true;
    58.         }
    59.  
    60.         if (Spawn)
    61.         {
    62.             if (state == SpawnState.Waiting)
    63.             {
    64.                 //Check if eneimes are still alive
    65.                 if (!EnemyIsAlive())
    66.                 {
    67.                     //Begin next new round
    68.                     WaveCompleted();
    69.  
    70.                     return;
    71.                 }
    72.                 else
    73.                 {
    74.                     return;
    75.                 }
    76.             }
    77.  
    78.             if (waveCountdown <= 0)
    79.             {
    80.                 if (state != SpawnState.Spawning)
    81.                 {
    82.                     //Start spawning wave
    83.                     StartCoroutine(SpawnWave(waves[nextWave]));
    84.                 }
    85.             }
    86.             else
    87.             {
    88.                 waveCountdown -= Time.deltaTime;
    89.             }
    90.         }
    91.     }
    92.  
    93.     void WaveCompleted()
    94.     {
    95.         //Debug.Log("Wave Completed");
    96.  
    97.         state = SpawnState.Counting;
    98.         waveCountdown = timeBetweenWaves;
    99.  
    100.         if(nextWave + 1 > waves.Length - 1) //If reach end of wave, (start new scene or add stats)
    101.         {
    102.             nextWave = 5;
    103.             //Debug.Log("All Wave Completed");
    104.         }
    105.         else
    106.         {
    107.             nextWave++;
    108.             currentWaveText.gameObject.SetActive(true);
    109.             currentWaveText.text = "Wave " + nextWave;
    110.             Invoke("DisableText", 3);
    111.         }
    112.     }
    113.  
    114.     bool EnemyIsAlive()
    115.     {
    116.         searchCountdown -= Time.deltaTime;
    117.  
    118.         if (searchCountdown <= 0f) //search every seconds
    119.         {
    120.             searchCountdown = 1f; //Reset search countdown timer
    121.  
    122.             if (GameObject.FindGameObjectWithTag("Enemy") == null)
    123.             {
    124.                 return false;
    125.             }
    126.         }
    127.         return true; //enemy still alive
    128.     }
    129.  
    130.     IEnumerator SpawnWave (Wave _wave)
    131.     {
    132.         //Debug.Log("Spawning Wave: " + _wave.name);
    133.         state = SpawnState.Spawning;
    134.  
    135.         //spawn
    136.         for (int i = 0; i < _wave.count; i++)
    137.         {
    138.             SpawnEnemy(_wave.enemy);
    139.             yield return new WaitForSeconds(1f/_wave.rate);
    140.         }
    141.  
    142.         state = SpawnState.Waiting;
    143.  
    144.         yield break; //End
    145.     }
    146.  
    147.     void SpawnEnemy(Transform _enemy)
    148.     {
    149.         Vector2 Position = new Vector2(Random.Range(-15f, 12f), Random.Range(5f, -6f));
    150.  
    151.         //Spawn enemy
    152.         objectPooler.SpawnFromPool("Enemy", Position, Quaternion.identity);
    153.  
    154.         totalEnemies = FindObjectsOfType<Enemy>().Length;
    155.  
    156.         ScoreManager.instance.AddTotalEnemies(totalEnemies);
    157.     }
    158.  
    159.     private void DisableText()
    160.     {
    161.         currentWaveText.gameObject.SetActive(false);
    162.     }
    163. }

    I get a null reference exception at Character/Enemy.cs:330. This part here on my enemy script:
    Code (CSharp):
    1.     public void OnObjectSpawn()
    2.     {
    3.         MySprite.material.color = Color.white;
    4.         this.MyHealth.MyCurrentValue = this.MyHealth.MyMaxValue;
    5.         MyBoxCollider.enabled = true;
    6.     }

    Abstract class doesn't work with interfaces? Any way to get rid of the null reference? Apparently only health resets because it inherits from stats script (stats is attached to enemy inspector)..
     
    Last edited: Jul 1, 2019
  2. Pixitales

    Pixitales

    Joined:
    Oct 24, 2018
    Posts:
    227
    Nevermind, I figured it out. Actually there was nothing wrong with what I posted. Forgot to call character void start on my enemy script. I am so dumb...