🎮 UnityDocs

Cómo hacer un spawner de oleadas

Receta para generar enemigos por oleadas: número creciente, retardo entre enemigos, pausa entre oleadas y aviso cuando se limpia una oleada. Con corrutinas y datos configurables.

🍳 RecetaIntermedio✅ RevisadoUnity 6.0 LTSActualizado 2026-07-15
DificultadConceptual Código Matemáticas
Tiempo📖 Lectura 6 min · ⌨️ Práctica 25 min

El objetivo#

Generar enemigos en oleadas: cada oleada trae más enemigos que la anterior, con un pequeño retardo entre cada uno y una pausa entre oleadas. Base de un tower defense, un survival o un modo horda.

La receta#

C#
using System.Collections;
using UnityEngine;

public class SpawnerOleadas : MonoBehaviour
{
    [Header("Qué y dónde")]
    [SerializeField] private GameObject enemigoPrefab;
    [SerializeField] private Transform[] puntosSpawn;

    [Header("Ritmo")]
    [SerializeField] private int enemigosOleada1 = 5;
    [SerializeField] private int aumentoPorOleada = 3;
    [SerializeField] private float tiempoEntreEnemigos = 0.5f;
    [SerializeField] private float tiempoEntreOleadas = 5f;

    private int oleadaActual = 0;

    void Start() => StartCoroutine(GestionarOleadas());

    IEnumerator GestionarOleadas()
    {
        while (true)   // oleadas sin fin (o pon una condición de victoria)
        {
            oleadaActual++;
            int cantidad = enemigosOleada1 + (oleadaActual - 1) * aumentoPorOleada;
            Debug.Log($"Oleada {oleadaActual}: {cantidad} enemigos");

            yield return StartCoroutine(GenerarOleada(cantidad));

            // Pausa antes de la siguiente oleada
            yield return new WaitForSeconds(tiempoEntreOleadas);
        }
    }

    IEnumerator GenerarOleada(int cantidad)
    {
        for (int i = 0; i < cantidad; i++)
        {
            Spawnear();
            yield return new WaitForSeconds(tiempoEntreEnemigos);
        }
    }

    void Spawnear()
    {
        Transform punto = puntosSpawn[Random.Range(0, puntosSpawn.Length)];
        Instantiate(enemigoPrefab, punto.position, punto.rotation);
    }
}

Por qué así#

  • Una corrutina orquesta las oleadas y llama a otra que genera los enemigos con retardo: separar "gestión de oleadas" de "generar enemigos" mantiene el código claro.
  • cantidad crece con la oleada (enemigosOleada1 + (oleada-1) * aumento).
  • Puntos de spawn aleatorios (Random.Range) reparten a los enemigos por el mapa.

Esperar a que se limpie la oleada#

A menudo quieres que la siguiente oleada no empiece hasta eliminar todos los enemigos de la actual. Lleva un contador y espera:

C#
private int enemigosVivos = 0;

void Spawnear()
{
    Transform punto = puntosSpawn[Random.Range(0, puntosSpawn.Length)];
    Instantiate(enemigoPrefab, punto.position, punto.rotation);
    enemigosVivos++;
}

// El enemigo llama a esto al morir (con un evento o referencia al spawner)
public void EnemigoMuerto() => enemigosVivos--;

IEnumerator GestionarOleadas()
{
    while (true)
    {
        oleadaActual++;
        int cantidad = enemigosOleada1 + (oleadaActual - 1) * aumentoPorOleada;
        yield return StartCoroutine(GenerarOleada(cantidad));

        // Espera a limpiar la oleada antes de la pausa
        yield return new WaitUntil(() => enemigosVivos == 0);
        yield return new WaitForSeconds(tiempoEntreOleadas);
    }
}
Avisa con eventos, no con referencias directas

Que cada enemigo conozca al spawner para avisarle acopla las clases. Mejor: un evento "enemigo murió" al que el spawner (y el marcador, y el audio...) se suscriben. El enemigo solo anuncia; no sabe quién escucha.

Rendimiento#

Muchos enemigos: pooling

Si generas y destruyes cientos de enemigos, Instantiate/Destroy constante genera tirones. Sustitúyelo por object pooling: reutiliza enemigos activándolos/desactivándolos. Es el patrón estándar para hordas.

Mejoras habituales#

  • Datos por oleada con ScriptableObjects: define cada oleada (tipos y cantidades) como un asset editable sin tocar código (ver ScriptableObject).
  • Varios tipos de enemigo por oleada.
  • UI: mostrar "Oleada 3" y el número de enemigos restantes.

Errores frecuentes#

  • Bucle while (true) sin ninguna condición de fin ni forma de parar (para el spawner al ganar/perder).
  • Instantiate de golpe en un frame en vez de con retardo (aparecen todos amontonados).
  • No reutilizar enemigos con pooling y provocar tirones en hordas grandes.
  • Acoplar cada enemigo al spawner en vez de usar eventos.

Resumen#

Una corrutina gestiona las oleadas (cantidad creciente, pausa entre ellas) y otra genera los enemigos con retardo en puntos aleatorios. Para encadenar oleadas al limpiar la anterior, lleva un contador y usa WaitUntil. Avisa de las muertes con eventos, usa pooling para hordas grandes y define las oleadas con ScriptableObjects para ajustarlas sin código.

Fuentes y para profundizar