Domina un patrón robusto para enemigos en Unity: colisiones que activan el giro, daño configurable al jugador y control del escenario con prefabs. Con una lógica clara en OnTriggerEnter2D y el uso correcto de capas y colliders, el enemigo queda contenido entre rocas y se mueve con un efecto visual suave gracias a la animación en FixedUpdate.
¿Cómo configurar rocas, capas y prefabs para contener al enemigo?
Coloca un bloque de nivel desde los prefabs y sitúa rocas en el suelo para delimitar la zona. No hace falta etiquetarlas: la capa suelo ya evita que el personaje atraviese el escenario. Si quieres identificarlas, puedes usar etiquetas como rock o escenario, pero no es necesario.
Usa un bloque de nivel base y sitúalo en 0,0,0.
Arrastra el enemigo dentro del bloque para que el cambio quede en el prefab.
Añade una roca y ajusta su tamaño y collider para que el personaje pueda saltar sobre ella.
Clona una segunda roca y coloca al enemigo entre ambas: rebotará de una a otra.
Aplica los cambios al prefab y elimina la instancia en escena si tienes generación automática de niveles.
Resultado esperado: el enemigo queda “domesticado”, rebotando entre rocas y sin escapar del área diseñada por el game designer.
¿Cómo detectar colisiones y depurar con OnTriggerEnter2D?
El enemigo tiene su propio collider (por ejemplo, un Capsule Collider) marcado como isTrigger. Así, cuando entra en contacto con otro collider, Unity llama a OnTriggerEnter2D. Para comprobar con qué choca, usa un registro simple del tag en consola.
¿Qué comprobar con Debug.Log?
Ver etiquetas como: untagged, Player, Coin.
Ajustar temporalmente la velocidad del jugador para observar mejor la consola.
Idea clave: en función de collision.tag decides la acción. Con monedas no haces nada; con el jugador aplicas daño; con escenario u otros, giras al enemigo.
¿Cómo aplicar daño al jugador y provocar el giro automático?
Gestiona la lógica por casos. Si la etiqueta es Coin, sales del método. Si es Player, localizas su componente y llamas a CollectHealth con un valor negativo. Si no es ninguna de las anteriores, cambias la dirección del enemigo invirtiendo la variable facingRight.
¿Cómo definir daño configurable desde el editor?
Expón un entero público enemyDamage.
Evita “hardcodear” valores: podrás crear enemigos con daños distintos.
publicint enemyDamage =10;voidOnTriggerEnter2D(Collider2D collision){// Moneda: no reaccionar.if(collision.tag =="coin")return;// Jugador: aplicar daño.if(collision.tag =="Player"){var player = collision.gameObject.GetComponent<PlayerController>();if(player !=null){ player.CollectHealth(-enemyDamage);}return;}// Escenario u otros: girar. facingRight =!facingRight;}
¿Cómo se logra el efecto visual del giro?
Cambias solo el estado: facingRight = !facingRight.
La rotación real la hace el método FixedUpdate ya implementado, generando un giro suave.
Comportamiento logrado: el enemigo choca con la roca, da la vuelta y vuelve. Al pasar por detrás de las monedas, no las afecta. Se crea un sistema de rutas simple con colisiones, alternativa directa a usar waypoints.
—
Habilidades y conceptos puestos en práctica:
Configuración de prefabs y colocación de elementos de escenario.
Uso de capas frente a etiquetas para colisión del suelo.
Manejo de colliders con isTrigger y método OnTriggerEnter2D.
Depuración con Debug.Log y lectura de collision.tag.
Parámetros configurables en el editor: enemyDamage como int.
Interacción con el jugador vía GetComponent<PlayerController> y CollectHealth con valores negativos.
Control de dirección con bandera booleana facingRight y animación en FixedUpdate.
Rutas por colisión frente a waypoints.
Nota práctica: queda por ajustar la altura de colisión para evitar que el collider se quede “enganchado” y gestionar la muerte del personaje en una mejora posterior.
¿Te gustaría comentar cómo limitas tú las rutas de los enemigos o qué otras etiquetas/capas te funcionan mejor en Unity?
tengo un problema con el prefab del enemigo, es que cuando uno el prefab del Level_Block_0 con el del enemigo y le doy guardar cambios psss a la final no me guarda correctamente porque le doy al simulador y el enemigo me sale en una cordenada distinta a la que le coloque, pense que era problema del prefab del enemigo asi que la borre y la hice denuevo pero a la final vuelve a pasar D= que hago ???
Yo quite el codigo del start y sirvio
Gracias ahora si me funciona bien =D
Un detalle que no se mencionó, me di cuenta en mi juego que por como está programado, los enemigos rebotan con las Exit Zone, por lo que tuve que agregar un nuevo Tag para éstas y en el código las coloqué de la siguiente manera:
if(collision.tag=="Coin"|| collision.tag=="ExitZone"){return;}```
Con eso evitamos el WTF que en el juego veamos al enemigo cambiar de sentido porque si.
En el título dice fire en vez de gire, se entiende pero sugiero cambiarlo :)
4 meses y contando... xD
fire fire fire
Porque es necesario poner un return al final de un triggerenter? que ganamos con eso?
Puedes echarle un ojo a esta documentación seguro te ayuda con lo que buscas.
Return nos permite indicarle a la ejecución del código que puede terminar la función en el lugar donde se invoca.
Por ejemplo, como al inicio del código validamos si chocamos con una moneda, en caso de ser cierto, llamamos return, para que termine allí y no continúe validando si fue el player o otro GameObject.
Si no fue una moneda, no llamara return y continuara con el resto del código, si fue el player, ejecutara la linea donde restamos su vida y al llegar al return, también terminará allí, pues no es necesario que valide más.
Si te fijas al final, deja la linea que cambia la orientación del enemigo y no es necesario poner return, pues ya llego a la línea final de la función OnTrigger.
Personalmente preferí esta forma a la del profesor ya que aquí específicamente realiza su acción con aquellos que estan en el layer "Ground" (si en algún futuro queremos crear otra etiqueta que no sea coin o player, con el método del profe girarían al chocar por la forma en que se programó. De hecho a algunos ya les giraba con elementos como el exit zone)
Agregué el TAG de "Coin" a las pociones para que pasara por encima en ese caso también, claro otra opcion seria colocarle diferente TAG (o cambiarlo por "collectables"?) para lograr el mismo efecto.
Yo cree un TAG "Potion", ya que creo que uno "Collectable", no quedaba bien porque conceptualmente las pociones no se colectan y sin mas, sino que también se "consumen" por decirlo de alguna manera.
En todo caso algo más genérico iría en layer O.o.
Si a alguien le sirve, yo le agregue 2 condicionales dentro de las condicionales para que según la distancia cambie su movimiento sin necesidad de hacer colisión con algo mas.
Que paso? , falta una clase, donde crea el script enemy?? porfavor arreglenlo, no puedo avanzar así la clase
Tuve un problema, cuando me pega el enemigo me baja la vida, pero al agarrar las pociones no sube. Tampoco puedo usar el super salto.
Hola, al menos en este momento, el orden de esta clase y algunas de las que sigue parece estar mal, en esta clase ya está armado el script del enemigo y no se muestra cuando este de hizo, por lo que revisé y vi que deberíamos ver, por lo menos, las dos próximas clases antes que esta. Por lo que, amenos que corrijan el orden, deberíamos saltearnos esta clase para volver más tarde. Saludos!
Me parece que este capitulo 46 debe ir después del 49?
Que importante es la nomenclatura en C# por favor. Mi enemigo colisionaba con la moneda, revisando el código me di cuenta que en el condicional de la colisión con la moneda, no escribí coin con C mayúscula, por lo tanto mi enemigo pasaba del tema y chocaba con ella.
En todos los lenguajes de programación es así.
no es lo mismo un texto en mayusculas que el mismo texto en minusculas.
Podria haber quedado con un CASE??
Los case se recomiendan para casos donde las opciones están bien acotadas o definidas, ya que si ocurre que el valor evaluado sea una opción no incluida daría lugar a un error grave.
Yo lo hice con "if else" y funcionó igual.
Los cases siempre tienen una acción por defecto, no necesariamente debe ser alguna de las consideradas.