SDL en C bajo GNU/Linux 006 – Horizontal parallax scrolling


  
La ventana vamos a definirla a 640 de ancho por 480 de alto como venimos haciendo.
  
Todo el tinglado puede bajarse desde aquí: paralax.zip
  
Los gráficos que vamos a usar son estos (bueno, en realidad son los bmp que vienen en el zip pero los muestro para ahora después explicar cosejas).
  
Si no sabes de que va esto puedes mirar los anteriores posts sobre SDL en C aquí: https://mierda.tv/?s=SDL+en+C
  

  

  

  

  
Esos 4 mapas de bits se entienden mejor unos sobre otros como si fuesen capas.
  
Se ha de entender tal y como se ve en esta foto que tendremos una ventana deslizante de 640×480 que irá recorriendo todo el ancho.
  

  
Mientras una capa se mueve a una velocidad otras se mueven más lento. De esa forma se consigue un efecto de profundidad.
Un ejemplo es por ejemplo lo que ves por la ventanilla cuando viajas en tren. Las montañas no irán desapareciendo tan rápidamente como lo que tienes más cerca.
  

  
En el ejemplo veremos que la capa de color más claro no la vamos a mover. Podría hacerse tambien e incuso ser la capa en la que van las estrellas y la luna. Podría moverse pero tendría que ser muy lento.
  
Yo no soy especialista en hacer parallax ni mierdas por el estilo pero si que he pensado que no tendría que ser muy complicado hacer uno. Me he puesto con ello y esté es el código resultante que luego examinaremos con detalle:
  

#include <SDL.h>
#include <stdbool.h>

int main(int argc, char ** argv) {

	SDL_Window * ventana;
	SDL_Renderer * render;
	SDL_Event evento;

	int anchoFondo = 640;	// El ancho de la ventana.
	int altoFondo = 480;	// El alto de la ventana.

	int step1 = 0;  // 640+640+640 =  1920  de ancho de las imagenes. La ventana muestra 640 de modo que el limite para recorrerlo será de 1280.
	int step2 = 0;  // Inicialmente se empieza cada uno de los niveles a 0. Cada uno se moverá a una velocidad diferente.
	int step3 = 0;  // Pararemos cuando alguno de ellos llegue a 1280.

	SDL_Surface * imgPrllx1; SDL_Texture * txtPrllx1; imgPrllx1 = SDL_LoadBMP("res/parallax1.bmp"); // este no se moverá. Es el fondo total.
	SDL_Surface * imgPrllx2; SDL_Texture * txtPrllx2; imgPrllx2 = SDL_LoadBMP("res/parallax2.bmp");
	SDL_Surface * imgPrllx3; SDL_Texture * txtPrllx3; imgPrllx3 = SDL_LoadBMP("res/parallax3.bmp");
	SDL_Surface * imgPrllx4; SDL_Texture * txtPrllx4; imgPrllx4 = SDL_LoadBMP("res/parallax4.bmp");


	SDL_Rect srcParallax1 = { 0, 0, anchoFondo, altoFondo }; SDL_Rect dstParallax1 = { 0, 0, anchoFondo, altoFondo }; // no se moverá. El fondo total.
	SDL_Rect srcParallax2 = { step1, 0, anchoFondo, altoFondo }; SDL_Rect dstParallax2 = { 0, 0, anchoFondo, altoFondo };
	SDL_Rect srcParallax3 = { step2, 0, anchoFondo, altoFondo }; SDL_Rect dstParallax3 = { 0, 0, anchoFondo, altoFondo };
	SDL_Rect srcParallax4 = { step3, 0, anchoFondo, altoFondo }; SDL_Rect dstParallax4 = { 0, 0, anchoFondo, altoFondo };



	bool quit = false;

	SDL_Init(SDL_INIT_VIDEO);
	ventana = SDL_CreateWindow("Ejemplo de Parallax",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,640,480,SDL_WINDOW_OPENGL);

	if (ventana == NULL) {
		printf("No se pudo crear la ventana: %s\n", SDL_GetError());
		return 1;
	}

	render = SDL_CreateRenderer(ventana, -1, 0);

	txtPrllx1 = SDL_CreateTextureFromSurface(render, imgPrllx1);SDL_RenderCopy(render, txtPrllx1, &srcParallax1, &dstParallax1);
	txtPrllx2 = SDL_CreateTextureFromSurface(render, imgPrllx2);SDL_RenderCopy(render, txtPrllx2, &srcParallax2, &dstParallax2);
	txtPrllx3 = SDL_CreateTextureFromSurface(render, imgPrllx3);SDL_RenderCopy(render, txtPrllx3, &srcParallax3, &dstParallax3);
	txtPrllx4 = SDL_CreateTextureFromSurface(render, imgPrllx4);SDL_RenderCopy(render, txtPrllx4, &srcParallax4, &dstParallax4);
	SDL_RenderPresent(render);

	while (!quit) {
		SDL_WaitEvent(&evento);

		switch (evento.type) {
			case SDL_QUIT:
				quit = true;
				break;
			case SDL_KEYDOWN:
				switch (evento.key.keysym.sym) {
					case SDLK_RIGHT:
						while (!quit) { // cuando algún step (el 3 si no se altera) pase 1280 quit será true. Se saldrá del while.
							if(1280>step1) { step1=step1+5; }else{ quit = true; }
							if(1280>step2) { step2=step2+20; }else{ quit = true; }
							if(1280>step3) { step3=step3+40; }else{ quit = true; }

							SDL_Rect srcParallax2 = { step1, 0, anchoFondo, altoFondo };
							SDL_Rect srcParallax3 = { step2, 0, anchoFondo, altoFondo };
							SDL_Rect srcParallax4 = { step3, 0, anchoFondo, altoFondo };

							txtPrllx1 = SDL_CreateTextureFromSurface(render, imgPrllx1);SDL_RenderCopy(render, txtPrllx1, &srcParallax1, &dstParallax1);
							txtPrllx2 = SDL_CreateTextureFromSurface(render, imgPrllx2);SDL_RenderCopy(render, txtPrllx2, &srcParallax2, &dstParallax2);
							txtPrllx3 = SDL_CreateTextureFromSurface(render, imgPrllx3);SDL_RenderCopy(render, txtPrllx3, &srcParallax3, &dstParallax3);
							txtPrllx4 = SDL_CreateTextureFromSurface(render, imgPrllx4);SDL_RenderCopy(render, txtPrllx4, &srcParallax4, &dstParallax4);
							SDL_RenderPresent(render);
						}
						break;
				}
				break;
		}


	}

}

  
Aquí puede verse el movimiento del ejemplo en un gif:
  

  
El movimiento comenzará cuando pulsemos la tecla cursor derecha.
  

case SDLK_RIGHT:

  
Tendremos 3 variables llamadas step1,step2 y step3. Al inicio las hemos declarado como enteros y las hemos puesto a 0. Las usaremos para en cada pasada que la función que recorre cada uno de los mapas de bits se posicione en un punto del eje x. De esa forma vamos creando un render que mostramos, un render que suma todo lo que va cambiando. Step3 por ejemplo irá más rápido al sumar 40px en cada pasada del bucle while en el que está encerrado. Más rápido que step2 y step1.
  
El mapa de bits más al fondo no lo moveremos. Se podría también mover pero en este ejemplo yo me lo he ahorrado.
  

if(1280>step1) { step1=step1+5; }else{ quit = true; }
if(1280>step2) { step2=step2+20; }else{ quit = true; }
if(1280>step3) { step3=step3+40; }else{ quit = true; }

  

Hemos de fijarnos que he metido un else en esos ifs para que cuando alguna de las variables sea mayor de 1280 se salga del programa (variable quit = true).
En el ejemplo siempre saldrá cuando step3 sea mayor de 1280 de modo que sobran los otros else, no obstante los he dejado por si alguien varia el valor y quiere que lo de más al fondo se mueva más rápido.
  
Existe una forma de conseguir un efecto parallax tanto de scroll horizontal como vertical. Si por ejemplo deslizásemos la ventana hacia arriba las velocidades tendrían que tender a ir igualándose y generaría un efecto de que hemos subido. Es como si nos saliesemos del vagón de un tren hacia arriba.
  
Aquí he encontrado ese efecto para que se entienda: https://www.youtube.com/watch?v=8RZRE0M3Hvc
  
Aquí más ejemplos del efecto parallax en juegos: https://www.youtube.com/watch?v=ltuRuGM271Q
  
Algo que suele hacerse es meter columnas por ejemplo por las que el jugador (el player, el personaje que manejamos/coche/nave/bicicleta/…) pasa por debajo. Eso genera también un efecto de profundidad.
  
Eso lo veremos en futuras entradas ya que todo puede combinarse y generar un juego mucho más rico que aunque sea 2D tenga cierta sensación de profundidad.
  
Una persecución en un tren o ir saltando de vagón en vagón desde arriba como indiana jones se presta a usar este efecto.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *