enemies spawn with offsets in positions and animations
This commit is contained in:
parent
d1930a6b00
commit
53e7fc78a7
4 changed files with 35 additions and 12 deletions
|
|
@ -16,4 +16,6 @@ MonoBehaviour:
|
||||||
Entries:
|
Entries:
|
||||||
- EnemyType: {fileID: 11400000, guid: a05af37e2256f164d80a66cc2a582466, type: 2}
|
- EnemyType: {fileID: 11400000, guid: a05af37e2256f164d80a66cc2a582466, type: 2}
|
||||||
Count: 50
|
Count: 50
|
||||||
SpawnInterval: 0.5
|
ChunkSize: 5
|
||||||
|
SpawnInterval: 2.5
|
||||||
|
SpawnSpread: 10
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,13 @@ namespace TD.Gameplay
|
||||||
{
|
{
|
||||||
status = GetComponent<EnemyStatus>();
|
status = GetComponent<EnemyStatus>();
|
||||||
|
|
||||||
|
// Randomize the walk-cycle phase on every peer independently. Animations
|
||||||
|
// are purely visual and not replicated, so each client picks its own offset —
|
||||||
|
// the result looks correct everywhere without any network traffic.
|
||||||
|
var animator = GetComponentInChildren<Animator>();
|
||||||
|
if (animator != null)
|
||||||
|
animator.Update(Random.Range(0f, 2f));
|
||||||
|
|
||||||
if (!IsServer) return;
|
if (!IsServer) return;
|
||||||
|
|
||||||
if (!hasPendingInit)
|
if (!hasPendingInit)
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,18 @@ namespace TD.Gameplay
|
||||||
[Tooltip("How many enemies of this type to spawn.")]
|
[Tooltip("How many enemies of this type to spawn.")]
|
||||||
public int Count;
|
public int Count;
|
||||||
|
|
||||||
[Tooltip("Seconds between each individual spawn within this group. " +
|
[Tooltip("How many enemies spawn per burst. 1 (default) = original one-at-a-time " +
|
||||||
"0 = all spawn simultaneously.")]
|
"behaviour. Values > 1 spawn that many simultaneously before waiting SpawnInterval.")]
|
||||||
|
public int ChunkSize;
|
||||||
|
|
||||||
|
[Tooltip("Seconds between each burst. With ChunkSize > 1 this is the pause between " +
|
||||||
|
"groups rather than between individual enemies.")]
|
||||||
public float SpawnInterval;
|
public float SpawnInterval;
|
||||||
|
|
||||||
|
[Tooltip("Max X-axis world-unit offset applied randomly to each enemy's spawn position. " +
|
||||||
|
"0 = all spawn at the exact tile centre. Spreads enemies left/right without " +
|
||||||
|
"affecting their forward/backward position or A* pathfinding.")]
|
||||||
|
public float SpawnSpread;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -336,9 +336,12 @@ namespace TD.Gameplay
|
||||||
{
|
{
|
||||||
if (entry.EnemyType == null || entry.Count <= 0) continue;
|
if (entry.EnemyType == null || entry.Count <= 0) continue;
|
||||||
|
|
||||||
for (int i = 0; i < entry.Count; i++)
|
int chunkSize = Mathf.Max(1, entry.ChunkSize);
|
||||||
|
for (int i = 0; i < entry.Count; i += chunkSize)
|
||||||
{
|
{
|
||||||
SpawnEnemyInAllZones(entry.EnemyType);
|
int burst = Mathf.Min(chunkSize, entry.Count - i);
|
||||||
|
for (int j = 0; j < burst; j++)
|
||||||
|
SpawnEnemyInAllZones(entry.EnemyType, entry.SpawnSpread);
|
||||||
|
|
||||||
if (entry.SpawnInterval > 0f)
|
if (entry.SpawnInterval > 0f)
|
||||||
yield return new WaitForSeconds(entry.SpawnInterval);
|
yield return new WaitForSeconds(entry.SpawnInterval);
|
||||||
|
|
@ -355,7 +358,7 @@ namespace TD.Gameplay
|
||||||
|
|
||||||
// ----- Spawn helpers ----------------------------------------------
|
// ----- Spawn helpers ----------------------------------------------
|
||||||
|
|
||||||
private void SpawnEnemyInAllZones(EnemyDefinition def)
|
private void SpawnEnemyInAllZones(EnemyDefinition def, float spawnSpread = 0f)
|
||||||
{
|
{
|
||||||
var loader = LevelLoader.Instance;
|
var loader = LevelLoader.Instance;
|
||||||
if (loader?.LevelData?.PlayerZones == null) return;
|
if (loader?.LevelData?.PlayerZones == null) return;
|
||||||
|
|
@ -376,11 +379,12 @@ namespace TD.Gameplay
|
||||||
// this enemy for leak attribution — can't be derived from the spawner
|
// this enemy for leak attribution — can't be derived from the spawner
|
||||||
// tile's owner-grid entry because SpawnerVolume sits outside
|
// tile's owner-grid entry because SpawnerVolume sits outside
|
||||||
// PlayerZoneVolume (so OwnerGrid[spawnerTile] = None).
|
// PlayerZoneVolume (so OwnerGrid[spawnerTile] = None).
|
||||||
SpawnEnemy(def, zone.Spawners[0].TilePosition, zone.Owner);
|
SpawnEnemy(def, zone.Spawners[0].TilePosition, zone.Owner, spawnSpread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SpawnEnemy(EnemyDefinition def, Vector2Int spawnerTile, PlayerSlot ownerSlot)
|
private void SpawnEnemy(EnemyDefinition def, Vector2Int spawnerTile, PlayerSlot ownerSlot,
|
||||||
|
float spawnSpread = 0f)
|
||||||
{
|
{
|
||||||
if (def.EnemyPrefab == null)
|
if (def.EnemyPrefab == null)
|
||||||
{
|
{
|
||||||
|
|
@ -388,10 +392,11 @@ namespace TD.Gameplay
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var go = Instantiate(
|
Vector3 spawnPos = GridCoordinates.GridToWorld(spawnerTile);
|
||||||
def.EnemyPrefab,
|
if (spawnSpread > 0f)
|
||||||
GridCoordinates.GridToWorld(spawnerTile),
|
spawnPos.x += Random.Range(-spawnSpread, spawnSpread);
|
||||||
Quaternion.identity);
|
|
||||||
|
var go = Instantiate(def.EnemyPrefab, spawnPos, Quaternion.identity);
|
||||||
|
|
||||||
var health = go.GetComponent<EnemyHealth>();
|
var health = go.GetComponent<EnemyHealth>();
|
||||||
var movement = go.GetComponent<EnemyMovement>();
|
var movement = go.GetComponent<EnemyMovement>();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue