Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Custom Inspector for Setting Up Waves

Discussion in 'Scripting' started by Cnc96, Jun 3, 2015.

  1. Cnc96

    Cnc96

    Joined:
    Dec 17, 2013
    Posts:
    57
    I have a wave manager script which obviously spawns various enemies in waves. At the moment I'm planning on setting up each wave (which enemies to spawn and how many to spawn) through code. Now I know this isn't very efficient and will take a long time to do.

    Would someone be willing to suggest how to create a custom inspector for creating waves like the ones in the code below:
    Code (CSharp):
    1. Wave wave1 = new Wave();
    2.         wave1.entries.Add(new Wave.Entry(enemies[0], 10 * difficulty));
    3.         waves.Add(wave1);
    4.      
    5.         Wave wave2 = new Wave();
    6.         wave2.entries.Add(new Wave.Entry(enemies[0], 20 * difficulty));
    7.         waves.Add(wave2);
    8.      
    9.         Wave wave3 = new Wave();
    10.         wave3.entries.Add(new Wave.Entry(enemies[0], 20 * difficulty));
    11.         wave3.entries.Add(new Wave.Entry(enemies[1], 10 * difficulty));
    12.         waves.Add(wave3);
    I should probably add, that the waves are held in a list
     
  2. Cnc96

    Cnc96

    Joined:
    Dec 17, 2013
    Posts:
    57
    Bump?
     
  3. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    have a look over: http://burgzergarcade.net/scriptableobjects-as-databases-in-unity/

    it's not "waves of enemies" but it should give you an idea of how to store types of things (in your case enemies rather than weapons) and setup a simple editor... they're also doing a complete remake of their tutorial vids on youtube for the items in their rpg at the moment, lots of editor scripting in there (they have literally hundred of vids, you want the more recent stuff :) )
     
  4. Cnc96

    Cnc96

    Joined:
    Dec 17, 2013
    Posts:
    57
    Thanks! I'll have a look through it all now :)
     
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,199
    If you create a Wave as a class that's [Serializeable], and implement your level as a list of Waves (ie you have a level MonoBehaviour with a Wave array), you will get an editable wave list in the inspector right out of the box.

    If you want a fancier inspector for that, you'll need to write a PropertyDrawer for the Wave class.

    You could also use a ReorderableList, as that'd probably give you some nice controll over when the things spawn. Somebody wrote up a tutorial for using those, with waves in a SHMUP as the example, so you'll probably get a lot of help from that.
     
    Kiwasi and chelnok like this.
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Not sure why you need a custom inspector for this...
     
  7. Cnc96

    Cnc96

    Joined:
    Dec 17, 2013
    Posts:
    57
    I suppose cause I thought that it would make organising large lists easier to do, for instance for my pool manager and enemies lists as I will have around 10 different types of enemies and easily 2 times as many objects which can spawn.

    And thanks Baste, that will probably be more useful to me know that I know of it. :D
     
  8. Effectcore

    Effectcore

    Joined:
    Mar 2, 2014
    Posts:
    56
    I also have used that code for my project Cnc96!

    Did you manage to solve your inspector issue? I am also trying implment so the WaveManager reads the Wave.Entries from something separate instead hardcoded into the script file

    Baste: Your first suggestion sounds exactly what I am looking for, I have been trying to implement this [Serializeable] function but can't seem to get it working. If you can maybe make example code or point to a tutorial about it?
     
  9. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,199
    It's not rocket science - if you have a class that's not a MonoBehaviour, you'll have to tag it like this to make Unity Serialize (save) other fields with the class in it. Try to create this:

    Code (CSharp):
    1. public class MyBehaviour : MonoBehaviour {
    2.    
    3.     public MyData[] allData;
    4.    
    5. }
    6.  
    7. [System.Serializeable]
    8. public class MyData {
    9.     public string foo;
    10.     public int bar;
    11. }
    If you put the MyBehaviour class on a GameObject, the allData field will be drawn. If you don't have [System.Serializeable] over the MyData class, Unity won't try to serialize it, and it won't be drawn. There's nothing more to it that that.

    If you want a tutorial for the reorderable list, I linked that.
     
  10. Cnc96

    Cnc96

    Joined:
    Dec 17, 2013
    Posts:
    57
    I never got it to work from a separate script using a custom editor, but I managed to change how the waves were set up. Basically, I removed the CreateWaves() method, and removed the hard coded wave setup and I made the public class Wave and public class Entry serializable objects like so:

    Code (CSharp):
    1. [System.Serializable]
    2.     public class Wave {
    3.         public List<Entry> entries;
    4.        
    5.         public Wave() {
    6.             this.entries = new List<Entry>();
    7.         }
    8.         [System.Serializable]
    9.         public class Entry {
    10.             public string enemy;
    11.             public int count;
    12.             [HideInInspector]
    13.             public int spawned;
    14.            
    15.             public Entry(string enemy, int count) {
    16.                 this.enemy = enemy;
    17.                 this.count = count;
    18.                 this.spawned = 0;
    19.             }
    20.         }
    21.     }
    Whilst it isnt the best looking in the inspector, it does allow you to have complete control over how each wave is set up.

    Hope this helps :)
     
  11. Effectcore

    Effectcore

    Joined:
    Mar 2, 2014
    Posts:
    56
    Thanks for your both answers!
    I got the Serializable function working and sofar I the inspector is looking exactly how I want it to look like!

    But I am still puzzled how to make a new CreateWave so it will be implmented in the game.
    I noticed you used String instead of gameobjects to ref

    Maybe you are willing to share how you did it? :)
     
  12. Cnc96

    Cnc96

    Joined:
    Dec 17, 2013
    Posts:
    57
    Sure, anything to help seeing as you helped me quite a bit. :)

    I changed the gameobject to string for 'enemy' as I am using a Pool Manager, so that is a reference to the Pooled Object. I'll post what I've got for my script below, just to be warned, it has a bunch of editor properties so that the inspector is organized :p

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class WaveManager : MonoBehaviour {
    7.     /*===================
    8.      * Creating a custom data type to easily store wave infromation
    9.      ====================*/
    10.     [System.Serializable]
    11.     public class Wave {
    12.         public List<Entry> entries;
    13.      
    14.         public Wave() {
    15.             this.entries = new List<Entry>();
    16.         }
    17.         [System.Serializable]
    18.         public class Entry {
    19.             //This HAS to be the same as the name in the list of enemies
    20.             public string enemy;
    21.             public int count;
    22.             [HideInInspector]
    23.             public int spawned;
    24.          
    25.             public Entry(string enemy, int count) {
    26.                 this.enemy = enemy;
    27.                 this.count = count;
    28.                 this.spawned = 0;
    29.             }
    30.         }
    31.     }
    32.  
    33.     [Header("Waves Setup")]
    34.     public List<Wave> waves = new List<Wave> ();
    35.     //The pooled objects we want to spawn, we use the names in the Pool Manager here
    36.     public List<string> pooledEnemies = new List<string>();
    37.  
    38.     [Header("Wave Spawning")]
    39.     //A distance from the view of the camera in which enemies will spawn,
    40.     //making sure that they do not spawn in sight of character
    41.     public float spawnBuffer = 100f;
    42.  
    43.     [Header("Wave Properties")]
    44.     //Time between each wave
    45.     public float waveSpread = 10f;
    46.     public float spawnTime = 3f;
    47.     public int startWave = 1;
    48.     //Value increases as waves increase so there is an increase in difficulty
    49.     public int startDifficulty = 1;
    50.  
    51.     [Header("Wave UI")]
    52.     //Reference to UI Text
    53.     public Text waveText;
    54.     //Hiding the number of alive enemies so the player cannot F*** with it via inspector
    55.     [HideInInspector]
    56.     public int enemiesStillAlive = 0;
    57.  
    58.     //Setting the central spawn pos to 0,0,0
    59.     Vector3 spawnPos = Vector3.zero;
    60.     int waveNumber;
    61.     float timer;
    62.     Wave currWave;
    63.     int spawnThisWave = 0;
    64.     int totalToSpawn;
    65.     bool shouldSpawn = false;
    66.     int difficulty;
    67.  
    68.     void Start() {
    69.         //Allowing us to start on higher waves
    70.         waveNumber = startWave > 0 ? startWave - 1 : 0;
    71.         difficulty = startDifficulty;
    72.  
    73.         //Create the Waves
    74.         //CreateWaves ();
    75.  
    76.         //Start the first wave
    77.         StartCoroutine("StartNextWave");
    78.     }
    79.  
    80.     void Update() {
    81.         if (!shouldSpawn) {
    82.             return;
    83.         }
    84.  
    85.         if (spawnThisWave == totalToSpawn && enemiesStillAlive == 0) {
    86.             StartCoroutine("StartNextWave");
    87.             return;
    88.         }
    89.  
    90.         timer += Time.deltaTime;
    91.  
    92.         if (timer >= spawnTime) {
    93.             foreach (Wave.Entry entry in currWave.entries) {
    94.                 if (entry.spawned < entry.count)
    95.                     Spawn(entry);
    96.             }
    97.         }
    98.     }
    99.  
    100.     IEnumerator StartNextWave() {
    101.         shouldSpawn = false;
    102.  
    103.         yield return new WaitForSeconds (waveSpread);
    104.  
    105.         if (waveNumber == waves.Count) {
    106.             waveNumber = 0;
    107.             difficulty++;
    108.             //waves = new List<Wave>();
    109.             //CreateWaves();
    110.         }
    111.  
    112.         currWave = waves [waveNumber];
    113.  
    114.         totalToSpawn = 0;
    115.  
    116.         foreach (Wave.Entry entry in currWave.entries) {
    117.             totalToSpawn += entry.count;
    118.         }
    119.  
    120.         spawnThisWave = 0;
    121.         shouldSpawn = true;
    122.  
    123.         waveNumber++;
    124.  
    125. //        waveText.text = (waveNumber + (difficulty - 1) * waves.Count).ToString();
    126.     }
    127.  
    128.     void Spawn(Wave.Entry entry) {
    129.         timer = 0;
    130.  
    131.         Vector3 randPos = Random.insideUnitSphere * 35;
    132.         randPos.y = 0;
    133.  
    134.         NavMeshHit hit;;
    135.         if (!NavMesh.SamplePosition (randPos, out hit, 5, 1)) {
    136.             return;
    137.         }
    138.  
    139.         spawnPos = hit.position;
    140.  
    141.         Vector3 screenPos = Camera.main.WorldToScreenPoint (spawnPos);
    142.         if ((screenPos.x > -spawnBuffer && screenPos.x < (Screen.width + spawnBuffer)) && (screenPos.y > - spawnBuffer && screenPos.y < (Screen.height + spawnBuffer))) {
    143.             return;
    144.         }
    145.         SpawnEnemy (spawnPos, entry.enemy);
    146.  
    147.         entry.spawned++;
    148.         spawnThisWave++;
    149.         enemiesStillAlive++;
    150.     }
    151.  
    152.     //Pooled Object Spawning
    153.     public void SpawnEnemy(Vector3 pTransform, string objName) {
    154.         if (objName != "") {
    155.             GameObject g = PoolManager.current.GetPooledObject(objName);
    156.          
    157.             if (g != null) {
    158.                 g.transform.position = pTransform;
    159.                 g.transform.rotation = Quaternion.identity;
    160.                 g.SetActive(true);
    161.             }
    162.         }
    163.     }
    I removed the CreateWaves method, as when you create the list using the inspector it's already filled with the values it needs.
     
    Last edited: Jun 22, 2015
  13. Effectcore

    Effectcore

    Joined:
    Mar 2, 2014
    Posts:
    56
    I apprecite your help Cnc96 but I just can't seem to make it work :/

    I tried simplify the code but I am getting errors like this:


    Figured out a way to make waves in the "bad" way instead of trying to make a friendly inspector.


    Code (CSharp):
    1.  
    2.     void CreateWaves() {
    3.         if (PlanetName == "Firera")
    4.         {
    5.             Wave wave1 = new Wave ();
    6.             wave1.entries.Add (new Wave.Entry (enemies [0], 9 * difficulty)); //9
    7.             waves.Add (wave1);
    8.      
    9.             Wave wave2 = new Wave ();
    10.             wave2.entries.Add (new Wave.Entry (enemies [0], 7 * difficulty));
    11.             wave2.entries.Add (new Wave.Entry (enemies [7], 4 * difficulty));
    12.             waves.Add (wave2);
    13.          
    14.             Wave wave3 = new Wave ();
    15.             wave3.entries.Add (new Wave.Entry (enemies [0], 7 * difficulty));
    16.             wave3.entries.Add (new Wave.Entry (enemies [1], 3 * difficulty));
    17.             waves.Add (wave3);
    18.      
    19.         }
    20.  
    21.         if (PlanetName == "Swampia")
    22.         {
    23.             Wave wave1 = new Wave ();
    24.             wave1.entries.Add (new Wave.Entry (enemies [3], 9 * difficulty)); //9
    25.             waves.Add (wave1);
    26.          
    27.             Wave wave2 = new Wave ();
    28.             wave2.entries.Add (new Wave.Entry (enemies [0], 7 * difficulty));
    29.             wave2.entries.Add (new Wave.Entry (enemies [7], 4 * difficulty));
    30.             waves.Add (wave2);
    31.          
    32.             Wave wave3 = new Wave ();
    33.             wave3.entries.Add (new Wave.Entry (enemies [0], 7 * difficulty));
    34.             wave3.entries.Add (new Wave.Entry (enemies [1], 3 * difficulty));
    35.             waves.Add (wave3);
    36.         }
    37.  
    Better to make solid progress than trying to make a dream feature ~ la ~ fail faster motto.


    Edit: Pretty much me atm

    I thought that that both of us are using the same template for our projects so it could be good do ball ideas between each other.

    My skype: christiannordgren1
     
    Last edited: Jun 26, 2015
  14. Cnc96

    Cnc96

    Joined:
    Dec 17, 2013
    Posts:
    57
    Yeah that'd be a great idea, my skype is: alliancegames.

    I couldn't really tell you what the error is cause I have a bunch of new lines which makes the line numbers different in our scripts .-.
    I've also decided to take a few week break from the project as I've had a bit of a creative block when coming up with the 3d art probably because I've been working on it everyday for nearly 2 months .-.