Cómo maximizar el rendimiento del motor LayaAir
Sección 1: Principios básicos de ejecución de código El motor LayaAir admite el desarrollo en tres lenguajes: AS 3, TypeScript y JavaScript. Sin embargo, no importa qué lenguaje de desarrollo se utilice, el código JavaScript finalmente se ejecuta. Todas las imágenes que ve son dibujadas por el motor y la frecuencia de actualización depende del FPS especificado por el desarrollador. Por ejemplo, si la frecuencia de cuadro especificada es 0 FPS, el tiempo de ejecución de cada cuadro durante el tiempo de ejecución es una sexagésima de segundo. entonces el cuadro Cuanto mayor sea la velocidad, más fluida será la experiencia visual, y 0 cuadros es un cuadro completo. Dado que el entorno de ejecución real está en el navegador, el rendimiento también depende de la eficiencia del intérprete de JavaScript. Es posible que la velocidad de fotogramas FPS especificada no se alcance en un intérprete de bajo rendimiento, por lo que esta parte no es algo que los desarrolladores puedan decidir. Podemos lo que hacemos es optimizar al máximo para aumentar la velocidad de fotogramas por FPS en dispositivos de gama baja o navegadores de bajo rendimiento. El motor LayaAir vuelve a dibujar cada cuadro. Al optimizar el rendimiento, además de prestar atención al consumo de CPU causado por la ejecución de código lógico en cada cuadro, también debe prestar atención a la cantidad de instrucciones de dibujo llamadas cada cuadro y la cantidad de envíos de texturas. a la GPU. Sección 2: Prueba de referencia La herramienta de estadísticas de rendimiento integrada en el motor LayaAir se puede utilizar para pruebas de referencia para detectar el rendimiento actual en tiempo real. Los desarrolladores pueden usar la clase laya.utils.Stat para mostrar el panel de estadísticas a través de Stat.show(). El código de escritura específico se muestra en el siguiente ejemplo: ? Yi Er Stat.show(0, 0); //Método de escritura de llamada al panel AS 3 Laya.Stat.show(0, 0); método de escritura Representación de lienzo Estadísticas para: Estadísticas de representación WebGL: Significado de los parámetros estadísticos: FPS: Número de fotogramas renderizados por segundo (cuanto mayor sea el número, mejor). Al renderizar usando lienzo, el campo de descripción muestra FPS (Canvas), y al renderizar usando WebGL, el campo de descripción muestra FPS (WebGL). Sprite: Número de nodos de renderizado (cuanto menor sea el número, mejor). Sprite cuenta todos los nodos de representación (incluidos los contenedores). El tamaño de este número afectará la cantidad de recorridos de nodos del motor, la organización de datos y la representación. DrawCall: DrawCall representa diferentes significados bajo el lienzo y la representación WebGL (cuanto menos, mejor). En Lienzo, indica la cantidad de dibujos por cuadro, incluidas imágenes, texto y gráficos vectoriales. Intente limitarlo a menos de 00. WebGL representa un lote de envío de renderizado. El proceso de preparar datos y notificar a la GPU para el renderizado cada vez se llama DrawCall secuencial. Además de notificar a la GPU del renderizado en cada DrawCall secuencial, cambiar materiales y sombreadores también requiere mucho tiempo. . El número de DrawCalls es un indicador importante que determina el rendimiento. Intente limitarlo a menos de 00. Lienzo: tres valores: la cantidad de lienzos redibujados por cuadro / la cantidad de lienzos cuyo tipo de caché es de tipo normal / la cantidad de lienzos cuyo tipo de caché es de mapa de bits. CurMem: solo para renderizado WebGL, indica el uso de memoria y memoria de video (cuanto menor, mejor). Shader: solo para renderizado WebGL, que indica la cantidad de envíos de Shader por cuadro. Ya sea en modo Canvas o en modo WebGL, debemos centrarnos en los tres parámetros de DrawCall, Sprite y Canvas, y luego optimizarlos en consecuencia. (Consulte Rendimiento de representación de gráficos) Sección 3: Optimización de la memoria Grupo de objetos El grupo de objetos implica la reutilización continua de objetos. Una cierta cantidad de objetos se crea durante la inicialización de la aplicación y se almacena en un grupo. Cuando haya terminado de trabajar en un objeto, vuelva a colocar el objeto en el grupo donde podrá recuperarse cuando se necesite un nuevo objeto. Dado que la creación de instancias de objetos es costosa, el uso de un grupo de objetos para reutilizar objetos reduce la necesidad de crear instancias de objetos. También puede reducir las posibilidades de que se ejecute el recolector de basura, aumentando así la velocidad del programa.
El siguiente código demuestra el uso de Laya.utils.Pool: ? Yi 234 5 Lu Qi Ba 9 Yi 0 Yiyi Yi 2 ar SPRITE_SIGN = 'spriteSign'; var sprites = []; ; i lt; por 000; i) { var sp = Pool.getItemByClass(SPRITE_SIGN, Sprite) sprites.push(sp); Laya.stage.addChild(sp); piscina. El siguiente código eliminará todos los objetos de visualización en la lista de visualización cuando se haga clic con el mouse y reutilizará estos objetos en otras tareas en el futuro: click", this, function() { var sp; for(var i = 0, len = sprites .length; i lt; i) { sp = sprites.pop(); Pool.recover(SPRITE_SIGN, sp); será reciclado a la piscina. Uso de Handler.create Durante el proceso de desarrollo, Handler se utiliza a menudo para completar devoluciones de llamadas asincrónicas. Handler.create utiliza la gestión integrada del grupo de objetos, por lo que Handler.create debe usarse para crear controladores de devolución de llamada cuando se utilizan objetos Handler. El siguiente código utiliza Handler.create para crear un controlador de devolución de llamada cargado: Según Laya.loader.load(urls, Handler.create(this, onAssetLoaded)); se ejecuta la devolución de llamada. En este punto, considere lo que sucederá con el siguiente código: Según Laya.loader.load(urls, Handler.create(this, onAssetLoaded), Handler.create(this, onLoading)); .create El controlador devuelto maneja el evento de progreso. La devolución de llamada en este momento es reciclada por el grupo de objetos después de ejecutarse una vez, por lo que el evento de progreso solo se activa una vez. En este momento, cuatro parámetros nombrados una vez deben establecerse en falso: según Laya.loader.load(urls). , Handler.create( this, onAssetLoaded), Handler.create(this, onLoading, null, false)); el recolector de basura no se puede iniciar cuando JavaScript se está ejecutando para liberar memoria. Para garantizar que un objeto se pueda reciclar, elimine todas las referencias al objeto. La destrucción proporcionada por Sprite ayudará a establecer la referencia interna en nula.
Por ejemplo, el siguiente código garantiza que el objeto se pueda recolectar como basura: ? var sp = new Sprite(); sp.destroy(); cuando un objeto se establece en nulo, no se elimina inmediatamente de la memoria. El recolector de basura solo se ejecutará cuando el sistema crea que la memoria es lo suficientemente baja. La asignación de memoria (no la eliminación de objetos) desencadena la recolección de basura. Los períodos de recolección de basura pueden consumir mucha CPU y afectar el rendimiento. Intente limitar el uso de la recolección de basura reutilizando objetos. Además, establezca las referencias en nulas siempre que sea posible para que el recolector de basura dedique menos tiempo a buscar el objeto. A veces (por ejemplo, dos objetos se refieren entre sí), es imposible establecer ambas referencias en nulas al mismo tiempo. El recolector de basura escaneará los objetos inalcanzables y los borrará, lo que consumirá más rendimiento que el recuento de referencias. Descarga de recursos: muchos recursos siempre se cargan cuando el juego se está ejecutando. Estos recursos deben descargarse a tiempo después de su uso; de lo contrario, permanecerán en la memoria. El siguiente ejemplo muestra cómo comparar el estado del recurso antes y después de descargarlo después de cargarlo: ? Yi2345 Luqiba9 Yi0 Yiyiyi2yi3yi4yi5 Yiluyiqiyiba var activos = []; activos.push("res/apes/monkey0.png"); push("res/apes/monkey2.png"); activos.push("res/apes/monkey2.png"); activos.push ("res/apes/monkeytres.png"); , Handler.create(this, onAssetsLoaded)); función onAssetsLoaded() { for(var i = 0, len = activos.length; i lt; len; i) { var activo = activos[i]; .loader.getRes(asset)); Laya.loader.clearRes(asset); console.log(Laya.loader.getRes(asset) } } En cuanto a filtros y máscaras, intente minimizar el uso de efectos de filtro. Cuando se aplican filtros (BlurFilter y GlowFilter) a un objeto de visualización, se crean dos mapas de bits en la memoria en tiempo de ejecución. Cada mapa de bits tiene el mismo tamaño que el objeto de visualización. El primer mapa de bits se crea como una versión rasterizada del objeto de visualización y luego se usa para generar otro mapa de bits al que se aplica el filtro: Los dos mapas de bits en la memoria cuando se aplica el filtro Cuando se modifica una de las propiedades del filtro o la visualización objeto, ambos mapas de bits en la memoria se actualizan para crear el mapa de bits resultante, que puede ocupar mucha memoria. Además, este proceso implica cálculos de CPU, lo que reducirá el rendimiento cuando se actualice dinámicamente (consulte Rendimiento de representación de gráficos: acerca de cacheA). ColorFiter necesita calcular cada píxel en la representación de Canvas, pero el consumo de GPU en WebGL es insignificante. Como práctica recomendada, siempre que sea posible, utilice mapas de bits creados con herramientas de creación de imágenes para simular filtros. Evitar la creación de mapas de bits dinámicos en tiempo de ejecución puede ayudar a reducir la carga de CPU o GPU. Especialmente una imagen que tiene un filtro aplicado y no está siendo modificada. Sección 4: Rendimiento de renderizado de gráficos Optimización de Sprites Minimice los niveles innecesarios de anidamiento y reduzca la cantidad de Sprites. 2. Los objetos en áreas no visibles deben eliminarse de la lista de visualización o establecerse visible=false tanto como sea posible.
3. Para contenedores con una gran cantidad de contenido estático o contenido que cambia con poca frecuencia (como botones), puede configurar el atributo cacheAs para todo el contenedor, lo que puede reducir en gran medida la cantidad de Sprites y mejorar significativamente el rendimiento. Si hay contenido dinámico, es mejor separarlo del contenido estático para que solo se almacene en caché el contenido estático. 4. En el Panel, los subobjetos directos fuera del área del panel (los subobjetos de los subobjetos no se pueden juzgar) no se renderizarán y los subobjetos más allá del área del panel no se consumirán. Optimice DrawCall. Configurar cachéAs para contenido estático complejo puede reducir en gran medida DrawCall. Hacer un buen uso de cachéAs es la clave para la optimización del juego. 2. Intente asegurarse de que el orden de representación de las imágenes en el mismo atlas esté uno al lado del otro. Si se renderizan diferentes atlas, la cantidad de DrawCalls aumentará. 3. Intente asegurarse de que todos los recursos en el mismo panel utilicen un atlas, lo que puede reducir la cantidad de lotes de envío. Optimización de Canvas Al optimizar Canvas, debemos prestar atención a no utilizar cacheAs en las siguientes situaciones: 1. El objeto es muy simple, como una palabra o una imagen. Configurar cacheAs=bitmap no mejorará el rendimiento, pero lo perderá. 2. Hay contenidos que cambian con frecuencia en el contenedor, como una animación o una cuenta regresiva en el contenedor. Si cacheAs=bitmap se establece en este contenedor, se perderá rendimiento. Puede determinar si la caché de Canvas se está actualizando observando el primer valor de las estadísticas de Canvas. Acerca de cacheAs La configuración de cacheAs puede almacenar en caché el objeto de visualización como una imagen estática. Cuando se usa cacheAs, si el subobjeto cambia, se volverá a almacenar en caché automáticamente. Al mismo tiempo, el método reCache también se puede llamar manualmente para actualizar. cache. Se recomienda almacenar en caché el contenido complejo que no cambia con frecuencia como imágenes estáticas, lo que puede mejorar en gran medida el rendimiento de la caché. Tiene tres valores opcionales: ninguno, normal y mapa de bits. El valor predeterminado es none, que no realiza ningún almacenamiento en caché. 2. Cuando el valor es normal, el almacenamiento en caché del lienzo se realiza en el lienzo y el almacenamiento en caché de comandos se realiza en el modo webgl. 3. Cuando el valor es un mapa de bits, la caché del lienzo todavía se usa en el lienzo y la caché de renderTarget se usa en el modo webGL. Cabe señalar aquí que el modo de caché renderTarget en webGL tiene un límite de tamaño de 204, y exceder 204 aumentará la sobrecarga de memoria adicional. Además, la sobrecarga del redibujado continuo es relativamente alta, pero las llamadas a sorteos se reducirán y el rendimiento de renderizado será el más alto. El modo de caché de comandos en webGL solo reducirá el recorrido de nodos y la organización de comandos, pero no reducirá las llamadas a sorteos y el rendimiento es medio. Después de configurar cacheAs, también puede configurar staticCache = true para evitar la actualización automática del caché y puede llamar manualmente al método reCache para actualizar el caché. cacheAs mejora principalmente el rendimiento de dos maneras. Uno es reducir el recorrido de nodos y el cálculo de vértices; el otro es reducir drawCall. Hacer un buen uso de los cachés será una herramienta poderosa para optimizar el rendimiento del motor.
El siguiente ejemplo dibuja texto 0000: ? 234 5 LUqiba 9 0 yiyiyi 2yi 3yi 4yi 5 yiluyiqibayi 9 Laya.init(550, 400, Laya.WebGL) ; ; var texto; para (var i = 0; i lt; por 0000; i) { texto = new Laya.Text(); text.text = (Math.random() * 00).toFixed(0); color = "#CCCCCC"; text.x = Math.random() * 550; text.y = Math.random() * 00; textBox.addChild(texto); es una captura de pantalla del tiempo de ejecución en la computadora del autor. El FPS es estable en alrededor de 52. Cuando configuramos el contenedor donde se encuentra el texto en caché, como se muestra en el siguiente ejemplo, el rendimiento mejora enormemente y el FPS alcanza 0 fotogramas. ? Según 2 // ...Se omite otro código... var textBox = new Laya.Sprite(); textBox.cacheAs = "bitmap" // ...Se omite otro código... Cuando el trazo de texto es ejecutar, el texto con trazo está configurado para ser mejor que el texto sin trazo. El texto con trazo llama al comando de dibujo una vez más. En este momento, el uso de texto por parte de la CPU es proporcional a la cantidad de texto. Por lo tanto, intente utilizar alternativas para satisfacer las mismas necesidades. Para contenido de texto que apenas cambia, puede utilizar cacheAs para reducir el consumo de rendimiento. Consulte Rendimiento de representación de gráficos: acerca de cacheAs. Para los campos de texto cuyo contenido cambia con frecuencia pero utiliza una pequeña cantidad de caracteres, puede optar por utilizar fuentes de mapa de bits. Omita el formato del texto y renderícelo directamente. En la mayoría de los casos, muchos textos no requieren un formato complejo y simplemente muestran una línea de texto. Para satisfacer esta demanda, Text proporciona un método llamado changeText que puede omitir directamente la composición tipográfica. ? Según 2345 var text = new Text(); text.text = "text"; Laya.stage.addChild(text); //El contenido del texto solo se actualiza más tarde, el uso de changeText puede mejorar el rendimiento text.changeText("text) cambiado."); Text.changeText modificará directamente la última instrucción para dibujar el texto en la instrucción de dibujo. Este comportamiento de la instrucción de dibujo anterior que aún existe hará que changeText solo se use en las siguientes situaciones: El texto siempre tiene solo una línea. El estilo del texto es siempre el mismo (color, peso, cursiva, alineación, etc.). Aun así, estas necesidades todavía se utilizan a menudo en la programación real. Sección 5: Reducir el uso de CPU Reducir la búsqueda de propiedades dinámicas Cualquier objeto en JavaScript es dinámico y puede agregar propiedades a voluntad. Sin embargo, buscar un atributo entre una gran cantidad de atributos puede llevar mucho tiempo.
Si necesita usar un determinado valor de atributo con frecuencia, puede usar una variable local para guardarlo: ? Yi2345 Lu Qiba function foo() { var prop = target.prop // Usar prop proceso Yi(prop); ); proceso tres (prop); } Temporizador LayaAir proporciona dos bucles de temporizador para ejecutar bloques de código. La frecuencia de ejecución de Laya.timer.frameLoop depende de la frecuencia de fotogramas y la velocidad de fotogramas actual se puede ver a través de Stat.FPS. La frecuencia de ejecución de Laya.timer.loop depende del tiempo especificado por el parámetro. Cuando finalice el ciclo de vida de un objeto, recuerde borrar su temporizador interno: según 2345 Lu Laya.timer.frameLoop(this, animateFrameRateBased); Laya.stage.on("click", this, dispose); ) { Laya.timer.clear(this, animateFrameRateBased } Cómo obtener el límite del objeto de visualización En el diseño relativo, a menudo es necesario obtener el límite del objeto de visualización correctamente. Hay muchas formas de obtener los límites del objeto mostrado y es necesario conocer las diferencias. Según. Utilice getBounds/getGraphicBounds. ,? var sp = new Sprite(); sp.graphics.drawRect(0, 0, 00, 00, "#FF0000"); var límites = sp.getGraphicBounds(); Satisface la mayoría de las necesidades, pero debido a que necesita calcular límites, no es adecuado para llamadas frecuentes. 2. Establezca el tamaño automático del contenedor en verdadero. ? var sp = new Sprite(); sp.autoSize = true; sp.graphics.drawRect(0, 0, 00, 00, "#FF0000"); Laya.stage.addChild(sp); el ancho y el alto en tiempo de ejecución. autoSize se volverá a calcular cuando se obtengan el ancho y el alto y cambie el estado de la lista de visualización (autoSize calcula el ancho y el alto a través de getBoudns). Por lo tanto, no es recomendable aplicar autoSize a un contenedor con una gran cantidad de subobjetos. Si se establece el tamaño, el tamaño automático no tendrá efecto. Obtenga el ancho y el alto después de usar loadImage: ? Yi2345 Lu var sp = new Sprite(); sp.loadImage("res/apes/monkey2.png", 0, 0, 0, 0, Handler.create(this, function( ) { console.log(sp.width, sp.height); })); Laya.stage.addChild(sp); loadImage puede obtener el ancho y el alto correctamente solo después de que se activa la función de devolución de llamada.
3. Llame directamente a la configuración de tamaño: ? Yi2345 Luqiba Laya.loader.load("res/apes/monkey2.png", Handler.create(this, function() { var textura = Laya.loader. getRes("res/apes /monkey2.png"); var sp = new Sprite(); sp.graphics.drawTexture(textura, 0, 0); sp.size(textura.ancho, textura.alto); Laya. stage.addChild(sp); })); El uso de Graphics.drawTexture no establecerá automáticamente el ancho y el alto del contenedor, pero puede usar el ancho y el alto de la textura para asignarlo al contenedor. No hace falta decir que esta es la forma más eficaz. Nota: getGraphicsBounds se utiliza para obtener el ancho y el alto del dibujo vectorial. Hay tres modos para cambiar la velocidad de fotogramas según el estado de la actividad. Stage.FRAME_SLOW mantiene el FPS en 30; Stage.FRAME_FAST mantiene el FPS en 0; Stage.FRAME_MOUSE mantiene selectivamente el FPS en 30 o 0 fotogramas. A veces no es necesario que el juego se ejecute a una velocidad de 30 FPS, porque 30 FPS ya pueden satisfacer la respuesta de la visión humana en la mayoría de los casos. Sin embargo, al interactuar con el mouse, 30 FPS pueden causar incoherencia en la imagen, por lo que Stage.FRAME_MOUSE. llegó a existir. El siguiente ejemplo muestra cómo mover el mouse sobre el lienzo a la velocidad de fotogramas de Stage.FRAME_SLOW para que la bola siga el movimiento del mouse: 伊二三四 5 鲁奇巴 9 伊伊伊二 Laya.init(Browser). .ancho, altura del navegador); Stat.show(); Laya.stage.frameRate = Stage.FRAME_SLOW; var sp = new Sprite(); Laya.stage.addChild (sp); Laya.stage.on(Event.MOUSE_MOVE, this, function() { sp.pos(Laya.stage.mouseX, Laya.stage.mouseY); }); muestra tres 0 y el mouse Al moverse, puede sentir que la posición de la pelota se actualiza de manera inconsistente. Establezca Stage.frameRate en Stage.FRAME_MOUSE: ? Según Laya.stage.frameRate = Stage.FRAME_MOUSE; en este momento, después de mover el mouse, el FPS mostrará 0 y se mejorará la suavidad de la pantalla. Después de que el mouse permanezca estacionario durante 2 segundos, FPS volverá a 30 fotogramas. Utilice callLater callLater para retrasar la ejecución del bloque de código hasta antes de que se represente el fotograma actual. Si la operación actual cambia con frecuencia el estado de un objeto, puede considerar usar callLater para reducir los cálculos repetidos.
Considere una figura en la que establecer cualquier propiedad que cambie su apariencia hará que se vuelva a dibujar la figura:二一二二二二三二四二5 二六var rotación = 0, escala =, posición = 0; { this.rotation = valor; actualizar(); } función setScale(valor) { this.scale = valor; } función setPosition(valor) { this.position = valor; { console.log('rotation: ' this.rotation '\tscale: ' this.scale '\tposition: ' position); Llame al siguiente código para cambiar el estado: ? Según setRotation(90); ; setPosition(tres 0); El resultado impreso de la consola es rotación: 90 escala: según posición: 0 rotación: 90 escala: 2 posición: 0 rotación: 90 escala: 2 posición: 3 0 se llamó a la actualización tres veces y el resultado final fue correcto, pero las dos primeras llamadas fueron innecesarias. Intente cambiar las tres actualizaciones a: ? Según Laya.timer.callLater(this, update) En este momento, la actualización solo se llamará una vez y es el resultado que queremos. Carga de imágenes/atlas Después de completar la carga de imágenes/atlas, el motor comenzará a procesar recursos de imágenes. Si se carga una galería de imágenes, se procesará cada subimagen. Si se procesa una gran cantidad de imágenes a la vez, este proceso puede provocar grandes retrasos. En la carga de recursos del juego, se pueden cargar recursos según niveles, escenas, etc. Cuantas menos imágenes tengas que procesar al mismo tiempo, más responsivo será el juego en ese momento. Una vez utilizado el recurso, también se puede descargar para liberar memoria. Sección 6: Otras estrategias de optimización Reduzca la cantidad de partículas utilizadas en el modo Canvas de la plataforma móvil, intente no usar partículas 2. En el modo Canvas, intente reducir el uso de rotación, escala, alfa y otros atributos; lo que consumirá rendimiento. (Se puede usar en modo WebGL); 3. No cree objetos ni cálculos complejos en el bucle de tiempo. 4. Intente reducir el uso de autoSize del contenedor y reduzca el uso de getBounds (), porque estas llamadas generarán más cálculos; 5. Intente utilizar try catch con moderación, ya que la ejecución de las funciones detectadas por try catch será muy lenta