Initial pass on wave spawning.
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				ci/woodpecker/push/woodpecker Pipeline was successful
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	ci/woodpecker/push/woodpecker Pipeline was successful
				
			This commit is contained in:
		
							
								
								
									
										4
									
								
								Assets/Scriptables/Waves/SpawnPack_Test.asset
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								Assets/Scriptables/Waves/SpawnPack_Test.asset
									
									
									
										generated
									
									
									
								
							| @@ -15,5 +15,5 @@ MonoBehaviour: | ||||
|   possibleEnemies: | ||||
|   - {fileID: 7321161463868935016, guid: 200dea255585b154f8222e550c23b55e, type: 3} | ||||
|   - {fileID: 1158114420728610952, guid: 9dff131d77697b242ae2e2b68aa7e1db, type: 3} | ||||
|   minCount: 5 | ||||
|   maxCount: 10 | ||||
|   minCount: 30 | ||||
|   maxCount: 50 | ||||
|   | ||||
							
								
								
									
										6
									
								
								Assets/Scriptables/Waves/SpawnWave_Test.asset
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								Assets/Scriptables/Waves/SpawnWave_Test.asset
									
									
									
										generated
									
									
									
								
							| @@ -26,8 +26,8 @@ MonoBehaviour: | ||||
|   - {fileID: 11400000, guid: 970a884aa40996749b022ebb2765d769, type: 2} | ||||
|   - {fileID: 11400000, guid: 970a884aa40996749b022ebb2765d769, type: 2} | ||||
|   distribution: 2 | ||||
|   radius: 25 | ||||
|   packRadius: 7.5 | ||||
|   radius: 50 | ||||
|   packRadius: 7 | ||||
|   timeBetweenSpawns: 0.01 | ||||
|   timeBetweenPacks: 0 | ||||
|   timeBetweenPacks: 0.1 | ||||
|   timeToComplete: 0 | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.ObjectModel; | ||||
| using DefaultNamespace; | ||||
| using UnityEngine; | ||||
| using UnityEngine.Assertions; | ||||
| @@ -12,7 +13,7 @@ namespace Wave | ||||
|         [SerializeField] [Min(0)] private int minCount; | ||||
|         [SerializeField] [Min(0)] private int maxCount; | ||||
|          | ||||
|         public IReadOnlyCollection<Enemy> EnemiesToSpawn() | ||||
|         public ReadOnlyCollection<Enemy> EnemiesToSpawn() | ||||
|         { | ||||
|             Assert.IsTrue(possibleEnemies.Count > 0); | ||||
|              | ||||
| @@ -25,7 +26,7 @@ namespace Wave | ||||
|                 enemiesToSpawn.Add(possibleEnemies[enemyIndex]); | ||||
|             } | ||||
| 
 | ||||
|             return enemiesToSpawn; | ||||
|             return enemiesToSpawn.AsReadOnly(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -47,42 +47,45 @@ namespace Wave | ||||
|                 yield return SpawnWave(spawnWave); | ||||
|                 _timeSinceLastSpawnWaveStarted = 0.0f; | ||||
| 
 | ||||
|                 yield return new WaitUntil(() => | ||||
|                     _timeSinceLastSpawnWaveStarted >= spawnWave.TimeToComplete | ||||
|                     || HasWaveBeenCleared); | ||||
|                 yield return new WaitUntil(() => _timeSinceLastSpawnWaveStarted >= spawnWave.TimeToComplete || HasWaveBeenCleared); | ||||
| 
 | ||||
|                 endSpawnWaveEventChannel.RaiseEvent(spawnWave); | ||||
|                 yield return new WaitForSeconds(timeBetweenClearedWaves); | ||||
| 
 | ||||
|                 if (HasWaveBeenCleared) | ||||
|                 { | ||||
|                     yield return new WaitForSeconds(timeBetweenClearedWaves); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private IEnumerator SpawnWave(SpawnWaveSO spawnWave) | ||||
|         { | ||||
|             var enemyPacksToSpawn = spawnWave.Packs.Select(pack => pack.EnemiesToSpawn()).ToList().AsReadOnly(); | ||||
|             var totalPacksToSpawn = enemyPacksToSpawn.Count; | ||||
|              | ||||
| 
 | ||||
|             var numberPacksSpawned = 0; | ||||
|             foreach (var enemyPackToSpawn in enemyPacksToSpawn) | ||||
|             for (var packN = 0; packN < enemyPacksToSpawn.Count; packN++) | ||||
|             { | ||||
|                 numberPacksSpawned++; | ||||
|                 var waveCompletionPercentage = (float) numberPacksSpawned / (float) totalPacksToSpawn; | ||||
|                 var waveCompletionPercentage = (float)packN / (float)enemyPacksToSpawn.Count; | ||||
| 
 | ||||
|                 // TODO (Michael): Both the pack and enemy position samplers need to be a bit smarter. I think in most scenarios the pack circle just needs to not overlap with other pack circles.  | ||||
|                 // The enemy spawns probably need something like a poission disk sampler so that monsters dont overlap. The enemy spawn radius at the pack's position should also probably scale with the size of the pack. | ||||
|                 var enemyPackSpawnCenter = CalculatePackSpawnPosition(waveCompletionPercentage, spawnWave); | ||||
|                  | ||||
|                 foreach (var enemy in enemyPackToSpawn) | ||||
|                 var enemyPackSpawnOffset = SampleSpawnOffset(spawnWave.Distribution, spawnWave.Radius, waveCompletionPercentage); | ||||
|                 enemyPackSpawnOffset += enemyPackSpawnOffset.normalized * (minimumSpawnRadius + spawnWave.PackRadius); | ||||
| 
 | ||||
|                 var enemyPackSpawnPosition = spawnCenter.position + enemyPackSpawnOffset; | ||||
| 
 | ||||
|                 var enemyPackToSpawn = enemyPacksToSpawn[packN]; | ||||
|                 for (var enemyN = 0; enemyN < enemyPackToSpawn.Count; enemyN++) | ||||
|                 { | ||||
| 
 | ||||
|                     var spawnLocation = CalculateEnemySpawnPosition(enemyPackSpawnCenter, spawnWave.PackRadius); | ||||
|                     var spawnedEnemy = Instantiate(enemy, spawnLocation, Quaternion.identity); | ||||
|                     enemyRuntimeSet.Add(spawnedEnemy); | ||||
|                     var packCompletionPercentage = (float)enemyN / (float)enemyPackToSpawn.Count; | ||||
|                     var enemySpawnOffset = SampleSpawnOffset(SpawnDistribution.DiskRandom, spawnWave.PackRadius, packCompletionPercentage); | ||||
|                     var enemySpawnPosition = enemyPackSpawnPosition + enemySpawnOffset; | ||||
|                      | ||||
|                     var spawnedEnemy = Instantiate(enemyPackToSpawn[enemyN], enemySpawnPosition, Quaternion.identity); | ||||
|                     enemyRuntimeSet.Add(spawnedEnemy); | ||||
| 
 | ||||
|                     if (spawnWave.TimeBetweenSpawns > 0.0f) | ||||
|                     { | ||||
|                         yield return new WaitForSeconds(spawnWave.TimeBetweenSpawns); | ||||
|                     } | ||||
| 
 | ||||
|                 } | ||||
|                  | ||||
|                 if (spawnWave.TimeBetweenPacks > 0.0f) | ||||
| @@ -92,35 +95,23 @@ namespace Wave | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private Vector3 CalculatePackSpawnPosition(float waveCompletionPercentage, SpawnWaveSO spawnWave) | ||||
|         private static Vector3 SampleSpawnOffset(SpawnDistribution distribution, float radius, float completionPercentage) | ||||
|         { | ||||
|             var angle = waveCompletionPercentage * 2 * MathF.PI; | ||||
|             // TODO (Michael): Both the pack and enemy position samplers need to be a bit smarter. I think in most scenarios the pack circle just needs to not overlap with other pack circles.  | ||||
|             // The enemy spawns probably need something like a poission disk sampler so that monsters dont overlap. The enemy spawn radius at the pack's position should also probably scale with the size of the pack. | ||||
|             var angle = completionPercentage * 2 * MathF.PI; | ||||
|             var unitCirclePosition = Random.insideUnitCircle; | ||||
|             var swizzledUnitCirclePosition = new Vector3(unitCirclePosition.x, 0, unitCirclePosition.y); | ||||
|              | ||||
|             var spawnOffset = spawnWave.Distribution switch | ||||
|             return distribution switch | ||||
|             { | ||||
|                 SpawnDistribution.Uniform => new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * spawnWave.Radius, | ||||
|                 SpawnDistribution.Random => swizzledUnitCirclePosition.normalized *  spawnWave.Radius, | ||||
|                 SpawnDistribution.DiskRandom => swizzledUnitCirclePosition *  spawnWave.Radius, | ||||
|                 _ => throw new ArgumentOutOfRangeException(nameof(spawnWave.Distribution), spawnWave.Distribution, null) | ||||
|                 SpawnDistribution.Uniform => new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * radius, | ||||
|                 SpawnDistribution.Random => swizzledUnitCirclePosition.normalized *  radius, | ||||
|                 SpawnDistribution.DiskRandom => swizzledUnitCirclePosition *  radius, | ||||
|                 _ => throw new ArgumentOutOfRangeException(nameof(distribution), distribution, null) | ||||
|             }; | ||||
|              | ||||
|             spawnOffset += spawnOffset.normalized * (minimumSpawnRadius + spawnWave.PackRadius); | ||||
| 
 | ||||
|             return spawnCenter.position + spawnOffset; | ||||
|         } | ||||
| 
 | ||||
|         private Vector3 CalculateEnemySpawnPosition(Vector3 center, float radius) | ||||
|         { | ||||
|             var unitCirclePosition = Random.insideUnitCircle; | ||||
|             var swizzledUnitCirclePosition = new Vector3(unitCirclePosition.x, 0, unitCirclePosition.y); | ||||
| 
 | ||||
|             var spawnOffset = swizzledUnitCirclePosition * radius; | ||||
| 
 | ||||
|             return center + spawnOffset; | ||||
|         } | ||||
|          | ||||
|         private void Update() | ||||
|         { | ||||
|             _timeSinceLastSpawnWaveStarted += Time.deltaTime; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user