diff --git a/Assets/Editor/WaveConfigCustomInspector.cs b/Assets/Editor/WaveConfigCustomInspector.cs index 8ee92ad..e56df76 100644 --- a/Assets/Editor/WaveConfigCustomInspector.cs +++ b/Assets/Editor/WaveConfigCustomInspector.cs @@ -10,12 +10,14 @@ namespace GatherAndDefend.LevelEditor { EditorGUILayout.HelpBox(@"How to use : - ConstantSpawn: Drag the chosen Enemy and insert the number to make in Count +- GroupSpawn: Each group contains enemies, and the time to trigger in float format for minutes. - GameDuration : Duration of the game, in minutes - Usage : Drop this config into a level -- Creation interval is determined by the game duration and the number of enemies to create +- The creation interval is determined by the game duration and the number of enemies to create -Important consideration : -- You must assign at least 1 enemy of Count of at least 1 to play.", MessageType.None); +Important consideration : +- You must assign at least 1 enemy of Count of at least 1 to play. +- GameDuration cannot be 0.", MessageType.None); base.OnInspectorGUI(); } } diff --git a/Assets/Scripts/LevelConfig/WaveConfig.cs b/Assets/Scripts/LevelConfig/WaveConfig.cs index e4bc275..afee1dd 100644 --- a/Assets/Scripts/LevelConfig/WaveConfig.cs +++ b/Assets/Scripts/LevelConfig/WaveConfig.cs @@ -1,6 +1,11 @@ using System.Collections.Generic; using UnityEngine; - +[System.Serializable] +public class GroupList +{ + public List groupSpawn; + public float triggerTime; +} [CreateAssetMenu(menuName = "Gather And Defend/Levels/WaveConfig")] public class WaveConfig : ScriptableObject { @@ -8,7 +13,9 @@ public class WaveConfig : ScriptableObject [SerializeField] private List _constantSpawn = new List(); [SerializeField] - private float _gameDuration = 0; + private List _nestedGroupSpawn = new List(); + [SerializeField] + private float _gameDuration = 1; private float _enemySpawndOnStart = 0; private int _enemySum = 0; public List ConstantSpawn @@ -18,17 +25,37 @@ public class WaveConfig : ScriptableObject return _constantSpawn; } } - public float GetInterval() + + /** + * Contains every list of enemy groups + */ + public List NestedGroupSpawn { - float interval = _gameDuration * 60.0f / (_enemySum > 0 ? _enemySum.ToFloat() : SumCount()) ; - return interval; + get + { + return _nestedGroupSpawn; + } } + public float Interval + { + get { + float interval = _gameDuration * 60.0f / (_enemySum > 0 ? _enemySum.ToFloat() : SumCount()); + return interval; + } + } + /** + * Returns the updated game interval after adjusting to enemies spawned at the start + */ public float GetUpdatedInterval() { _enemySpawndOnStart++; float interval = Mathf.Max(_gameDuration * 60.0f / (_enemySum - _enemySpawndOnStart).ToFloat(), 0); return interval; } + + /** + * Returns a random enemy among the constantSpawn list + */ public EnemyType GetRandomSpawn() { if (_constantSpawn.Count == 1) @@ -37,6 +64,10 @@ public class WaveConfig : ScriptableObject } return _constantSpawn[Random.Range(0, _constantSpawn.Count - 1)]; } + + /** + * Returns the count of enemies to spawn + */ private float SumCount() { foreach (EnemyType enemy in _constantSpawn) @@ -45,5 +76,4 @@ public class WaveConfig : ScriptableObject } return _enemySum.ToFloat(); } - } diff --git a/Assets/Scripts/LevelConfig/WaveObserver.cs b/Assets/Scripts/LevelConfig/WaveObserver.cs index f1ee0e3..3229353 100644 --- a/Assets/Scripts/LevelConfig/WaveObserver.cs +++ b/Assets/Scripts/LevelConfig/WaveObserver.cs @@ -1,26 +1,34 @@ using System.Collections.Generic; using UnityEngine; -using System; public class WaveObserver : Singleton { private List _subjects = new List(); private List _aliveEnemyCount = new List(); private List _copyConstantSpawn; + private List _copyGroupSpawn; + private List _groupSpawnTimers; private WaveConfig _levelConfig; private const int MAXTOUGHNESS = 10; private int _spawnerTiming = 0; private List _intervalTiming = new List(); - public WaveConfig LevelConfig + private bool _once = true; + private int _currentGroupIndex = 0; + + public void SetLevelConfig(WaveConfig config) { - set + _levelConfig = config; + _copyConstantSpawn = new List(); + _copyGroupSpawn = new List(); + _groupSpawnTimers = new List(); + foreach (EnemyType enemy in _levelConfig.ConstantSpawn) { - _levelConfig = value; - _copyConstantSpawn = new List(); - foreach (EnemyType enemy in _levelConfig.ConstantSpawn) - { - _copyConstantSpawn.Add(enemy.Count); - } + _copyConstantSpawn.Add(enemy.Count); + } + for (int index = 0; index < _levelConfig.NestedGroupSpawn.Count; index++) + { + _copyGroupSpawn.Add(_levelConfig.NestedGroupSpawn[index]); + _groupSpawnTimers.Add(_levelConfig.NestedGroupSpawn[index].triggerTime); } } @@ -35,6 +43,12 @@ public class WaveObserver : Singleton _subjects.Add(spawnerSubject); _aliveEnemyCount.Add(0); _intervalTiming.Add(++_spawnerTiming); + // Ensures that only one spawner keeps track of the grouped spawn timer. + if (_once) + { + _once = false; + spawnerSubject.SetGroupSpawnTimers(_groupSpawnTimers); + } } /** @@ -44,7 +58,7 @@ public class WaveObserver : Singleton public void NotifySpawned(SpawnerTile spawnerSubject) { GameObject paramPrefab = spawnerSubject.Prefab; - spawnerSubject.ChangeSpawnSpeed(_levelConfig.GetInterval() * _spawnerTiming); + spawnerSubject.ChangeSpawnSpeed(_levelConfig.Interval * _spawnerTiming); if (paramPrefab.Equals(_levelConfig.ConstantSpawn[0].GetEnemyObject())) { int currentCount = 0; @@ -136,7 +150,24 @@ public class WaveObserver : Singleton { index = rand.Next(_subjects.Count); } while (_intervalTiming.Count <= index); - spawnerTile.ChangeSpawnSpeed(_levelConfig.GetInterval() * _intervalTiming[index]); + spawnerTile.ChangeSpawnSpeed(_levelConfig.Interval * _intervalTiming[index]); _intervalTiming.Remove(index); } + + /** + * Called when it is time to spawn a group + * Assigns a random element of the group to a random spawner + */ + public void NotifyGroupSpawn() + { + System.Random rand = new System.Random(); + foreach (EnemyType groupEnemy in _copyGroupSpawn[_currentGroupIndex].groupSpawn) + { + for (int i = 0; i < groupEnemy.Count; i++) + { + _subjects[rand.Next(_subjects.Count)].TriggerSpawn(groupEnemy.GetEnemyObject()); + } + } + _currentGroupIndex++; + } } diff --git a/Assets/Scripts/LevelManager/LevelManager.cs b/Assets/Scripts/LevelManager/LevelManager.cs index a181634..940bd8f 100644 --- a/Assets/Scripts/LevelManager/LevelManager.cs +++ b/Assets/Scripts/LevelManager/LevelManager.cs @@ -166,7 +166,7 @@ public class LevelManager : Singleton _currentLevel = level; _waveObserver = WaveObserver.Instance; - _waveObserver.LevelConfig = _currentLevel.WaveConfig; + _waveObserver.SetLevelConfig(_currentLevel.WaveConfig); Grid grid = Object.FindObjectOfType(); //create new grid if there is none if (!grid) diff --git a/Assets/Scripts/Tiles/SpawnerTile.cs b/Assets/Scripts/Tiles/SpawnerTile.cs index 38450a7..b37aa4f 100644 --- a/Assets/Scripts/Tiles/SpawnerTile.cs +++ b/Assets/Scripts/Tiles/SpawnerTile.cs @@ -16,6 +16,15 @@ public class SpawnerTile : LevelTile private WaveObserver _observer; private bool _stopped = false; private bool _cooldownEnded = false; + private List _groupSpawnTimers = new List(); + + public void SetGroupSpawnTimers(List groupSpawnTimers) + { + for (int i = 0; i < groupSpawnTimers.Count-1; i++) + { + _groupSpawnTimers.Add(groupSpawnTimers[i]); + } + } public override void LevelStart() @@ -24,7 +33,7 @@ public class SpawnerTile : LevelTile _observer.Attach(this); if (_spawnOnStart && _lifetime <= 0) { - _prefab.Create(Position, parent: LevelManager.Instance.LevelTransform); + TriggerSpawn(_prefab); _observer.NotifyOnStart(this); } } @@ -36,6 +45,14 @@ public class SpawnerTile : LevelTile { _spawnCounter += Time.deltaTime; } + if (_groupSpawnTimers.Count > 0) + { + if (_lifetime / 60 > _groupSpawnTimers[0]) + { + _groupSpawnTimers.RemoveAt(0); + _observer.NotifyGroupSpawn(); + } + } if (_spawnCounter < _spawnSpeed) return; _spawnCounter = 0; @@ -45,7 +62,7 @@ public class SpawnerTile : LevelTile _cooldownEnded = true; return; } - _prefab.Create(Position, parent: LevelManager.Instance.LevelTransform); + TriggerSpawn(_prefab); _observer.NotifySpawned(this); } public override bool Equals(ILevelObject other) @@ -90,13 +107,13 @@ public class SpawnerTile : LevelTile _spawnOnStart = dict[nameof(_spawnOnStart)].ToBool(); } - public GameObject Prefab - { - set + public GameObject Prefab + { + set { - _prefab = value; - } - get + _prefab = value; + } + get { return _prefab; } @@ -110,4 +127,13 @@ public class SpawnerTile : LevelTile { _spawnSpeed = value; } + + /** + * Instantly spawns an enemy. + * Used for GroupSpawn + */ + public void TriggerSpawn(GameObject enemy) + { + enemy.Create(Position, parent: LevelManager.Instance.LevelTransform); + } } \ No newline at end of file diff --git a/Assets/WaveConfig/Config01.asset b/Assets/WaveConfig/Config01.asset index 371055b..257aaa6 100644 --- a/Assets/WaveConfig/Config01.asset +++ b/Assets/WaveConfig/Config01.asset @@ -15,4 +15,17 @@ MonoBehaviour: _constantSpawn: - _enemy: {fileID: 313037212318601125, guid: 5bbf0d85fa5bb3f4599da79f0a84e3a9, type: 3} _count: 5 - _gameDuration: 1 + _nestedGroupSpawn: + - groupSpawn: + - _enemy: {fileID: 313037212318601125, guid: 5bbf0d85fa5bb3f4599da79f0a84e3a9, type: 3} + _count: 3 + - _enemy: {fileID: 313037212318601125, guid: 5bbf0d85fa5bb3f4599da79f0a84e3a9, type: 3} + _count: 1 + triggerTime: 0.5 + - groupSpawn: + - _enemy: {fileID: 313037212318601125, guid: 5bbf0d85fa5bb3f4599da79f0a84e3a9, type: 3} + _count: 1 + - _enemy: {fileID: 313037212318601125, guid: 5bbf0d85fa5bb3f4599da79f0a84e3a9, type: 3} + _count: 2 + triggerTime: 1 + _gameDuration: 3