4 Animaciones

Es hora de empezar a animar esto un poco.

A la hora de desarrollar un juego y mucho antes de empezar a escribir código, deberemos resolver algunos puntos clave.

Empezaremos por elegir un tipo de juego: plataformas, puzzles, rpg, sandbox, batalla espacial...

A continuación deberemos diseñar un argumento o guion, objetivos, fases, enemigos...

Necesitaremos gráficos y sonidos. Esta parte es importante, ya que un buen juego puede no resultar atractivo si los gráficos y/o los sonidos son pobres. Y, por desgracia, las capacidades de programación no siempre van unidas a las de diseño gráfico, y los sonidos no son fáciles de generar o captar.

Para ésta última parte, para el objetivo de este curso, y como punto de partida para aprender y desarrollar prototipos, disponemos de páginas con recursos libres, como OpenGameArt, desde la que podemos descargar gráficos y sonidos para nuestros juegos.

Animación de personajes

Ejemplo de animación 1
Ejemplo de animación 1

En toda animación podemos distinguir dos partes:

Primeramente, el sujeto cambiará de forma con el tiempo. Por ejemplo, una planta que crece, aumentará su altura, crecerán hojas y flores, etc. O un personaje que camina, cambiará la posición de sus extremidades, moverá la cabeza, etc.

En segundo lugar, el sujeto cambiará de ubicación a medida que se desplace. El sujeto que camina avanza, retrocede, sube o baja, un vehículo igual, etc.

Nuestros gráficos para animaciones pueden presentar varios formatos, ya sea en diferentes tipos de ficheros de imagen, o en distintas formas de almacenar la información para crear las imágenes animadas.

Los formatos de fichero pueden ser diferentes. Es habitual que se trate de formatos que permitan transparencias, de modo que al sobreponerlos con diferentes fondos, las siluetas se adapten correctamente, o que ciertas partes dejen pasar parte del fondo, con las partes acristaladas, o traslúcidas. En ese sentido, formatos como png son ideales, así como los gráficos vectoriales, aunque hay otras opciones.

En cuanto a la propia información de los gráficos, puede tratarse de una colección de fotogramas para cada sujeto en diferentes posiciones, de modo que podamos elegir en qué secuencia mostrar cada uno en función del movimiento que esté realizando el sujeto. Por ejemplo la imagen de la derecha corresponde a un posible personaje en un juego de plataformas. Para mostrar al personaje caminando usaríamos los fotogramas 2 a 7, para saltar el 8, 9 y 10 de la segunda línea, etc.

Se pueden crear diferentes personajes o modificar su apariencia a partir de la misma animación, añadiendo diferentes capas, como armaduras, armas, peinados, etc.

Ejemplo 3

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 3 sdl_003.zip 2024-01-07 21632 bytes 45

Montar por piezas

Gráfico por partes
Gráfico por partes

Otra forma de crear nuestros personajes es crearlos a partir de piezas básicas. Así, por ejemplo, para crear una nave espacial en un juego podemos seleccionar desde una colección de imágenes un fuselaje, alas, motores, cañones, etc y crear una imagen única para usar en el juego. Esto nos permitiría que el jugador personalice su nave o ir añadiendo módulos a medida que el juego avance.

Esa colección puede estar en varios formatos diferentes: en un único fichero, en un fichero para cada parte, en ficheros para cada tipo de parte en particular, en formato vectorial, etc.

A la hora de optimizar siempre será mejor tener una única textura que tener que componer la textura a partir de sus partes cada vez que queramos representarla, lo que puede ocurrir muchas veces por segundo. Podemos crear una textura a partir de otras de una forma relativamente simple.

Para ello bastará con crear una textura para nuestro personaje, mediante la función SDL_CreateTexture, indicando el formato adecuado, el tipo de acceso SDL_TEXTUREACCESS_TARGET, y las medidas.

Para poder modificar una textura hay que convertirla en el objetivo de renderización, mediante SDL_SetRenderTarget, y renderizar a partir de las texturas, líneas, puntos, etc, igual que lo haríamos en una ventana.

Una vez creada la textura restauramos el destino de renderizado a la ventana, usando también SDL_SetRenderTarget, pero con el segundo parámetro a cero, y ya podemos empezar a usar la nueva textura.

    renderer = SDL_CreateRenderer(ventana, -1, 0);
    cockpit = IMG_LoadTexture(renderer, "./SpaceShooterRedux/PNG/Parts/cockpitBlue_1.png");
...

    nave    = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, 180, 90);

    SDL_SetRenderTarget(renderer, nave);
    SDL_SetRenderDrawColor(renderer, 0,0,0,0);
    SDL_RenderClear(renderer);
    SDL_SetTextureBlendMode(nave, SDL_BLENDMODE_BLEND);

    SDL_QueryTexture(cockpit, NULL, NULL, &Origen.w, &Origen.h);
    Destino.x = 73;
    Destino.y = 20;
    Destino.w = Origen.w;
    Destino.h = Origen.h;
    SDL_RenderCopy(renderer, cockpit, &Origen, &Destino);
...
    SDL_SetRenderTarget(renderer, 0);
...

Ejemplo 4

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 4 sdl_004.zip 2024-01-07 5698968 bytes 46

Gráficos vectoriales

Los gráficos vectoriales se crean a partir de una lista de segmentos. No es necesario almacenar imágenes, basta con una lista de puntos y líneas que unen esos puntos. Nuestro juego puede usar esa información para trazar los gráficos en la ventana usando funciones como SDL_RenderDrawLine o SDL_RenderDrawLines.

En el capítulo 6 del curso de gráficos vimos cómo rotar, trasladar y cambiar la escala de gráficos en 2D.

Estos cálculos se hacen aplicando matrices de transformación. Por ejemplo, para rotar un ángulo α un objeto dado por coordenadas en 2D alrededor del punto de origen (0,0), hay que multiplicar por la matriz:

Matriz de rotación
Matriz de rotación

En este caso, las nuevas coordenadas son:

x' = x * con(α) - y * sin(α)
y' = x * sin(α) + y * cos(α)

Pero esto es para un sistema de coordenadas normal. En SDL el eje y está invertido, es decir, los valores de la coordenada y crecen hacia abajo, por lo que tendremos que corregir estas fórmulas, que ahora quedan así:

x' = x * con(α) + y * sin(α)
y' = -x * sin(α) + y * cos(α)

La matriz de traslación es:

Matriz de traslación
Matriz de traslación

Las nuevas coordenadas son ahora:

x' = x + dx
y' = y + dy

La matriz de escala es:

Matriz de escala
Matriz de escala

Y las nuevas coordenadas son:

x' = x" * x 
y' = y" * y 

La ventaja es que podemos combinar las tres matrices de transformación en una, multiplicando sus elementos uno a uno.

Por ejemplo, si queremos rotar α, trasladar dx unidades en el eje x y dy en el y, y agrandar el gráfico x" veces en el eje x e y" veces en el eje y, la matriz resultante es:

Matriz transformadora
Matriz transformadora
Asteroids
Asteroids

Y las nuevas coordenadas se calculan:

x' = x * x" * cos(α) + y * y" * sin(α) + dx
y' = -x * x" * sin(α) + y * y" * cos(α) + dy

Es importante que si queremos que las rotaciones sean sobre el centro de las figuras, los puntos que componen la figura deben estar tan centrados alrededor del origen como se posible.

Ejemplo 5

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 5 sdl_005.zip 2024-01-07 1439 bytes 53

Por supuesto, podemos generar Texturas a partir de la lista de líneas y trabajar como con el resto de imágenes, pero en ocasiones podemos querer dar un toque retro a nuestro juego, como en el clásico Asteroids.