Entiendo el principio de las corutinas. Sé cómo hacer que el estándar StartCoroutine
/ yield return
patrón funcione en C # en Unity, por ejemplo, invocar un método que regresa a IEnumerator
través de StartCoroutine
y en ese método hacer algo, yield return new WaitForSeconds(1);
esperar un segundo y luego hacer otra cosa.
Mi pregunta es: ¿qué está pasando realmente detrás de escena? ¿Qué hace StartCoroutine
realmente? ¿Qué IEnumerator
está WaitForSeconds
volviendo? ¿Cómo StartCoroutine
devuelve el control a la parte "otra cosa" del método llamado? ¿Cómo interactúa todo esto con el modelo de concurrencia de Unity (donde suceden muchas cosas al mismo tiempo sin el uso de corutinas)?
IEnumerator
/IEnumerable
(o los equivalentes genéricos) y que contienen layield
palabra clave. Busque iteradores.Respuestas:
El enlace a menudo detallado de Cority de Unity3D en detalle está muerto. Como se menciona en los comentarios y las respuestas, voy a publicar el contenido del artículo aquí. Este contenido proviene de este espejo .
fuente
El primer encabezado a continuación es una respuesta directa a la pregunta. Los dos títulos siguientes son más útiles para el programador cotidiano.
Detalles de implementación posiblemente aburridos de las rutinas
Las corutinas se explican en Wikipedia y en otros lugares. Aquí solo proporcionaré algunos detalles desde un punto de vista práctico.
IEnumerator
,yield
, Etc., son características del lenguaje C # que se utilizan para algo así como un propósito diferente en la Unidad.En pocas palabras, un
IEnumerator
reclamo de tener una colección de valores que puede solicitar uno por uno, algo así como unList
. En C #, una función con una firma para devolver unIEnumerator
no tiene que crear y devolver uno, pero puede permitir que C # proporcione un implícitoIEnumerator
. La función puede proporcionar el contenido devueltoIEnumerator
en el futuro de manera perezosa, a través deyield return
declaraciones. Cada vez que la persona que llama solicita otro valor de ese implícitoIEnumerator
, la función se ejecuta hasta la siguienteyield return
instrucción, que proporciona el siguiente valor. Como subproducto de esto, la función se detiene hasta que se solicita el siguiente valor.En Unity, no los usamos para proporcionar valores futuros, explotamos el hecho de que la función se detiene. Debido a esta explotación, muchas cosas sobre las corutinas en Unity no tienen sentido (¿Qué
IEnumerator
tiene que ver con algo? ¿Qué esyield
? ¿Por quénew WaitForSeconds(3)
? Etc.). Lo que sucede "bajo el capó" es que los valores que proporciona a través del IEnumerator se utilizanStartCoroutine()
para decidir cuándo solicitar el siguiente valor, lo que determina cuándo su rutina se reanudará nuevamente.Your Unity Game es Single Threaded (*)
Las corutinas no son hilos. Hay un bucle principal de Unity y todas esas funciones que escribes están siendo llamadas por el mismo hilo principal en orden. Puede verificar esto colocando un
while(true);
en cualquiera de sus funciones o rutinas. Congelará todo, incluso el editor de Unity. Esto es evidencia de que todo se ejecuta en un hilo principal. Este enlace que Kay mencionó en su comentario anterior también es un gran recurso.(*) Unity llama a sus funciones desde un hilo. Entonces, a menos que usted mismo cree un hilo, el código que escribió es de un solo hilo. Por supuesto, Unity emplea otros hilos y puede crearlos usted mismo si lo desea.
Una descripción práctica de las rutinas para programadores de juegos
Básicamente, cuando se llama
StartCoroutine(MyCoroutine())
, es exactamente igual que una llamada a la función regular paraMyCoroutine()
, hasta que la primerayield return X
, dondeX
es algo así comonull
,new WaitForSeconds(3)
,StartCoroutine(AnotherCoroutine())
,break
, etc Esto es cuando comienza a ser diferente de una función. Unity "pausa" esa función justo en esayield return X
línea, continúa con otros asuntos y pasan algunos marcos, y cuando llega el momento, Unity reanuda esa función justo después de esa línea. Recuerda los valores para todas las variables locales en la función. De esta manera, puede tener unfor
bucle que se repita cada dos segundos, por ejemplo.Cuándo Unity reanudará tu rutina depende de lo que
X
haya en tuyield return X
. Por ejemplo, si lo usóyield return new WaitForSeconds(3);
, se reanuda después de 3 segundos. Si lo usóyield return StartCoroutine(AnotherCoroutine())
, se reanudará una vez queAnotherCoroutine()
haya terminado por completo, lo que le permite anidar comportamientos a tiempo. Si acaba de utilizar unyield return null;
, se reanuda en el siguiente cuadro.fuente
No podría ser más simple:
La unidad (y todos los motores de juego) están basados en marcos .
Todo el punto, toda la razón de ser de la Unidad, es que está basado en marcos. El motor hace las cosas "cada cuadro" por usted. (Anima, renderiza objetos, hace física, etc.)
Usted podría preguntar ... "Oh, eso es genial. ¿Qué pasa si quiero que el motor haga algo por mí en cada cuadro? ¿Cómo le digo al motor que haga tal o cual cosa en un cuadro?"
La respuesta es ...
Para eso es exactamente una "corutina".
Es así de simple.
Y considera esto ...
Conoces la función "Actualizar". En pocas palabras, todo lo que pones allí se hace en cada cuadro . Literalmente es exactamente lo mismo, sin ninguna diferencia, de la sintaxis de rendimiento de rutina.
No hay absolutamente ninguna diferencia.
Nota al pie: como todos han señalado, Unity simplemente no tiene hilos . Los "marcos" en Unity o en cualquier motor de juego no tienen ninguna conexión con hilos de ninguna manera.
Las rutinas / rendimiento son simplemente cómo accede a los marcos en Unity. Eso es. (Y, de hecho, es absolutamente lo mismo que la función Update () proporcionada por Unity). Eso es todo, es así de simple.
fuente
Update()
? Quiero decir que debería haber al menos una ligera diferencia entre estas dos implementaciones y sus casos de uso, lo cual es bastante obvio.Excavó en esto últimamente, escribió una publicación aquí - http://eppz.eu/blog/understanding-ienumerator-in-unity-3d/ - que arroja una luz sobre las partes internas (con ejemplos de códigos densos), la
IEnumerator
interfaz subyacente , y cómo se usa para las corutinas.fuente
Las funciones básicas en Unity que obtienes automáticamente son la función Start () y la función Update (), por lo que Coroutine son esencialmente funciones como las funciones Start () y Update (). Cualquier función antigua func () se puede llamar de la misma manera que se puede llamar a Coroutine. Obviamente, la unidad ha establecido ciertos límites para las Coroutinas que las hacen diferentes de las funciones normales. Una diferencia es en lugar de
Usted escribe
para corutinas. Y de la misma manera puede controlar el tiempo en funciones normales con líneas de código como
Una corutina tiene un control específico sobre la forma en que se puede controlar el tiempo.
Aunque esto no es lo único que se puede hacer dentro de un IEnumerator / Coroutine, es una de las cosas útiles para las que se utilizan Coroutines. Tendría que investigar la API de secuencias de comandos de Unity para conocer otros usos específicos de Coroutines.
fuente
StartCoroutine es un método para llamar a una función IEnumerator. Es similar a simplemente llamar a una función vacía simple, la diferencia es que la usa en las funciones de IEnumerator. Este tipo de función es única, ya que puede permitirle usar una función de rendimiento especial , tenga en cuenta que debe devolver algo. Eso es lo que yo sé. Aquí escribí un simple juego de parpadeo sobre el método de texto en la unidad
Luego lo llamé desde el IEnumerator
Como puede ver, utilicé el método StartCoroutine (). Espero haber ayudado de alguna manera. Yo también soy un ganador, por lo que si me corrige o me aprecia, cualquier tipo de comentario sería excelente.
fuente