Soy nuevo en Unity. Estaba aprendiendo corutinas y escribí esto.
private void Fire()
{
if(Input.GetButtonDown("Fire1"))
{
StartCoroutine(FireContinuously());
}
if(Input.GetButtonUp("Fire1"))
{
StopAllCoroutines();
}
}
IEnumerator FireContinuously()
{
while(true)
{
GameObject laser = Instantiate(LaserPrefab, transform.position, Quaternion.identity) as GameObject;
laser.GetComponent<Rigidbody2D>().velocity = new Vector2(0, 10f);
yield return new WaitForSeconds(firetime);
}
}
Cuando se presiona el botón, se llama a la rutina y entra en el ciclo 'while'. Cuando dejo el botón, se detiene la rutina. ¿No debería quedar atascado en el ciclo 'while' ya que es un ciclo infinito? ¿Por qué?
unity
c#
coroutines
babybrain
fuente
fuente

"Fire1", ¿es algo que puede configurar en el motor para permitir reasignaciones de teclas en lugar de escribirKeycode.Foo?yieldefectivamente es la abreviatura de "Ceda el control a la persona que llama hasta que se solicite el siguiente elemento en el Enumerable".StopAllCoroutines()en este caso. Está bien cuando solo usas una rutina, pero si alguna vez planeaste tener más de una, esto tendría efectos no deseados. En su lugar, debe usarStopCoroutine()y simplemente detener el que sea relevante en lugar de todos. (StopAllCoroutines()sería útil, por ejemplo, al finalizar el nivel o cargar una nueva área, etc., pero no para cosas específicas como "Ya no estoy disparando".)Respuestas:
La razón es la palabra clave
yieldque tiene un significado específico en C #.Al encontrar las palabras,
yield returnuna función en C # regresa, como cabría esperar.Entonces no hay un bucle infinito. Hay una función / iterador que se puede llamar un número infinito de veces.
La función Unity
StartCoroutine()hace que el marco Unity llame a la función / iterador una vez por cuadro.La función Unity
StopAllCoroutineshace que el marco Unity deje de llamar a la función / iterador.Y al regresar
WaitForSeconds(time)del iterador, el marco de Unity deja de llamar a la función / iteradortime.Un comentario confuso y un voto positivo igualmente confuso sobre ese comentario me animaron a profundizar en lo que
yieldhace y no hace la palabra clave .Si escribes esto:
En su lugar, también puede escribir esto:
De ello se deduce que la palabra clave
yieldno está relacionada con subprocesos múltiples y absolutamente no llamaSystem.Threading.Thread.Yield().fuente
On encountering the words yield return a function in C# returns". No, no lo hace. El texto que usted cita lo explica, al igual que Wikipedia - "In computer science, yield is an action that occurs in a computer program during multithreading, of forcing a processor to relinquish control of the current running thread, and sending it to the end of the running queue, of the same scheduling priority.". Básicamente, "por favor detenme donde estoy y deja que alguien más corra por un tiempo".Cuando se levanta el botón de disparo, se ingresa el segundo if, y se ejecuta StopAllCoroutines. Esto significa que la Corutina en la que se está ejecutando el ciclo while finaliza, por lo que no hay más ciclos infinitos. La corutina es como un contenedor para que el código se ejecute.
Puedo recomendar el Manual de Unity y la API de Unity Scripting para comprender mejor qué son las corutinas y cuán poderosas pueden ser.
Este blog y la búsqueda en la publicación de YouTube también me ayudaron a usar mejor las corutinas.
fuente
Las corutinas son una bestia extraña. El rendimiento de rendimiento hace que el método suspenda la ejecución hasta que luego se escalone. Detrás de escena, podría verse más o menos así:
E interno a Unity / C # (dado que el rendimiento devuelto es una característica nativa de C #), cuando llama a StartCoroutine, crea un
FireContinuouslyDataobjeto y lo pasa al método. Según el valor de retorno, determina cuándo volver a llamarlo más tarde, simplemente almacenando el objeto FireContinuouslyData para pasarlo la próxima vez.Si alguna vez hiciste un corte de rendimiento, podría establecerse internamente
data.shouldBreak = truey luego Unity simplemente tiraría los datos y no volvería a programarlos.Y si hubiera datos que debían guardarse entre ejecuciones, también se almacenarían en los datos para más adelante.
Un ejemplo de cómo Unity / C # podría implementar la funcionalidad de rutina:
fuente
Otra respuesta menciona que está deteniendo las co-rutinas cuando
"Fire1"está activo; esto es completamente correcto, en la medida en que la rutina no continúa creando instancias de GameObjects después de la primera pulsación de"Fire1".Sin embargo, en su caso, este código no se 'quedará atascado' en un bucle infinito, que es lo que parece que está buscando una respuesta, es decir, el
while(true) {}bucle, incluso si no lo detuvo externamente.No se atascará, pero su rutina no terminará (sin llamar
StopCoroutine()oStopAllCoroutines()) tampoco. Esto se debe a que las corutinas de Unity ceden el control a su interlocutor.yielding es diferente areturning:returndeclaración dejará de ejecutar una función, incluso si hay más código siguiéndolayieldinstrucción pausará la función, comenzando en la siguiente línea después deyieldcuando se reanude.Por lo general, las rutinas se reanudarán en cada cuadro, pero también devolverá un
WaitForSecondsobjeto.La línea
yield return new WaitForSeconds(fireTime)se traduce aproximadamente como "ahora suspenderme, y no volver hasta quefireTimehayan pasado los segundos".A menos que se detenga, esta es una rutina que, una vez iniciada, realizará todo el ciclo una vez cada
fireTimesegundo.fuente
Una explicación simple: debajo del capó, Unity está iterando sobre una colección (de YieldInstruction s o nulls o lo que sea que usted
yield returnuse) usando laIEnumeratorfunción que devuelve su función.Como usa la
yieldpalabra clave, su método es un iterador . No es lo de Unity, es una característica del lenguaje C #. ¿Como funciona?Es vago y no genera toda la colección a la vez (y la colección puede ser infinita e imposible de generar a la vez). Los elementos de la colección se generan según sea necesario. Su función devuelve un iterador para que Unity trabaje. Llama a su
MoveNextmétodo para generar un nuevo elemento yCurrentpropiedad para acceder a él.Por lo tanto, su bucle no es interminable, ejecuta un código, devuelve un elemento y devuelve el control a Unity para que no se atasque y pueda hacer otro trabajo, como manejar su entrada para detener la rutina.
fuente
Piensa en cómo
foreachfunciona un :El control sobre la iteración está en la persona que llama; si detiene la iteración (aquí con
break), eso es todo.La
yieldpalabra clave es una forma simple de hacer un enumerable en C #. El nombre sugiere esto:yield returndevuelve el control a la persona que llama (en este caso, nuestraforeach); Es la persona que llama quien decide cuándo continuar con el siguiente elemento. Entonces puedes hacer un método como este:Parece ingenuo que se ejecutará para siempre; pero en realidad, depende completamente de la persona que llama. Puedes hacer algo como esto:
Esto puede ser un poco confuso si no estás acostumbrado a este concepto, pero espero que también sea obvio que esta es una propiedad muy útil. Era la forma más sencilla en la que podía ceder el control a su interlocutor, y cuando la persona que llama decide hacer un seguimiento, solo puede hacer el siguiente paso (si Unity se creó hoy, probablemente lo usaría en
awaitlugar deyield, peroawaitno existía) entonces).Todo lo que necesita para implementar sus propias rutinas (no hace falta decir que las más simples y más estúpidas) es esto:
Para agregar una
WaitForSecondsimplementación muy simple , solo necesita algo como esto:Y el código correspondiente en nuestro bucle principal:
Ta-da: eso es todo lo que necesita un sistema de rutina simple. Y al ceder el control a la persona que llama, la persona que llama puede decidir cualquier cantidad de cosas; podrían tener una tabla de eventos ordenada en lugar de iterar a través de todas las rutinas en cada cuadro; pueden tener prioridades o dependencias. Permite una implementación muy simple de la multitarea cooperativa. Y mira lo simple que es esto, gracias a
yield:)fuente