Initial pass on wave spawning.
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
Michael 2023-08-02 17:27:45 -04:00
parent 505b292718
commit 41ceb7a01c
4 changed files with 39 additions and 47 deletions

View File

@ -15,5 +15,5 @@ MonoBehaviour:
possibleEnemies: possibleEnemies:
- {fileID: 7321161463868935016, guid: 200dea255585b154f8222e550c23b55e, type: 3} - {fileID: 7321161463868935016, guid: 200dea255585b154f8222e550c23b55e, type: 3}
- {fileID: 1158114420728610952, guid: 9dff131d77697b242ae2e2b68aa7e1db, type: 3} - {fileID: 1158114420728610952, guid: 9dff131d77697b242ae2e2b68aa7e1db, type: 3}
minCount: 5 minCount: 30
maxCount: 10 maxCount: 50

View File

@ -26,8 +26,8 @@ MonoBehaviour:
- {fileID: 11400000, guid: 970a884aa40996749b022ebb2765d769, type: 2} - {fileID: 11400000, guid: 970a884aa40996749b022ebb2765d769, type: 2}
- {fileID: 11400000, guid: 970a884aa40996749b022ebb2765d769, type: 2} - {fileID: 11400000, guid: 970a884aa40996749b022ebb2765d769, type: 2}
distribution: 2 distribution: 2
radius: 25 radius: 50
packRadius: 7.5 packRadius: 7
timeBetweenSpawns: 0.01 timeBetweenSpawns: 0.01
timeBetweenPacks: 0 timeBetweenPacks: 0.1
timeToComplete: 0 timeToComplete: 0

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using DefaultNamespace; using DefaultNamespace;
using UnityEngine; using UnityEngine;
using UnityEngine.Assertions; using UnityEngine.Assertions;
@ -12,7 +13,7 @@ namespace Wave
[SerializeField] [Min(0)] private int minCount; [SerializeField] [Min(0)] private int minCount;
[SerializeField] [Min(0)] private int maxCount; [SerializeField] [Min(0)] private int maxCount;
public IReadOnlyCollection<Enemy> EnemiesToSpawn() public ReadOnlyCollection<Enemy> EnemiesToSpawn()
{ {
Assert.IsTrue(possibleEnemies.Count > 0); Assert.IsTrue(possibleEnemies.Count > 0);
@ -25,7 +26,7 @@ namespace Wave
enemiesToSpawn.Add(possibleEnemies[enemyIndex]); enemiesToSpawn.Add(possibleEnemies[enemyIndex]);
} }
return enemiesToSpawn; return enemiesToSpawn.AsReadOnly();
} }
} }
} }

View File

@ -47,42 +47,45 @@ namespace Wave
yield return SpawnWave(spawnWave); yield return SpawnWave(spawnWave);
_timeSinceLastSpawnWaveStarted = 0.0f; _timeSinceLastSpawnWaveStarted = 0.0f;
yield return new WaitUntil(() => yield return new WaitUntil(() => _timeSinceLastSpawnWaveStarted >= spawnWave.TimeToComplete || HasWaveBeenCleared);
_timeSinceLastSpawnWaveStarted >= spawnWave.TimeToComplete
|| HasWaveBeenCleared);
endSpawnWaveEventChannel.RaiseEvent(spawnWave); endSpawnWaveEventChannel.RaiseEvent(spawnWave);
yield return new WaitForSeconds(timeBetweenClearedWaves);
if (HasWaveBeenCleared)
{
yield return new WaitForSeconds(timeBetweenClearedWaves);
}
} }
} }
private IEnumerator SpawnWave(SpawnWaveSO spawnWave) private IEnumerator SpawnWave(SpawnWaveSO spawnWave)
{ {
var enemyPacksToSpawn = spawnWave.Packs.Select(pack => pack.EnemiesToSpawn()).ToList().AsReadOnly(); var enemyPacksToSpawn = spawnWave.Packs.Select(pack => pack.EnemiesToSpawn()).ToList().AsReadOnly();
var totalPacksToSpawn = enemyPacksToSpawn.Count;
for (var packN = 0; packN < enemyPacksToSpawn.Count; packN++)
var numberPacksSpawned = 0;
foreach (var enemyPackToSpawn in enemyPacksToSpawn)
{ {
numberPacksSpawned++; var waveCompletionPercentage = (float)packN / (float)enemyPacksToSpawn.Count;
var waveCompletionPercentage = (float) numberPacksSpawned / (float) totalPacksToSpawn;
// 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. var enemyPackSpawnOffset = SampleSpawnOffset(spawnWave.Distribution, spawnWave.Radius, waveCompletionPercentage);
// 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. enemyPackSpawnOffset += enemyPackSpawnOffset.normalized * (minimumSpawnRadius + spawnWave.PackRadius);
var enemyPackSpawnCenter = CalculatePackSpawnPosition(waveCompletionPercentage, spawnWave);
var enemyPackSpawnPosition = spawnCenter.position + enemyPackSpawnOffset;
foreach (var enemy in enemyPackToSpawn)
var enemyPackToSpawn = enemyPacksToSpawn[packN];
for (var enemyN = 0; enemyN < enemyPackToSpawn.Count; enemyN++)
{ {
var packCompletionPercentage = (float)enemyN / (float)enemyPackToSpawn.Count;
var spawnLocation = CalculateEnemySpawnPosition(enemyPackSpawnCenter, spawnWave.PackRadius); var enemySpawnOffset = SampleSpawnOffset(SpawnDistribution.DiskRandom, spawnWave.PackRadius, packCompletionPercentage);
var spawnedEnemy = Instantiate(enemy, spawnLocation, Quaternion.identity); var enemySpawnPosition = enemyPackSpawnPosition + enemySpawnOffset;
enemyRuntimeSet.Add(spawnedEnemy);
var spawnedEnemy = Instantiate(enemyPackToSpawn[enemyN], enemySpawnPosition, Quaternion.identity);
enemyRuntimeSet.Add(spawnedEnemy);
if (spawnWave.TimeBetweenSpawns > 0.0f) if (spawnWave.TimeBetweenSpawns > 0.0f)
{ {
yield return new WaitForSeconds(spawnWave.TimeBetweenSpawns); yield return new WaitForSeconds(spawnWave.TimeBetweenSpawns);
} }
} }
if (spawnWave.TimeBetweenPacks > 0.0f) 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 unitCirclePosition = Random.insideUnitCircle;
var swizzledUnitCirclePosition = new Vector3(unitCirclePosition.x, 0, unitCirclePosition.y); 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.Uniform => new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * radius,
SpawnDistribution.Random => swizzledUnitCirclePosition.normalized * spawnWave.Radius, SpawnDistribution.Random => swizzledUnitCirclePosition.normalized * radius,
SpawnDistribution.DiskRandom => swizzledUnitCirclePosition * spawnWave.Radius, SpawnDistribution.DiskRandom => swizzledUnitCirclePosition * radius,
_ => throw new ArgumentOutOfRangeException(nameof(spawnWave.Distribution), spawnWave.Distribution, null) _ => 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() private void Update()
{ {
_timeSinceLastSpawnWaveStarted += Time.deltaTime; _timeSinceLastSpawnWaveStarted += Time.deltaTime;