Articulo Técnicas de programación de videojuegos en 2D con Visual Basic 6.0: La anatomía de un videojuego.

Mensajes
1,139
Oro
235,024
Los videojuegos son programas informáticos como todos los demás programas usados por nosotros cada día, software diseñado para correr sobre un sistema operativo dado instalado en un dispositivo determinado, sin embargo, debido a sus características particulares, los videojuegos también poseen algunas diferencias a la hora de elaborarlos en comparación con el software orientado a otras actividades comunes de la vida cotidiana.

La diferencia en este caso podría venir en lo fundamental de la forma como los videojuegos deben presentar la salida en pantalla cuando los usuarios interactúan con ellos a través de distintos dispositivos de entrada como un teclado, un ratón, un mando, etc.

En un videojuego se logra la sensación de movimiento como mismo se lo hace en una película cinematográfica, se consigue del mismo modo sin importar si se trata de un videojuego en 2D o en 3D, y se hace presentando en la pantalla de la computadora una sucesión de imágenes estáticas generadas dinámicamente y conocidas como fotogramas o más frecuentemente como frames.

La diferencia en este aspecto entre un videojuego en 2D y uno en 3D, está más bien en cómo se elabora en ellos cada una de esas imágenes estáticas representativas del estado del sistema en un instante determinado, o en cómo se animan distintas partes o elementos gráficos contenidos en ella como podrían ser los caracteres o personajes; en los videojuegos en 2D la animación de los elementos gráficos con esta característica se realiza también a partir de secuencias de imágenes utilizando la técnica de los sprites, en tanto en los videojuegos en 3D se animan modelos en tres dimensiones basados en polígonos con texturas en su superficie para aumentar su realismo visual.

En todo caso, en esta serie de textos no vamos a tratar los videojuegos en 3D (como su título lo indica), y en publicaciones posteriores veremos en detalle cómo animar los personajes en 2D a partir de una secuencias de imágenes; por ahora sólo es importante conocer por lo menos de forma superficial cómo se logra la presentación en pantalla de un videojuego, porque como se ha dicho de esto se deriva en lo fundamental la forma específica de conformar estos programas informáticos.

En efecto, por lo pronto nos basta con conocer como mostrando una serie de imágenes en sucesión en una cadencia lo suficientemente alta, en cada una de las cuales los distintos elementos gráficos o figuras pueden haber variado un poco en cuanto a su posición y forma, podemos hacer creer a los ojos humanos que se está produciendo un movimiento fluido, tal como sucede en las películas cinematográficas, porque los humanos somos incapaces de darnos cuenta de que se trata en realidad nada más de una sucesión de diapositivas estáticas en una presentación rápida debido a nuestras limitaciones sensoriales.

Nota: Es conocido como otros animales como los chimpancés sí pueden percibir esto con más detalles y ver imágenes subliminares incluidas en la presentación pasadas por alto por los humanos.

Es posible alguno de ustedes se haya divertido en la escuela durante la clase de matemáticas haciendo distintas figuritas en un borde de las hojas de su libreta de tal modo que cuando después pasaban las hojas en rápida sucesión las figuritas parecían moverse y cambiar de forma; por lo menos en mi época todo eso era bastante común porque como es posible sepan no disponíamos de otros medios para entretenernos, aunque me imagino ahora todo sea diferente, porque incluso muchos niños disponen de móviles y eso debe distraerlos mucho más.

El mismo principio básico comentado antes es usado en las películas y los videojuegos para mostrar los movimientos de los distintos caracteres, con la particularidad de que en los últimos se hace más bien como en las películas animadas, separando los personajes de uno o más fondos más o menos estáticos en lugar de mostrando una serie de fotografías del todo, y por eso estos programas tienen unas demandas diferentes en comparación con los demás programas donde lo mencionado no es necesario.

En el mundo del cine tradicional filmado en cinta de celuloide la cantidad de fotogramas por segundo a ser presentado solía ser de 24, y de ahí el nombre de un conocido programa de la televisión cubana dedicado a hablar de cine (24 por segundo).

Por su parte, en los videojuegos modernos, la cantidad de frames por segundo (fps) a ser mostrados en pantalla puede llegar a ser de 60 y más, porque a más fps más realismo visual con movimientos más suaves y fluidos, incluso cuando se considera que con 12 fps como mínimo es posible engañar a un ojo humano (o más bien un cerebro), y la televisión analógica solía usar una cadencia de 30 imágenes por segundo en la norma americana NTSC.

Nota: En otras normas de televisión analógica comunes se usaban sólo 25 fps o imágenes por segundo.

La necesidad de presentar tantas imágenes por segundo en pantalla (dependiendo esto del género del videojuego), hace a los videojuegos programas más o menos demandantes de procesador, en especial si tenemos en cuenta que esto no se hace como en una película o un video donde cada imagen está previamente creada, puesto en este caso cada imagen de la secuencia a mostrar, como se ha mencionado, deber ser primero generada en tiempo real, teniendo en cuenta la existencia de una miríada de distintos elementos gráficos interactuando los unos contra los otros, a todos los cuales podría ser necesario recolocar y animar constantemente en dependencia de su interacción entre ellos y con los usuarios del software.

Por otro lado, en cada uno de los frames de un videojuego por lo general también es necesario hacer variados cálculos para llevar a cabo la comentada recolocación de los elementos gráficos con más realismo teniendo en cuenta la física, y se toman otras decisiones, a veces no relacionadas solamente con la presentación en pantalla, sino con un comportamiento determinado de los personajes, como pasa con la inteligencia artificial involucrada en los NPCs (Non Player Characters).

Pero lo más importante no es la forma como un videojuego usa más o menos potencia de procesador, puesto eso no influye mucho en la manera de elaborar estos programas, lo realmente importante es como la característica de los videojuegos de mostrar como salida gráficos intensivos, nos obliga a evitar el uso de ciertos mecanismos subyacentes del sistema operativo por lo común usados en los demás programas donde la salida por pantalla no es tan demandante.

En resumen, los videojuegos no se elaboran por lo general usando muchos de los mecanismos comunes en otros software de un sistema operativo multitarea y con interfaz gráfica de usuario como Windows básicamente debido a lo relatado antes, en cambio, en lugar de ser programas dirigidos por eventos del sistema operativo como por lo general se hace con otros software de sistemas operativos con las características de Windows, son más parecidos a un programa hecho de la manera más tradicional destinado a un sistema operativo como MS DOS o Unix.

Los sistemas operativos existen por dos motivos fundamentales, uno de ellos es porque existe la necesidad de administrar de manera coherente los distintos recursos disponibles en un sistema de computo como la memoria, los dispositivos de almacenamiento, los procesadores, la pantalla, las impresoras, etc., en caso contrario distintos procesos (como se les llama a los programas corriendo sobre un sistema) podrían intentar acceder a la misma vez a una impresora o escribir en un mismo archivo; la otra función de importancia de un sistema operativo es la de exponer a los programas una interfaz de programación más sencilla, consistente, e independiente que la más complicada, primitiva, y dependiente de los dispositivos electrónicos provista por el hardware, con lo cual se espera conseguir facilitar la creación de software sin vernos en la necesidad de interactuar directamente con los numerosos dichos dispositivos a menudo incompatibles entre sí producidos por un cierto número de fabricantes competidores.

Por eso por lo normal los programas suelen servirse de esa interfaz software expuesta por un sistema operativo en particular con lo cual además facilitamos la creación de los programas y evitamos la necesidad de reinventar la rueda a cada instante.

En un sistema operativo moderno como Windows existen por lo menos dos mecanismos principales diseñados para servir como interfaz entre los programas y el sistema operativo.

El primer mecanismo es la API (Application Programming Interface) del sistema operativo por medio de la cual los programas solicitan servicios a Windows como podría ser la creación de una ventana; este mecanismo es utilizado con profusión por todos los programas diseñados para ese sistema operativo dado de otro modo no se podrían hacer muchas cosas con éste debido a su naturaleza multitarea y a correr en modo protegido.

En cambio, el otro mecanismo es usado para la comunicación entre procesos, lo cual se lleva a cabo entre otras formas por medio del uso de mensajes traducidos a nivel del programa en eventos o sucesos; este mecanismo orientado a eventos como se ha insinuado no resulta idóneo cuando se trata de programar un videojuego, a menos sea un videojuego de cartas o de tablero, y debido a eso cuando se trata de hacer videojuegos con gráficos intensivos es necesario darle de lado en lo posible.

Por lo general un videojuego debe de realizar las siguientes tareas (para llamarlas de alguna manera) durante su procesamiento:​
  • Inicialización​
  • Introducción​
  • Inicio de partida​
  • Bucle principal (este no es un bucle único en un videojuego pero es donde se hace lo fundamental y por eso lo llamo principal)​
  • Terminación de partida​
  • Finalización​
Las tareas de inicialización y de introducción se producen una sola vez en cada carga del programa.

La primera consiste básicamente en inicializar variables globales, localizar memoria para otras necesidades, abrir ficheros o conexiones a bases de datos a ser utilizadas, crear tablas con valores precalculados de modo se pueda conseguir más eficiencia cuando se deban realizar cálculos intensivos dentro del bucle principal, descomprimir distintos recursos como gráficos o música y efectos sonoros y por lo menos cargar parte de ellos, entre otras cosas demandantes y dependientes de las necesidades de cada videojuego en específico.

Todo lo anterior suele hacerse en tanto se muestra a los usuarios una pantalla de carga con una barra de progreso u otro medio para indicarle se está procesando la tarea.

Por su parte, en la introducción es donde se suele dar la bienvenida a los usuarios del videojuego una vez cargados los recursos de modo no se produzcan retardos más tarde, y se les presenta un menú en adición a una animación o una cinemática para estimularlos o mostrarles cómo se juega.

La tarea siguiente es la del inicio de la partida del videojuego, donde los usuarios pueden tener la oportunidad de personalizarla antes de comenzarla, y se podrían inicializar variables dependientes de esta como la puntuación, número de vidas, nivel, etc., además de mostrar una cinemática de la partida entre otras cosas.

Por fin se entra en un bucle o lazo principal donde se realizan en sucesión y de forma continua todas las actividades propias de un videojuego, como el procesamiento de la entrada de los usuarios, los cálculos para la actualización de las posiciones de los distintos elementos gráficos, la detección de colisiones entre dichos elementos gráficos, los cálculos relacionados con técnicas de inteligencia artificial para darle vida a los personajes controlados por la computadora, y por último la presentación en pantalla de cada frame del videojuego generado previamente.

En resumen, el bucle principal es la tarea más importante de un videojuego, es donde sucede todo mientras se está jugando, y perdura hasta cuando se termina la partida o el videojuego.

Las líneas de código Visual Basic mostradas a continuación pueden dar una idea de un bucle principal de un videojuego:
Código:
While Running 'En tanto la variable booleana Running (Corriendo) contenga un valor verdadero
  DoEvents 'Procesar los eventos de Windows para impedir se congele el programa y lograr responda cuando sea cerrado
  If Not Paused Then 'Si la variable booleana Paused (Pausado) no contiene un valor verdadero
    TickCount = GetTickCount() 'Obtener la cantidad de milisegundos transcurridos en la variable TickCount.
    If TickCount > TickDelay Then 'Si han pasado los milisegundos necesarios para mantener la cantidad de fps establecida de forma constante.
      TickDelay = TickCount + FrameDelay 'Salvar momento de procesar siguiente frame.
      ProcessFrame 'Procesar un frame del videojuego.
    End If
  End If
Wend
En estas pocas líneas de código podemos ver cómo se consigue la cantidad de milisegundos transcurridos desde cuando se inició el sistema operativo, usando una llamada a la función de la API de Windows denominada GetTickCount, con lo cual podemos lograr dentro de lo posible mantener un nivel de fps constante dado esto es importante por muchas razones como veremos más adelante en otros textos cuando tratemos la animación.

La variable FrameDelay se calcula dividiendo 1000 entre la cantidad de fps deseado como también veremos en más detalle próximamente.

El bucle principal continuará repitiéndose hasta cuando en algún lugar se asigne un valor de falso a la variable Running y se salga de éste.

En ese momento se producirá la tarea de terminación, dado salir del bucle principal significa se terminó la partida (o en otros videojuegos el propio juego); esta tarea consiste en mostrar por pantalla información relativa a los resultados obtenidos, como un listado de las máximas puntuaciones, estadísticas de la partida, etc.

En adición a lo mencionado, la tarea de terminación podría permitir volver a la tarea de inicio para empezar una partida de nuevo o pasar de nivel si esto es necesario.

Por último está la tarea de finalización, donde debemos liberar todas las variables dinámicamente creadas, y los recursos utilizados, y llevar a cabo el cierre de ficheros abiertos o conexiones a bases de datos, todo esto en tanto se podría estar mostrando en la pantalla información pertinente.

Pero lo más importante es recordar como todo lo relevante en un videojuego se produce dentro del bucle principal antes mostrado, o en este caso dentro de la subrutina ProcessFrame llamada desde dicho bucle principal de forma recurrente cuando se considera que debe ser procesado cada uno de los frames del videojuego.

Por eso volveré a listar las actividades más comunes a realizar dentro del bucle principal a pesar de haberlas mencionado por encima antes, aun cuando también podrán suponer como los detalles de esto dependerán del videojuego en específico:​
  • Obtener entrada desde los dispositivos (teclado, ratón, mando, etc.)​
  • Procesar AI​
  • Procesar cálculos de físicas​
  • Actualizar posiciones de los sprites (teniendo en cuenta tanto los deseos del usuario demandados a través de la entrada antes obtenida como los resultados de los cálculos implicados)​
  • Comprobar colisiones​
  • Reproducir efectos de sonido​
  • Reproducir música​
  • Renderizar la escena del frame en la pantalla o en la superficie de una ventana​
El orden de realización de todas estas actividades es importante en algunos casos, como podrán darse cuenta, porque no podemos actualizar la posición de un elemento animado en pantalla si primero no conocemos dónde recolocarlo, y para esto es posible necesitemos los resultado de algunos cálculos de trayectoria o relacionados con el comportamiento del objeto.

En todo caso, por ahora podemos dar por concluido este artículo donde por lo menos hemos visto de una forma somera cómo es la anatomía básica de un videojuego, y las tareas involucradas en su programación, aun si todo esto lo hemos hecho como se ha dicho de un modo bastante superficial, como una forma de introducción, y después vamos a ir tratando cada cosa en más detalles por medio de demostraciones con código real.

En un próximo artículo vamos a ver en la práctica cómo podemos implementar todo lo dicho usando una clase, y también vamos a elaborar otra clase para cargar en memoria y mostrar imágenes en pantalla, puesto debemos comenzar por ahí si pretendemos poder notar los cambios efectuados como resultado de nuestras manipulaciones o calculos.

Me gustaría pudieran comentar sobre lo dicho en este texto y dar más elementos si consideran faltó algo a la explicación para hacerla más asimilable (todo se va a volver más claro con la práctica).​
 
Por lo general un videojuego debe de realizar las siguientes tareas (para llamarlas de alguna manera) durante su procesamiento:​
  • Inicialización​
  • Introducción​
  • Inicio de partida​
  • Bucle principal (este no es un bucle único en un videojuego pero es donde se hace lo fundamental y por eso lo llamo principal)​
  • Terminación de partida​
  • Finalización​
Las tareas de inicialización y de introducción se producen una sola vez en cada carga del programa.​
Esto es basicamente una maquina de estado finita. Va cambiando de estado o no en base al entorno (variables globales/locales). Por eso hay mucha gente que se queja de que los videojuegos estan limitados por la imaginacion del desarrollador, pues la base conceptual de muchos videojuegos no esta diseñada para dar sorpresas.
Aunque se puede maquillar (ej. creando una dungeon a partir de partes elementales dispuestas en un orden random) o puedes generar mas estados a que los que un humano pueda experimentar en un tiempo razonable.
 
Atrás
Arriba