28 de Diciembre, 2005

[PJRS] Dibujando con SDL

Esta anotación forma parte de la serie Programando Juegos con Ruby y SDL.

Ya estamos cada vez más cerca de tener completo el bucle de nuestro juego. La parte que nos falta es bastante crítica, ya que dibujar en pantalla es muy costoso. Cuando estemos haciendo un juego tendremos que ser cuidadosos sobre cómo y cuándo dibujamos.

Pero aún falta un poco para eso, primero tenemos que saber lo básico sobre cómo dibujar con SDL.

Aunque SDL, sobretodo con el extra de SGE, nos proporciona un buen conjunto de primitivas para dibujar en las superficies, en realidad haremos uso de otro tipo de métodos que se centran en copiar un mapa de pixeles de una superficie a otra. Por ahora vamos a usar una de estas primitivas para tener algo con lo que trabajar para acabar la estructura del bucle.

He mencionado la palabra 'superficie'. Se trata de un tipo de objeto que define SDL, y el principal es el que nos devuelve setVideoMode y que es nuestra pantalla. Recordemos el código de esa parte:

# ponemos el modo de vídeo a 640x480 16bpp
pantalla = SDL.setVideoMode(640, 480, 16,  sdlVideoFlags)

Si todo ha ido bien, en pantalla tendremos una instancia de SDL::Surface que corresponde con lo que el usuario ve en la ventana (o en toda la pantalla, depende de sdlVideoFlags).

Vamos completar los elementos del bucle que nos faltaban:

  • toca_redibujar: donde verifcaremos que estamos dibujando 60 cuadros por segundo, por ejemplo.
  • actualiza_estados: comprobaremos las teclas ARRIBA y ABAJO para regular un valor de luminosidad que emplearemos para pintar la superficie.
  • dibuja: pintaremos la pantalla según el valor de luminosidad que tengamos, así de simple (por ahora).

toca_redibujar

Para saber cuándo hay que dibujar la pantalla, y no estar constantemente dedicados a ese menester, vamos a emplear la salida de getTicks. Este método nos devuelve el número de milisegundos desde que la librería se inicializó.

Si restamos el número de ticks entre dos instantes sabremos entonces el número de milisegundos que han pasado, y convirtiendo el número de cuadros por segundo a esa escala (solo hay que hacer 1000/FPS) podremos saber si ha pasado el tiempo necesario entre redibujados.

Tendremos que añadir al código que ya teníamos:

# antes de entrar en bucle, para calcular
# FPS en la primera vuelta
tickAntes=SDL.getTicks

... dentro del bucle, tras capturar eventos ...

	# recoge los ticks actuales
	tickDespues=SDL.getTicks
	# toca dibujar?
        if tickDespues-tickAntes >= 1000/60
        	tickAntes = tickDespues
		
		# aquí actualizamos estados

		# aquí redibujamos
	end
end # fin del bucle

El código dentro de la condicional solo se ejecutará en las vueltas en las que haya pasado el tiempo suficiente.

actualiza_estados

En este caso solo vamos a incrementar o decrementar una variable, que será la que marcará el grado de luminisidad (en un intervalo de 0 a 255, que es donde se mueven los valores de color RGB).

Habrá que inicializar la variable fuera del bucle (a cero, por ejemplo), y la actualizamos antes de redibujar:

# antes del bucle inicializamos
luz = 0

... antes de redibujar, dentro de 'toca_redibujar' ...

		# actualizamos estados
		if SDL::Key.press?(SDL::Key::UP) && luz < 255
			luz += 1
		elsif SDL::Key.press?(SDL::Key::DOWN) && luz > 0
			luz -= 1
		end

		# aquí redibujamos
	end # fin del 'toca_redibujar'
end # fin del bucle

El código es muy sencillo y bastante explicativo. Lo único a destacar es que nos aseguramos de no salir del intervalo de valores válidos que hemos comentado antes.

dibuja

Como he comentado al principio, normalmente no trabajaremos de esta forma cuando estemos haciendo nuestro juego, porque emplearemos mapas de pixeles. Además implementaremos una ténica que se llama double buffering (o en castellano, buffer doble), pero hoy vamos a hacer las cosas algo más simples.

Solo tenemos que pintar la pantalla y decirle a la librería que actualize la superficie para que se refleje el resultado en la memoria de vídeo. Para ello usamos fillRect para pintar y updateRect para actualizar.

... tras haber actualizado estados ...

		# redibujamos
		pantalla.fillRect(0, 0, 640, 480, 
			pantalla.mapRGB(luz, luz, luz))

		# actualizamos toda la pantalla
		pantalla.updateRect(0, 0, 0, 0)
	
	end # fin del 'toca_redibujar'
end # fin del bucle

Se usa el valor de luz para mapear una terna RGB (rojo, verde, azul) a la profundidad de bits por pixel que hayamos puesto en nuetro modo de vídeo empleando el método mapRGB.

Así finalmente tenemos completo nuestro bucle del juego que mencionábamos en la primera entrega, aunque esto no es un juego :).

Habiendo asimilado hasta este punto, ya solo falta explicar algunas cosas relacionadas con la animación de mapas de pixeles, que serán el fundamento real de algo que ya podremos llamar 'juego'. Hay que ir pensando qué vamos a implementar, aunque el mejor candidato para esto es Tetris, porque tiene todos los componentes esenciales, y además alguna ventaja (como lo fácil que es preparar los gráficos :D).

Anotación por Juan J. Martínez, clasificada en: sdl, ruby, programming.

Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.

Algunas anotaciones relacionadas: