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:
parent
4182b3340d
commit
90fc5331bf
4
Assets/Scriptables/Waves/SpawnPack_Test.asset
generated
4
Assets/Scriptables/Waves/SpawnPack_Test.asset
generated
@ -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
|
||||||
|
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}
|
||||||
- {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
|
||||||
|
@ -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 @@ public class SpawnPackSO : ScriptableObject
|
|||||||
[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 @@ public IReadOnlyCollection<Enemy> EnemiesToSpawn()
|
|||||||
enemiesToSpawn.Add(possibleEnemies[enemyIndex]);
|
enemiesToSpawn.Add(possibleEnemies[enemyIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return enemiesToSpawn;
|
return enemiesToSpawn.AsReadOnly();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -47,42 +47,45 @@ private IEnumerator CO_HandleSpawnWaves(IEnumerable<SpawnWaveSO> spawnWaves)
|
|||||||
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 @@ private IEnumerator SpawnWave(SpawnWaveSO spawnWave)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user