Tengo un algoritmo de generación de nivel que es computacionalmente pesado. Como tal, llamarlo siempre provoca la congelación de la pantalla del juego. ¿Cómo puedo colocar la función en un segundo subproceso mientras el juego sigue mostrando una pantalla de carga para indicar que el juego no está congelado?
unity
multithreading
DarkDestry
fuente
fuente
List
de acciones para almacenar las funciones que desea llamar en el hilo principal. bloquee y copie laAction
lista en laUpdate
función a la lista temporal, borre la lista original y luego ejecute elAction
código enList
el hilo principal. Vea UnityThread de mi otra publicación sobre cómo hacer esto. Por ejemplo, para llamar a una función en el subproceso principal,UnityThread.executeInUpdate(() => { transform.Rotate(new Vector3(0f, 90f, 0f)); });
Respuestas:
Actualización: en 2018, Unity está implementando un sistema de trabajo C # como una forma de descargar el trabajo y utilizar múltiples núcleos de CPU.
La respuesta a continuación es anterior a este sistema. Seguirá funcionando, pero puede haber mejores opciones disponibles en Unity moderno, según sus necesidades. En particular, el sistema de trabajo parece resolver algunas de las limitaciones sobre qué hilos creados manualmente pueden acceder de forma segura, como se describe a continuación. Los desarrolladores que experimentan con el informe de vista previa realizan emisiones de rayos y construyen mallas en paralelo , por ejemplo.
Invitaría a los usuarios con experiencia en el uso de este sistema de trabajo a agregar sus propias respuestas que reflejen el estado actual del motor.
He usado subprocesos para tareas pesadas en Unity en el pasado (generalmente procesamiento de imágenes y geometría), y no es drásticamente diferente que usar subprocesos en otras aplicaciones de C #, con dos advertencias:
Debido a que Unity usa un subconjunto algo más antiguo de .NET, hay algunas características y bibliotecas de subprocesos más nuevas que no podemos usar de fábrica, pero los conceptos básicos están ahí.
Como Almo señala en un comentario anterior, muchos tipos de Unity no son seguros para subprocesos y arrojarán excepciones si intenta construirlos, usarlos o incluso compararlos desde el hilo principal. Cosas a tener en cuenta:
Un caso común es verificar si una referencia de GameObject o Monobehaviour es nula antes de intentar acceder a sus miembros.
myUnityObject == null
llama a un operador sobrecargado para cualquier cosa que descienda de UnityEngine.Object, peroSystem.Object.ReferenceEquals()
trabaja en torno a esto hasta cierto punto; solo recuerde que un GameObject Destroy () ed se compara como igual a nulo usando la sobrecarga, pero aún no es ReferenceEqual a nulo.La lectura de los parámetros de los tipos de Unity generalmente es segura en otro subproceso (en el sentido de que no arrojará una excepción de inmediato siempre y cuando tenga cuidado de verificar los nulos como se indicó anteriormente), pero tenga en cuenta la advertencia de Philipp aquí que el subproceso principal podría estar modificando el estado mientras lo estás leyendo Tendrá que ser disciplinado sobre quién puede modificar qué y cuándo para evitar leer algún estado inconsistente, lo que puede conducir a errores que pueden ser muy difíciles de rastrear porque dependen de tiempos de sub-milisegundos entre hilos que podemos No reproducir a voluntad.
Los miembros estáticos aleatorios y de tiempo no están disponibles. Cree una instancia de System.Random por hilo si necesita aleatoriedad, y System.Diagnostics.Stopwatch si necesita información de sincronización.
Las estructuras Mathf, Vector, Matrix, Quaternion y Color funcionan bien en todos los subprocesos, por lo que puede hacer la mayoría de sus cálculos por separado
Crear GameObjects, adjuntar monobehaviours o crear / actualizar texturas, mallas, materiales, etc., todo debe suceder en el hilo principal. En el pasado, cuando necesitaba trabajar con estos, establecí una cola de productor-consumidor, donde mi hilo de trabajo prepara los datos en bruto (como una gran variedad de vectores / colores para aplicar a una malla o textura), y una actualización o rutina en el hilo principal sondea los datos y los aplica.
Con esas notas fuera del camino, aquí hay un patrón que uso a menudo para el trabajo enhebrado. No garantizo que sea un estilo de mejores prácticas, pero hace el trabajo. (Los comentarios o ediciones para mejorar son bienvenidos; sé que el enhebrado es un tema muy profundo del cual solo conozco los conceptos básicos)
Si no necesitas dividir estrictamente el trabajo entre hilos para obtener velocidad, y solo estás buscando una manera de que no se bloquee para que el resto del juego siga funcionando, una solución más ligera en Unity es Coroutines . Estas son funciones que pueden hacer algo de trabajo y luego devolver el control al motor para continuar lo que está haciendo y reanudar sin problemas en un momento posterior.
Esto no necesita ninguna consideración especial de limpieza, ya que el motor (por lo que puedo decir) elimina las rutinas de los objetos destruidos para usted.
Todo el estado local del método se conserva cuando cede y se reanuda, por lo que para muchos propósitos es como si se estuviera ejecutando sin interrupciones en otro hilo (pero tiene todas las comodidades de ejecutarse en el hilo principal). Solo necesita asegurarse de que cada iteración sea lo suficientemente corta como para que no ralentice su hilo principal sin razón.
Al garantizar que las operaciones importantes no estén separadas por un rendimiento, puede obtener la consistencia del comportamiento de un solo subproceso, sabiendo que ningún otro script o sistema en el hilo principal puede modificar los datos en los que está trabajando.
La línea de retorno de rendimiento le ofrece algunas opciones. Usted puede...
yield return null
reanudar después de la siguiente actualización del marco ()yield return new WaitForFixedUpdate()
reanudar después de la próxima FixedUpdate ()yield return new WaitForSeconds(delay)
reanudar después de que transcurra una cierta cantidad de tiempo de juegoyield return new WaitForEndOfFrame()
reanudar después de que la GUI termine de renderizaryield return myRequest
dondemyRequest
es una instancia WWW , para reanudar una vez que los datos solicitados terminen de cargarse desde la web o el disco.yield return otherCoroutine
dondeotherCoroutine
es una instancia de Coroutine , para reanudar una vezotherCoroutine
completada. Esto a menudo se usa en la formayield return StartCoroutine(OtherCoroutineMethod())
de encadenar la ejecución a una nueva rutina que puede producir cuando lo desee.Experimentalmente, omitir el segundo
StartCoroutine
y simplemente escribiryield return OtherCoroutineMethod()
logra el mismo objetivo si desea encadenar la ejecución en el mismo contexto.Ajustar dentro de un
StartCoroutine
puede ser útil si desea ejecutar la rutina anidada en asociación con un segundo objeto, comoyield return otherObject.StartCoroutine(OtherObjectsCoroutineMethod())
... dependiendo de cuándo quieras que la corutina tome su próximo turno.
O
yield break;
para detener la corutina antes de que llegue al final, de la forma en que podría utilizarlareturn;
antes de un método convencional.fuente
Puede poner su cálculo pesado en otro hilo, pero la API de Unity no es segura para hilos, debe ejecutarlos en el hilo principal.
Bueno, puedes probar este paquete en Asset Store que te ayudará a usar el enhebrado más fácilmente. http://u3d.as/wQg Simplemente puede usar una sola línea de código para iniciar un hilo y ejecutar Unity API de forma segura.
fuente
Con Svelto.Tasks, puede devolver el resultado de una rutina de subprocesos múltiples al subproceso principal (y, por lo tanto, las funciones de la unidad) con bastante facilidad:
http://www.sebaslab.com/svelto-taskrunner-run-serial-and-parallel-asynchronous-tasks-in-unity3d/
fuente
@DMGregory lo explicó muy bien.
Se pueden usar tanto hilos como corutinas. Primero para descargar el hilo principal y más tarde para devolver el control al hilo principal. Empujar el trabajo pesado al hilo separado parece más razonable. Por lo tanto, las colas de trabajo pueden ser lo que busca.
Hay un buen script de ejemplo de JobQueue en Unity Wiki.
fuente