Iniciar sesión

Ver la versión completa : Unity Instantiate usando foreach con múltiples GameObject no funciona



Anarchy
06/01/2016, 12:01
Buenas!

A ver si alguno puede ayudarme con este caso, porque me tiene un poco confuso.

Uso un Instantiate para crear una explosión en los GameObject dependiendo de ciertos factores (por ejemplo, al chocar con una pared, o al aparecer el enemigo final en determinadas fases).
Me funciona perfecto al hacerlo directamente sobre un GameObject en particular, normalmente al hacer un OnCollisionEnter, o incluso al finalizar una animación, mediante una llamada a una corutina, pero no tengo cojones de que me funcione con grupos de GameObjects. Por ejemplo, estoy haciendo que al entrar en un trigger, se destruyan automáticamente un grupo de enemigos, y quiero que todos explosionen al hacerlo.

El proceso que estoy usando:



public GameObject Explosion; //(El prefrab con la explosión)
private GameObject[] pajaros;
private GameObject objeto; //(Este lo estoy usando como último recurso para hacer pruebas, originalmente no lo utilizaba).

void Start () {
pajaros = GameObject.FindGameObjectsWithTag("Pajaro");
}

void OnTriggerEnter2D(Collider2D other)
{
foreach (GameObject pajaro in pajaros)
{
objeto= Instantiate(Explosion, new Vector3(pajaro.transform.position.x, pajaro.transform.position.y, -1), pajaro.transform.rotation) as GameObject; //-1 simplemente para asegurarme de que la explosión aparezca por delante de los pájaros
objeto.transform.localScale = new Vector3(2, 2, 0); //No lo necesito, pero estoy haciendo pruebas al no funcionarme. Originalmente lanzaba el Instantitate sin usar el GameObject "objeto"
StartCoroutine(destruyePajaro(pajaro)); //La rutina que se carga los pájaros tras la explosión, esta se lanza sin problemas
}
}


Pero no hay forma de que funcione. Los pájaros se destruyen, pero no se lanza la animación de la explosión en ninguno de ellos.
¿Alguna razón para que esto suceda?

¡Un saludo y gracias!
Anarchy

swapd0
06/01/2016, 13:32
Porque solo tienes una variable objeto (y no un array) y al ir asignando cada objeto del array pajaros se destruye el objeto apuntado anteriormente (explosion).

Que conste que no tengo NPI de Unity pero supongo que pasaría eso si estuvieras usando java o algo así, con C++ tendrías memory leaks.

-----Actualizado-----

Y viendo los comentarios, supongo que al crear objetos dentro del bucle, al salir de este se destruyen por no estar asignados a nadie.

Anarchy
06/01/2016, 13:56
Yesss, creo que en eso tienes toda la razón, es lo que venía pensando en el coche.
PERO, debo decir que la variable objeto la he añadido al final para hacer pruebas. Antes hacía el Instantiate directamente sobre cada objeto del array pajaros, sin utilizar la variable objeto, y el problema era el mismo. Añadí la variable objeto al final para hacer unas pruebas, y lo dejé así cuando hice la consulta (ahora la tengo quitada).
Es decir, en lugar de

objeto= Instantiate(Explosion, new Vector3(pajaro.transform.position.x, pajaro.transform.position.y, -1), pajaro.transform.rotation) as GameObject;
Estoy usando:

Instantiate(Explosion, new Vector3(pajaro.transform.position.x, pajaro.transform.position.y, -1), pajaro.transform.rotation);
Esto me funciona sin problemas generando varios Instantiate consecutivos al tener varias colisiones, pero sólo sobre un único objeto, no si hago un foreach con un array de objetos.
El nuevo objeto que se crea se destruye automáticamente al finalizar su animación.

-----Actualizado-----


Que conste que no tengo NPI de Unity pero supongo que pasaría eso si estuvieras usando java o algo así, con C++ tendrías memory leaks.Estoy usando C#.

swapd0
06/01/2016, 16:20
Prueba quitar el foreach y poner varios Instantiate(Explosion, new ...) seguidos, si eso funciona es porque al salir del bloque foreach se borran los objetos que se han creado dentro (y supongo) que no se han asignados a ninguna variable.

Yo soy de C++, de C# he tocado lo justo para saber que no me gusta. XD

-----Actualizado-----

Lo digo porque si en C++ haces esto:


for (...)
{
Objeto x;
...
}

En cada iteracion del bucle se crea y se destruye el objeto x.

Eskema
06/01/2016, 16:31
Si estas llamando a destroy se destruye el gameobject que sea, y se para cualquier tipo de actividad que tenga asociada. Asi que en ese caso al destruirse no se mostraria nada mas.

A bote pronto no veo mas error.... solo tener claro que al llamar a instantiate lo haces desde ese script usando el objeto donde esta puesto como padre

Anarchy
06/01/2016, 17:28
Sí, por eso el script que llama al Instantiate no depende de los pájaros. Es un objeto con un trigger el que lanza el script y los pájaros están en otro grupo de objetos diferente (el de enemigos), pero cada uno es independiente del otro.
Probaré a desactivar el spriterenderer y el collider de cada uno en lugar de destruirlos (o de desactivarlos por completo), a ver qué pasa.

También probaré a hacer los Instantiate separados, pero es que no tengo porqué conocer cuantos pájaros hay en escena, por eso uso el foreach. Estoy programándolo para poder crear bandadas de pájaros al azar más adelante y luego destruirlos automáticamente al realizar cierta acción.

Jurk
06/01/2016, 20:22
Por que no hacer que la wxplosion sea otro objeto, y lanzanrlo en el onDestroyEnter o lo que sea?

Anarchy
06/01/2016, 21:18
La explosión ya es otro objeto (un prefab con una animación). Lo que hago es un intantiate (que viene a ser "clonarlo") sobre el objeto pájaro, para que se muestre por delante, e inmediatamente después de finalizar, destruyo el objeto pájaro.
Esto me funciona perfectamente sobre el personaje y sobre los jefes finales. Por eso me mosquea, porque en realidad es algo que me funciona sin problemas en muchos casos (al morir, al chocar a cierta velocidad, al aparecer el enemigo, al desaparecer, etc...), pero me falla al realizarlo sobre múltiples objetos usando el foreach.

chipan
06/01/2016, 22:19
Por mi experiencia con div-fenix-bennu, parece que te está ocurriendo lo mismo que me pasaba a mi cuando mataba un proceso con s_kill; y es que se eliminaba del todo, se dejaba de ejecutar su código y con ello todas las acciones derivadas (explosiones, sonidos, etc).
Si no encuentras otra solución podrías utilizar "la solución guarra"; crear una variable que sea accesible desde todas partes o al menos que sea accesible por todos los pájaros y demás implicados y poner una condición en el código de los pájaros en la que cuando la variable sea 1 el pajaro explote y desaparezca. entonces cuando quieras que mueran solo necesitas poner la variable a 1.

pakoito
06/01/2016, 22:40
¿Estás modificando una lista dentro de un iterator? en Java es una operación ilegal.

Anarchy
06/01/2016, 22:50
Por mi experiencia con div-fenix-bennu, parece que te está ocurriendo lo mismo que me pasaba a mi cuando mataba un proceso con s_kill; y es que se eliminaba del todo, se dejaba de ejecutar su código y con ello todas las acciones derivadas (explosiones, sonidos, etc).
Si no encuentras otra solución podrías utilizar "la solución guarra"; crear una variable que sea accesible desde todas partes o al menos que sea accesible por todos los pájaros y demás implicados y poner una condición en el código de los pájaros en la que cuando la variable sea 1 el pajaro explote y desaparezca. entonces cuando quieras que mueran solo necesitas poner la variable a 1.

Eso hago en otros casos. Unity te lo pone fácil, ya que en los Mechanism o animaciones permite poner condicionales para cambiar las rutinas de la animación. Luego desde el script le pasas el valor de la condicional al "Mechanism" y este actúa en consecuencia según lo hayas indicado. Pero es que me jode tener que hacer eso con algo que pensaba que solucionaría con 2 líneas de código.

chipan
07/01/2016, 04:34
¿Y no puede ser que al matar los pájaros las explosiones no hereden la posición indicada y ocurran fuera de pantalla?

DarkDijkstra
07/01/2016, 09:55
Por pegar así un tiro al aire...
¿Has probado a cambiar el foreach por un bucle for de toda la vida?

Igualmente, después de cada Instantiate, prueba a usar un Debug.Log() con algo de info sobre el nuevo objeto... Si pruebas el juego en el editor de Unity, ves los nuevos objetos en la lista de GameObjects?

IronArthur
07/01/2016, 09:56
¿Estás modificando una lista dentro de un iterator? en Java es una operación ilegal.

En c# tb.

BTW, no uses foreach en unity. Tiene un bug que por culpa de mono y funciona mal.

Salu2

notbad
07/01/2016, 10:01
En c# tb.

BTW, no uses foreach en unity. Tiene un bug que por culpa de mono y funciona mal.

Salu2
Hombre, tanto como un bug no es. Es una implementación y ya está. Seguramente para otras cosas que no sea juegos no pasará nada ni la gente se habrá percatado de que cree basura a tutiplen.

Un saludo.

IronArthur
07/01/2016, 10:18
Hombre, tanto como un bug no es. Es una implementación y ya está. Seguramente para otras cosas que no sea juegos no pasará nada ni la gente se habrá percatado de que cree basura a tutiplen.

Un saludo.

Hombre si en el otro hilo Eskema se quejaba que una corutina gastaba mucha memoria, lo del foreach es un apaga y vamonos. pq depende de como lo estes haciendo ahoga el juego que no veas. En mi caso en un proyecto fue quitar los foreach y pasar de tener outofmemorys de vez en cuando a ir pasable.

Salu2

Eskema
07/01/2016, 10:34
Yo no he dicho nada porque me trae sin cuidado y paso 3 pueblos de "ayudar" a la gente, mis consejos estan en mi canal de youtube donde tengo un video de optimizacion y buenas practicas. El que lo quiera ver y aplicarlas pues estupendo para él, todo eso que se ganará, él que no pues que siga como va xDD
Total hoy dia nadie se preocupa de nada, siempre hay recursos de sobra o siempre esta la misma excusa, "es que es un proyecto pequeño y asi va bien" xDDD

Anarchy
07/01/2016, 11:47
Mira, no sabía lo del foreach. Haré limpieza y cambiaré las funciones para evitarlo.

Anarchy
07/01/2016, 11:48
Las corutinas estoy tratando de limitarlas después de lo que comentó eskema, pero hice bastante uso de ellas al comienzo del proyecto y no se si me vale la pena andar cambiando el código. Este juego lo estoy usando para aprender Unity y hacerme al c#, y creo que me está sirviendo de sobra. :D

notbad
07/01/2016, 12:01
Las corutinas estoy tratando de limitarlas después de lo que comentó eskema, pero hice bastante uso de ellas al comienzo del proyecto y no se si me vale la pena andar cambiando el código. Este juego lo estoy usando para aprender Unity y hacerme al c#, y creo que me está sirviendo de sobra. :D

las coroutinas te saldrán a cuenta quitarlas si las estás usando (disparandolas) en metodos que se ejecutan muy frecuentemente como update, late update, fixedupdate, etc... Si no, pues no vas a notar casi nada. De todas formas si está en medio del desarrollo yo no me pondría a optimizar nada, como mucho a iterar sobre el código para hacerlo más mantenible y entendible.

La gente que sabe de esto suele comentar que ... early optimizations == bullet hell :D, por algo será.

Un saludo.

Anarchy
07/01/2016, 12:04
las coroutinas te saldrán a cuenta quitarlas si las estás usando (disparandolas) en metodos que se ejecutan muy frecuentemente como update, late update, fixedupdate, etc... Si no, pues no vas a notar casi nada.
No se me ocurriría usarlas en el update ni aunque no me hubieran dicho que consumen memoria. Las uso en momentos muy puntuales. Desde luego no he tenido problemas de rendimiento ni de memoria probando el juego en móviles súper cutres de gama bajísima, así que entiendo que tampoco estoy haciendo un uso excesivo de las mismas.

notbad
07/01/2016, 14:52
No se me ocurriría usarlas en el update ni aunque no me hubieran dicho que consumen memoria. Las uso en momentos muy puntuales. Desde luego no he tenido problemas de rendimiento ni de memoria probando el juego en móviles súper cutres de gama bajísima, así que entiendo que tampoco estoy haciendo un uso excesivo de las mismas.

No le des más vueltas. Dejalas donde están e invierte el tema en cosas más importantes como terminar el juego. Al final cuando todo esté funcionando ya coges el profiler y miras que movidas están relamente haciendo sufrir el frame rate del juego.

Un saludo.

chipan
07/01/2016, 22:09
No le des más vueltas. Dejalas donde están e invierte el tema en cosas más importantes como terminar el juego. Al final cuando todo esté funcionando ya coges el profiler y miras que movidas están relamente haciendo sufrir el frame rate del juego.

Un saludo.

Exactamente, es un juego con pajaros que explotan asi que tiene que ser bueno XD.

Anarchy
07/01/2016, 23:54
Y gusanos gigantes que te persiguen mientras escalas plataformas! xDDD

chipan
08/01/2016, 01:09
Y gusanos gigantes que te persiguen mientras escalas plataformas! xDDD

¡Mejor me lo pones!

Drumpi
09/01/2016, 18:18
Así sin probarlo, yo como lo programaría sería así:


public GameObject Explosion; //(El prefrab con la explosión)
private GameObject[] pajaros;
private GameObject objeto; //(Este lo estoy usando como último recurso para hacer pruebas, originalmente no lo utilizaba).

void Start () {
pajaros = GameObject.FindGameObjectsWithTag("Pajaro");
}

void OnTriggerEnter2D(Collider2D other)
{
foreach (GameObject pajaro in pajaros)
{
objeto= (GameObject) Instantiate(Explosion, new Vector3(pajaro.transform.position.x, pajaro.transform.position.y, -1), pajaro.transform.rotation); //-1 simplemente para asegurarme de que la explosión aparezca por delante de los pájaros
objeto.transform.localScale = new Vector3(2, 2, 0); //No lo necesito, pero estoy haciendo pruebas al no funcionarme. Originalmente lanzaba el Instantitate sin usar el GameObject "objeto"
StartCoroutine(destruyePajaro(pajaro)); //La rutina que se carga los pájaros tras la explosión, esta se lanza sin problemas
}
}

Ya no sé lo que hace la corrutina, pero no recomiendo usarlas SALVO para tareas que ocurren en diferentes frames (generalmente para esperar tiempos, hacer una animación "a mano", y siempre desactivando el propio script en la corrutina para que no ejecute código, la activas al terminar la corrutina o llamando a un método del propio script; sí, puedes ejecutar métodos del script aunque este esté deshabilitado, muy útil para usar con OnTriggerEnter).

Según he entendido, la filosofía de Unity te pide que sea el propio pájaro el que genere la explosión al morir, pero en mi experiencia, lo mejor que puedes hacer es que este script de control llame a un método del pájaro que se llame "morir" o algo así, y que haga dos cosas: instanciar la explosión, y autodestruirse (no uses el método OnDestroy, generaría la explosión al salir de la escena).
Usar grandes códigos que controlan varios elementos parece que va en contra de la filosofía de Unity, que es lo opuesto a lo que nos enseñan en programación, pero te tienes que hacer a la idea que lo que controla el juego es el editor gráfico, y los scripts son pequeñas instrucciones para que los objetos actúen por su cuenta. Otra cosa es que quieras mantener tu estilo, cada uno lo hace como le resulta más cómodo.