Advertisement
  1. Code
  2. Games

Construye un Shoot-'Em-Up de Stage3D: Prueba de Sprite

Scroll to top
Read Time: 42 min
This post is part of a series called Shoot-'Em-Up.
Create a Simple Space Shooter Game in HTML5 With EaselJS
Build a Stage3D Shoot-'Em-Up: Interaction

Spanish (Español) translation by Esther (you can also view the original English article)

En esta serie de tutoriales (parte gratuita, parte Premium) crearemos un juego de disparos en 2D de alto rendimiento utilizando el nuevo motor de renderizado Stage3D acelerado por hardware. Aprovecharemos varias técnicas de optimización para conseguir un gran rendimiento en el renderizado de sprites 2D. En esta parte, construiremos una demostración de alto rendimiento que dibuja cientos de sprites en movimiento en la pantalla a la vez.


Avance del resultado final

Echemos un vistazo al resultado final en el que trabajaremos: una demo de sprite 2D de alto rendimiento que utiliza Stage3D con optimizaciones que incluyen una hoja de sprites y la agrupación de objetos.


Introducción: Flash 11 Stage3D

Si quieres llevar tus juegos Flash al siguiente nivel y buscas un montón de efectos visuales y una velocidad de fotogramas increíble, Stage3D va a ser tu nuevo mejor amigo.

La increíble velocidad de la nueva API Stage3D acelerada por hardware de Flash 11 está pidiendo que se utilice para juegos 2D. En lugar de utilizar los anticuados sprites de Flash en la DisplayList o las técnicas de blitting de última generación popularizadas por motores como FlashPunk y Flixel, la nueva generación de juegos en 2D utiliza la potencia de la GPU de tu tarjeta de vídeo para realizar las tareas de renderizado a una velocidad hasta 1000 veces superior a la que podía alcanzar Flash 10.

Aunque lleva 3D en su nombre, esta nueva API también es estupenda para los juegos en 2D. Podemos renderizar geometría simple en forma de cuadrados 2D (llamados quads) y dibujarlos en un plano. Esto nos permitirá renderizar toneladas de sprites en pantalla a unos sedosos 60fps.

Haremos un juego de disparos de desplazamiento lateral inspirado en títulos de arcade retro como R-Type o Gradius en ActionScript utilizando la API Stage3D de Flash 11. No es ni la mitad de difícil de lo que algunos dicen, y no necesitarás aprender los opcodes AGAL en lenguaje ensamblador.

En esta serie de tutoriales de 6 partes, vamos a programar un sencillo juego de disparos en 2D que ofrece un rendimiento de renderizado alucinante. Vamos a construirlo usando AS3 puro, compilado en FlashDevelop (lee más sobre él aquí). FlashDevelop es genial porque es 100% freeware, no necesitas comprar ninguna herramienta cara para tener el mejor IDE de AS3.


Paso 1: Crear un nuevo proyecto

Si aún no lo tienes, asegúrate de descargar e instalar FlashDevelop. Una vez que esté todo configurado (y le hayas permitido instalar la última versión del compilador de Flex automáticamente), enciéndelo e inicia un nuevo "Proyecto AS3".

Create an .AS3 project using FlashDevelopCreate an .AS3 project using FlashDevelopCreate an .AS3 project using FlashDevelop

FlashDevelop creará un proyecto de plantilla en blanco para ti. Vamos a rellenar los espacios en blanco, pieza por pieza, hasta que hayamos creado un juego decente.


Paso 2: Flash de destino 11

Entra en el menú del proyecto y cambia algunas opciones:

  1. Target Flash 11.1
  2. Cambia el tamaño a 600x400px
  3. Cambia el color de fondo a negro
  4. Cambia los FPS a 60
  5. Cambia el nombre del archivo SWF por un nombre de tu elección
Project propertiesProject propertiesProject properties

Paso 3: Importaciones

Ahora que nuestro proyecto en blanco está configurado, vamos a sumergirnos en la codificación. Para empezar, necesitaremos importar toda la funcionalidad de Stage3D requerida. Añade lo siguiente al principio de tu archivo Main.as.


Paso 4: Inicializar Stage3D

El siguiente paso es esperar a que nuestro juego aparezca en el escenario de Flash. Hacer las cosas de esta manera permite el uso futuro de un precargador. Para simplificar, vamos a hacer la mayor parte de nuestro juego en una sola clase pequeña que hereda de la clase Sprite de Flash como sigue.

Después de establecer algunas propiedades específicas del escenario, solicitamos un contexto Stage3D. Esto puede tardar un poco (una fracción de segundo) ya que tu tarjeta de vídeo está configurada para el renderizado por hardware, así que tenemos que esperar al evento onContext3DCreate.

También queremos detectar cualquier error que pueda producirse, especialmente porque el contenido de Stage3D no se ejecuta si el código HTML incrustado que carga tu SWF no incluye el parámetro "wmode=direct". Estos errores también pueden producirse si el usuario está ejecutando una versión antigua de Flash o si no tiene una tarjeta de vídeo capaz de manejar el sombreado de píxeles 2.0.


Paso 5: Manejar cualquier evento

Añade las siguientes funciones que detectan cualquier evento que pueda activarse como se ha especificado anteriormente. En el caso de errores debidos a la ejecución de plugins de Flash antiguos, en futuras versiones de este juego podríamos querer emitir un mensaje y recordar al usuario que se actualice, pero por ahora este error simplemente se ignora.

Para los usuarios con tarjetas de vídeo antiguas (o controladores) que no soportan el modelo de sombreado 2.0, la buena noticia es que Flash 11 es lo suficientemente inteligente como para proporcionar un renderizador de software. No se ejecuta muy rápido, pero al menos todo el mundo podrá jugar a tu juego. Los que tengan equipos de juego decentes obtendrán una velocidad de fotogramas fantástica como nunca antes se había visto en un juego Flash.

El código de manejo de eventos anterior detecta cuando Stage3D está listo para el renderizado por hardware y establece la variable context3D para su uso futuro. Los errores se ignoran por ahora. El evento de redimensionamiento simplemente actualiza el tamaño del escenario y las dimensiones del sistema de renderizado por lotes.


Paso 6: Iniciar el motor de sprites

Una vez recibido el contexto3D, estamos listos para comenzar a ejecutar el juego. Continuando con Main.as, añade lo siguiente.

Esta función crea un motor de renderizado de sprites (que se implementará más adelante) en el escenario, listo para utilizar el tamaño completo de tu archivo flash. A continuación, añadimos el gestor de entidades y el sistema de geometría por lotes (del que hablaremos más adelante). Ahora podemos dar una referencia al gestor de entidades a nuestra clase GUI de estadísticas para que pueda mostrar algunos números en pantalla sobre cuántos sprites se han creado o reutilizado. Por último, empezamos a escuchar el evento ENTER_FRAME, que empezará a disparar a un ritmo de hasta 60 veces por segundo.


Paso 7: Iniciar el bucle de renderización

Ahora que todo ha sido inicializado, ¡estamos listos para jugar! La siguiente función se ejecutará cada fotograma. Para los propósitos de esta primera demostración técnica, vamos a añadir un nuevo sprite en el escenario cada fotograma. Como vamos a implementar un pool de objetos (del que puedes leer más en este tutorial) en lugar de crear inifinitamente nuevos objetos hasta que se nos acabe la RAM, vamos a poder reutilizar las entidades antiguas que se hayan movido fuera de la pantalla.

Después de engendrar otro sprite, limpiamos el área stage3D de la pantalla (poniéndola en negro puro). A continuación actualizamos todas las entidades que están siendo controladas por nuestro gestor de entidades. Esto hará que se muevan un poco más en cada fotograma. Una vez que todos los sprites han sido actualizados, le decimos al sistema de geometría por lotes que los reúna a todos en un gran búfer de vértices y los ponga en pantalla en una sola llamada de dibujo, por eficiencia. Por último, le decimos al context3D que actualice la pantalla con nuestro renderizado final.

¡Eso es todo para los inits! Tan simple como parece, ahora hemos creado un proyecto de plantilla que está listo para lanzar un número insano de sprites. No vamos a utilizar ningún arte vectorial. No vamos a poner ningún sprites de Flash anticuado en el escenario, aparte de la ventana Stage3D y un par de superposiciones de GUI. Todo el trabajo de renderización de nuestros gráficos en el juego va a ser manejado por Stage3D, para que podamos disfrutar de un rendimiento mejorado.


Profundizando: ¿Por qué Stage3D es tan rápido?

Por dos razones:

  1. Utiliza la aceleración por hardware, lo que significa que todos los comandos de dibujo se envían a la GPU 3D de tu tarjeta de vídeo de la misma manera que se renderizan los juegos de XBOX360 y PlayStation3.
  2. Estos comandos de renderizado se procesan en paralelo al resto de tu código ActionScript. Esto significa que una vez que los comandos se envían a tu tarjeta de vídeo, todo el renderizado se realiza al mismo tiempo que se ejecuta el resto del código de tu juego, Flash no tiene que esperar a que terminen. Mientras los píxeles aparecen en la pantalla, Flash se encarga de otras cosas, como manejar la entrada del jugador, reproducir sonidos y actualizar las posiciones de los enemigos.
  3. Dicho esto, muchos motores Stage3D parecen atascarse con unos cientos de sprites. Esto se debe a que han sido programados sin tener en cuenta la sobrecarga que añade cada comando de dibujo. Cuando Stage3D salió por primera vez, algunos de los primeros motores 2D dibujaban todos y cada uno de los sprites individualmente en un bucle gigante (lento e ineficiente). Como este artículo trata de la optimización extrema para un juego 2D de nueva generación con un framerate fabuloso, vamos a implementar un sistema de renderizado extremadamente eficiente que almacena toda la geometría en un gran lote para que podamos dibujar todo en solo uno o dos comandos.


    Cómo ser un Hardcore: ¡Optimizar!

    A los gamedevs más duros les encantan las optimizaciones. Con el fin de hacer estallar la mayor cantidad de sprites en la pantalla con el menor número de cambios de estado (como el cambio de texturas, la selección de un nuevo búfer de vértices, o tener que actualizar la transformación una vez para todos y cada uno de los sprites en la pantalla), vamos a tomar ventaja de las siguientes tres optimizaciones de rendimiento:

    1. agrupación de objetos
    2. spritesheet (atlas de texturas)
    3. geometría de lotes

    Estos tres trucos de gamedev son la clave para conseguir unos FPS increíbles en tu juego. Vamos a implementarlos ahora. Antes de hacerlo, necesitamos crear algunas de las pequeñas clases de las que harán uso estas técnicas.


    Paso 8: La pantalla de estadísticas

    Si vamos a realizar toneladas de optimizaciones y a utilizar Stage3D en un intento de conseguir un rendimiento de renderizado increíblemente rápido, necesitamos una forma de llevar un registro de las estadísticas. Unas pequeñas pruebas de referencia pueden servir para demostrar que lo que estamos haciendo tiene un efecto positivo en la velocidad de fotogramas. Antes de ir más lejos, crea una nueva clase llamada GameGUI.as e implementa una pantalla de FPS y estadísticas super simple como sigue.


    Paso 9: La clase de entidad

    Vamos a implementar una clase gestora de entidades que será el "pool de objetos" descrito anteriormente. Primero tenemos que crear una clase simplista para cada entidad individual en nuestro juego. Esta clase se utilizará para todos los objetos del juego, desde las naves espaciales hasta las balas.

    Crea un nuevo archivo llamado Entity.as y añade unos cuantos getters y setters ahora. Para esta primera demostración técnica, esta clase es simplemente un marcador de posición vacío sin mucha funcionalidad, pero en tutoriales posteriores es donde implementaremos gran parte del juego.


    Paso 10: Hacer una hoja de sprites

    Una importante técnica de optimización que vamos a utilizar es el uso de una hoja de sprites, a veces denominada Atlas de Textura. En lugar de cargar docenas o cientos de imágenes individuales en la memoria RAM de vídeo para utilizarlas durante el renderizado, vamos a hacer una sola imagen que contenga todos los sprites de nuestro juego. De esta manera, podemos utilizar una sola textura para dibujar toneladas de diferentes tipos de enemigos o terrenos.

    El uso de una hoja de sprites se considera una buena práctica por parte de los gamedevs veteranos que necesitan asegurarse de que sus juegos se ejecutan lo más rápido posible. La razón por la que acelera tanto las cosas es muy parecida a la razón por la que vamos a usar la geometría por lotes: en lugar de tener que decirle a la tarjeta de vídeo una y otra vez que use una textura concreta para dibujar un sprite concreto, podemos simplemente decirle que use siempre la misma textura para todas las llamadas de dibujo.

    Esto reduce los "cambios de estado" que son extremadamente costosos en términos de tiempo. Ya no tenemos que decir "tarjeta de vídeo, empieza a usar la textura 24... ahora dibuja el sprite 14" y así sucesivamente. Simplemente decimos "dibujar todo usando esta textura" en una sola pasada. Esto puede aumentar el rendimiento en un orden de magnitud.

    Para nuestro juego de ejemplo utilizaremos una colección de imágenes gratuitas de uso legal del talentoso DanC, que puedes conseguir aquí. Recuerda que si utilizas estas imágenes debes acreditarlas en tu juego de la siguiente manera "Título de la colección de arte" arte de Daniel Cook (Lostgarden.com).

    Utilizando Photoshop (o GIMP, o el editor de imágenes que prefieras), corta y pega los sprites que necesitará tu juego en un único archivo PNG que tenga un fondo transparente. Coloca cada uno de los sprites en una cuadrícula uniforme con un par de píxeles de espacio en blanco entre cada uno. Este pequeño búfer es necesario para evitar cualquier "sangría" de los píxeles de los bordes de los sprites adyacentes que puede ocurrir debido al filtrado bilineal de la textura que ocurre en la GPU. Si cada sprite está tocando al siguiente, tus sprites en el juego pueden tener bordes no deseados donde deberían ser completamente transparentes.

    Por razones de optimización, las GPUs funcionan mejor con imágenes (llamadas texturas) que son cuadradas y cuyas dimensiones son iguales a una potencia de dos y divisibles uniformemente por ocho. ¿Por qué? Debido a la forma en que se accede a los datos de los píxeles, estos números mágicos se alinean en la VRAM de la forma correcta para que el acceso sea más rápido, ya que los datos se suelen leer en trozos.

    Por lo tanto, asegúrate de que tu hoja de sprites sea de 64x64, 128x128, 256x256, 512x512 o 1024x1024. Como es de esperar, cuanto más pequeño sea, mejor, no solo en términos de rendimiento, sino porque una textura más pequeña hará que el SWF final del juego sea más pequeño.

    Aquí está la hoja de sprites que usaremos para nuestro ejemplo. Arte de "Tyrian" por Daniel Cook (Lostgarden.com).

    The spritesheet imageHaz clic con el botón derecho para descargar

    Paso 11: El Gestor de Entidades

    La primera técnica de optimización que vamos a aprovechar para conseguir un rendimiento fulgurante es el uso de "object pools". En lugar de asignar constantemente más ram para objetos como balas o enemigos, vamos a hacer un pool de reutilización que reciba los sprites no utilizados una y otra vez.

    Esta técnica garantiza que el uso de la memoria RAM sea muy bajo y que los problemas de recolección de basura rara vez se produzcan. El resultado es que la velocidad de fotogramas será mayor y tu juego funcionará sin problemas, independientemente del tiempo que juegues.

    Crea una nueva clase en tu proyecto llamada EntityManager.as e implementa un sencillo mecanismo de reciclaje bajo demanda como el siguiente.


    Paso 12: Establecer límites

    Nuestro gestor de entidades va a reciclar las entidades cuando se muevan fuera del borde izquierdo de la pantalla. La función de abajo es llamada durante los inits o cuando se dispara el evento de redimensionamiento. Añadimos algunos píxeles adicionales a los bordes para que los sprites no aparezcan o desaparezcan de repente.


    Paso 13: Configurar los sprites

    El gestor de entidades ejecuta esta función una vez al inicio. Crea un nuevo lote de geometría utilizando la imagen de la hoja de sprites que fue incrustada en nuestro código anterior. Envía el bitmapData al constructor de la clase spritesheet, que se utilizará para generar una textura que tiene todas las imágenes de sprites disponibles en una cuadrícula. Le decimos a nuestra hoja de sprites que vamos a usar 64 sprites diferentes (8 por 8) en una textura. Esta hoja de sprites será utilizada por el renderizador de geometría por lotes.

    Si quisiéramos, podríamos utilizar más de una hoja de sprites, inicializando imágenes y lotes adicionales según sea necesario. En el futuro, esto podría ser donde se crea un segundo lote para todos los azulejos del terreno que van debajo de los sprites de la nave espacial. Incluso se podría implementar un tercer lote que se superponga a todo para obtener efectos de partículas de fantasía y un toque visual. Por ahora, esta sencilla demostración técnica solo necesita un único lote de texturas y geometría de spritesheet.


    Paso 14: La reserva de objetos

    Aquí es donde el gestor de entidades aumenta el rendimiento. Esta optimización (un pool de reutilización de objetos) nos permitirá crear nuevas entidades solo bajo demanda (cuando no haya ninguna inactiva que pueda ser reutilizada). Observa cómo reutilizamos los sprites que están marcados como inactivos, a menos que estén siendo utilizados, en cuyo caso creamos uno nuevo. De esta manera, nuestro pool de objetos solo contiene tantos sprites como sean visibles al mismo tiempo. Después de los primeros segundos de ejecución de nuestro juego, la reserva de entidades se mantendrá constante, rara vez será necesario crear una nueva entidad una vez que haya suficientes para manejar lo que sucede en la pantalla.

    Continúa añadiendo a EntityManager.as lo siguiente:

    Las funciones anteriores se ejecutan cada vez que hay que añadir un nuevo sprite en la pantalla. El gestor de entidades escanea la lista de entidades en busca de una que no esté en uso actualmente y la devuelve cuando es posible. Si la lista está llena de entidades activas, hay que crear una nueva.


    Paso 15: ¡Simular!

    La última función que es responsabilidad de nuestro gestor de entidades es la que se llama en cada fotograma. Se utiliza para hacer cualquier simulación, IA, detección de colisiones, física o animación según sea necesario. Para la actual demostración técnica simplista, simplemente recorre la lista de entidades activas en el pool y actualiza sus posiciones en función de la velocidad. Cada entidad se mueve según su velocidad actual. Solo para divertirse, también están configuradas para girar un poco en cada fotograma.

    Cualquier entidad que pase del lado izquierdo de la pantalla se "mata" y se marca como inactiva e invisible, lista para ser reutilizada en las funciones anteriores. Si una entidad toca los otros tres bordes de la pantalla, la velocidad se invierte para que "rebote" en ese borde. Continúa añadiendo a EntityManager.as lo siguiente:


    Paso 16: La clase Sprite

    El último paso para poner todo en marcha es implementar las cuatro clases que componen nuestro sistema de "motor de renderizado". Debido a que la palabra Sprite ya está en uso en Flash, las próximas clases utilizarán el término LiteSprite, que no es solo un nombre pegadizo sino que implica la naturaleza ligera y simplista de este motor.

    Para empezar, crearemos la clase simple de sprite 2D a la que se refiere nuestra clase de entidad anterior. Habrá muchos sprites en nuestro juego, cada uno de los cuales se recoge en un gran lote de polígonos y se renderiza en una sola pasada.

    Crea un nuevo archivo en tu proyecto llamado LiteSprite.as e implementa algunos getters y setters como sigue. Probablemente podríamos salirnos con la nuestra utilizando simplemente variables públicas, pero en futuras versiones el cambio de algunos de estos valores requerirá la ejecución de algún código primero, por lo que esta técnica resultará inestimable.

    Cada sprite puede ahora llevar la cuenta de dónde se encuentra en la pantalla, así como su tamaño, su transparencia y el ángulo al que está orientado. La propiedad spriteID es un número que se utiliza durante el renderizado para buscar qué coordenada UV (textura) debe utilizarse como rectángulo de origen para los píxeles de la imagen de la hoja de sprites que utiliza.


    Paso 17: La clase Spritesheet

    Ahora necesitamos implementar un mecanismo para procesar la imagen de la hoja de sprites que hemos incrustado arriba y utilizar partes de ella en toda nuestra geometría renderizada. Crea un nuevo archivo en tu proyecto llamado LiteSpriteSheet.as y comienza importando la funcionalidad requerida, definiendo algunas variables de clase y una función constructora.

    El constructor de la clase anterior recibe un BitmapData para nuestra hoja de sprites, así como el número de sprites que hay en ella (en esta demo, 64).


    Paso 18: Picarla

    Debido a que estamos utilizando una sola textura para almacenar todas las imágenes de los sprites, necesitamos dividir la imagen en varias partes (una para cada sprite en ella) al renderizar. Esto lo hacemos asignando coordenadas diferentes para cada vértice (esquina) de cada malla quad utilizada para dibujar un sprite.

    Estas coordenadas se denominan UVs, y cada una va de 0 a 1 y representa en qué parte de la textura stage3D debe empezar a muestrear los píxeles al renderizar. Las coordenadas UV y los rectángulos de píxeles se almacenan en una matriz para su posterior uso durante el renderizado, de modo que no tengamos que calcularlos en cada fotograma. También almacenamos el tamaño y la forma de cada sprite (que en esta demo son todos idénticos) para que cuando rotemos un sprite sepamos su radio (que se utiliza para mantener el pivote en el mismo centro del sprite).


    Paso 19: Generar Mipmaps

    Ahora necesitamos procesar esta imagen durante el init. Vamos a cargarla para que sea utilizada como textura por la GPU. Mientras lo hacemos, vamos a crear copias más pequeñas que se llaman "mipmaps". El hardware 3D utiliza el mapeo de mip para acelerar el renderizado utilizando versiones más pequeñas de la misma textura cuando se ve desde lejos (a escala) o, en los verdaderos juegos 3D, cuando se ve en un ángulo oblicuo. Esto evita los efectos de "moiree" (parpadeos) que pueden ocurrir si no se utiliza el mipmapping. Cada mipmap es la mitad de ancho y alto que el anterior.

    Continuando con LiteSpriteSheet.as, vamos a implementar la rutina que necesitamos para generar mipmaps y cargarlos todos en la GPU de tu tarjeta de vídeo.


    Paso 20: Geometría de lotes

    La última optimización de hardcore que vamos a implementar es un sistema de renderizado de geometría por lotes. Esta técnica de "geometría por lotes" se utiliza a menudo en los sistemas de partículas. La última optimización de hardcore que vamos a implementar es un sistema de renderizado de geometría por lotes. Esta técnica de "geometría por lotes" se utiliza a menudo en los sistemas de partículas.

    Para minimizar el número de llamadas de dibujo y renderizar todo de una sola vez, vamos a agrupar todos los sprites del juego en una larga lista de coordenadas (x,y). Esencialmente, el lote de geometría es tratado por tu hardware de vídeo como una única malla 3D. Luego, una vez por fotograma, subiremos todo el buffer a Stage3D en una sola llamada a la función. Hacer las cosas de esta manera es mucho más rápido que subir las coordenadas individuales de cada sprite por separado.

    Crea un nuevo archivo en tu proyecto llamado LiteSpriteBatch.as y comienza incluyendo todas las importaciones para la funcionalidad que necesitará, las variables de clase que utilizará y el constructor como sigue:


    Paso 21: Lote de padres e hijos

    Continúa implementando getters y setters y la funcionalidad para manejar la adición de cualquier nuevo sprites al lote. El padre se refiere al objeto sprite stage utilizado por nuestro motor de juego, mientras que los hijos son todos los sprites de este lote de renderizado. Cuando añadimos un sprite hijo, añadimos más datos a la lista de vértices (que proporciona las ubicaciones en pantalla de ese sprite en particular) así como las coordenadas UV (la ubicación en la textura de la hoja de sprites en la que se almacena este sprite en particular). Cuando se añade o se elimina un sprite hijo del lote, establecemos una variable booleana para indicar a nuestro sistema de lotes que los buffers deben volver a cargarse ahora que han cambiado.


    Paso 22: Configurar el sombreado

    Un sombreador es un conjunto de comandos que se cargan directamente en la tarjeta de vídeo para conseguir una renderización extremadamente rápida. En Flash 11 Stage3D, se escriben en una especie de lenguaje ensamblador llamado AGAL. Este sombreador solo necesita ser creado una vez, al inicio. No es necesario que entiendas los opcodes del lenguaje ensamblador para este tutorial. En su lugar, simplemente implementa la creación de un programa de vértices (que calcula las ubicaciones de tus sprites en la pantalla) y un programa de fragmentos (que calcula el color de cada píxel) como sigue.


    Paso 23: Mover los Sprites

    Justo antes de ser renderizado, las coordenadas de los vértices de cada sprite en la pantalla probablemente habrán cambiado a medida que el sprite se mueve o gira. La siguiente función calcula dónde debe estar cada vértice (esquina de la geometría). Como cada quad (el cuadrado que compone un sprite) tiene cuatro vértices cada uno, y cada vértice necesita una coordenada x, y y z, hay doce valores que actualizar. Como una pequeña optimización, si el sprite no es visible simplemente escribimos ceros en nuestro buffer de vértices para evitar hacer cálculos innecesarios.


    Paso 24: Dibuja la geometría

    Por último, continúa añadiendo a la clase LiteSpriteBatch.as implementando la función de dibujo. Aquí es donde le decimos a stage3D que renderice todos los sprites en una sola pasada. En primer lugar, hacemos un bucle a través de todos los hijos conocidos (los sprites individuales) y actualizamos las posiciones del vértice en función de dónde están en la pantalla. A continuación, le decimos a stage3D qué sombreador y textura debe utilizar, así como establecer los factores de mezcla para la representación.

    ¿Qué es un factor de mezcla? Define si debemos utilizar o no la transparencia, y cómo tratar los píxeles transparentes en nuestra textura. Podrías cambiar las opciones en la llamada setBlendFactors para usar blanding aditivo, por ejemplo, lo que se ve muy bien para efectos de partículas como explosiones, ya que los píxeles aumentarán el brillo en la pantalla a medida que se superponen. En el caso de los sprites normales, todo lo que queremos es dibujarlos con el color exacto almacenado en la textura de nuestra hoja de sprites y permitir regiones transparentes.

    El paso final de nuestra función de dibujo es actualizar los buffers de UV e índice si el lote ha cambiado de tamaño, y cargar siempre los datos de vértices porque se espera que nuestros sprites estén en constante movimiento. Le decimos a stage3D qué búferes debe utilizar y, por último, renderizamos toda la gigantesca lista de geometría como si fuera una única malla 3D, para que se dibuje utilizando una única y rápida llamada a drawTriangles.


    Paso 25: La clase de escenario Sprite

    La última clase requerida por nuestro elegante (y veloz) motor de renderizado de sprites acelerado por hardware es la clase sprite stage. Esta etapa, al igual que la etapa tradicional de Flash, contiene una lista de todos los lotes que se utilizan para tu juego. En esta primera demostración, nuestro escenario sólo utilizará un único lote de sprites, que a su vez solo utiliza una única hoja de sprites.

    Crea un último archivo en tu proyecto llamado LiteSpriteStage.as y comienza creando la clase como sigue:


    Paso 26: La matriz de la cámara

    Para saber exactamente en qué lugar de la pantalla debe ir cada sprite, haremos un seguimiento de la ubicación y el tamaño de la ventana de renderizado. Durante las inicializaciones de nuestro juego (o si cambia) creamos una matriz de vista del modelo que es utilizada por Stage3D para transformar las coordenadas 3D internas de nuestros lotes de geometría a las ubicaciones adecuadas en pantalla.


    Paso 27: Manejar los lotes

    El paso final en la creación de nuestra demo del juego Stage3D es manejar la adición y eliminación de lotes de geometría, así como un bucle que llama a la función de dibujo en cada lote. De esta manera, cuando el evento principal ENTER_FRAME de nuestro juego se dispara, moverá los sprites por la pantalla a través del gestor de entidades y luego le dirá al sistema de escenarios de sprites que se dibuje a sí mismo, que a su vez le dice a todos los lotes conocidos que se dibujen.

    Debido a que esta es una demo muy optimizada, solo habrá un lote en uso, pero esto cambiará en futuros tutoriales a medida que vayamos añadiendo más caramelos para la vista.


    Paso 28: ¡Compilar y ejecutar!

    ¡Ya casi hemos terminado! Compila tu SWF, corrige cualquier error tipográfico y comprueba la bondad gráfica. Deberías tener una demo con este aspecto:

    Screenshot of our final .SWF in action. Sprite-o-licious!Screenshot of our final .SWF in action. Sprite-o-licious!Screenshot of our final .SWF in action. Sprite-o-licious!

    Si tienes dificultades para compilar, ten en cuenta que este proyecto necesita una clase hecha por Adobe que se encarga de la compilación de los shaders AGAL, que se incluye en la descarga del archivo .zip del código fuente.

    Solo como referencia, y para asegurarte de que has utilizado los nombres de archivo y las ubicaciones correctas para todo, este es el aspecto que debería tener tu proyecto de FlashDevelop:

    The project window - what each file is named.

    Tutorial completo: Eres increíble

    Eso es todo para el primer tutorial de esta serie. La semana que viene podrás ver cómo el juego evoluciona poco a poco hasta convertirse en un juego de disparos a 60 FPS de gran aspecto y fluidez. En la siguiente parte, implementaremos los controles del jugador (usando el teclado para moverse) y añadiremos algo de movimiento, sonidos y música al juego.

    Me encantaría que me comentaras algo sobre este tutorial. Invito a todos los lectores a ponerse en contacto conmigo a través de Twitter: @mcfunkypants o mi blog mcfunkypants.com o en Google+ en cualquier momento. Siempre estoy buscando nuevos temas para escribir futuros tutoriales, así que no dudes en solicitar uno. Por último, ¡me encantaría ver los juegos que hagas con este código!

    Gracias por leer. Nos vemos la semana que viene. Buena suerte y ¡diviértete!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.