Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Fisica | Tutoriales | Links
usando
DirectDraw 7.0 en Visual Basic
Por Rodrigo Pelorosso (Assembly)
ANTES DE EMPEZAR
Antes de hacer la
introducción a DirectDraw en Visual Basic, y el posterior desarrollo de algunos
temas relacionados, quiero aclarar que no soy un programador profesional (y
mucho menos escritor), de modo que la presente información puede contener (he
tratado de evitarlos) errores.
Como una breve reseña histórica, decidí escribir este tutorial debido a que
mucha gente entraba al canal #programadores de irc.ciudad.com.ar, preguntando
sobre el manejo de gráficos en Visual Basic, para todos ellos, y los demás,
espero que les sea de ayuda. El codigo fuente que acompaña este tutorial puede
ser conseguido aqui
INTRODUCCIÓN
La gran pregunta es
"que es DirectDraw?"
DirectDraw es una parte de la API Microsoft DirectX, que se encarga del manejo
de imágenes 2D, almacenados en buffers (que técnicamente, son bloques de
memoria), usaremos DirectDraw (de aquí en mas DD) para manipular esos bloques,
y mostrarlos en la pantalla de la forma que mas nos convenga, ahora bien,
cualquiera podaría preguntar
"por que no usar el GDI de windows?"
la respuesta es sencilla, el GDI ES LENTO, DD esta preparado para sacarle
provecho al Hardware instalado en el sistema haciendo que el proceso sea mas
rápido, ademas, DD nos permite
- Manipular múltiples Superficies
- Acceder a la memoria de vídeo directammente
- Page Flipping
- Back Buffering
- Manejo de la paleta de colores
- Clipping
Algunos términos pueden parecer extraños y oscuros, pero van a ver que son muy
sencillos y útiles.
GRÁFICOS 2D: CONCEPTOS GENERALES
Vamos a analizar ahora algunos términos y técnicas que usaremos a lo largo de este tutorial
Blitting
Blit es la
abreviación de "Bit Block Transfer" o "Transferencia de Bloques
De Bits", que es el proceso de transferir un bloque de Bits de un lugar de
la memoria a otro, el Blitting lo usaremos para implementar Sprites.
Una aclaración, como no encontré un verbo en castellano correspondiente a Blit,
así que Vamos a inventar un verbo: "blitear"
Superficies
Las superficies o
surfaces, (en DD, DirectDrawSurface) representan un área lineal de la memoria
de video. Una superficie usualmente reside en la memoria de la placa de video,
pero en algunos casos también puede existir en la memoria del sistema.
Los objetos DirectDrawSurface pueden tomar ventaja de procesadores
especializados en placas de video, no solo para desarrollar algunas operaciones
mas rápido, sino también para trabajar en paralelo con el CPU.
Por si se necesita la aclaración, en las superficies guardaremos las imágenes
que usaremos a lo largo de nuestro programa.
Color Keying
DirectDraw soporta
Color Keying de destino y origen.
Color Keying se llama a la técnica que permite mostrar una imagen arriba de
otra selectivamente, esto significa que ciertos pixeles del rectángulo de
arriba serán mostrados y solo ciertos pixeles del de abajo serán sobrescritos.
Por lo general siempre usaremos Color Keying de Origen, para hacer que una
imagen tenga fondo transparente, y así hacer que se adapte perfectamente al
fondo. (si, Titus the Fox era cuadrado :P)
Page
Flipping y Back Buffering
Page Flipping se
denomina a la acción de pasar el contenido de una Superficie o Surface a otra.
Esto se utiliza en todos los juegos y es imprescindible, de otro modo el
usuario vería el orden y la forma en la que se dibujan los objetos.
El programador debe dibujar en una Superficie que no se ve (denominada Back
Buffer) y luego transferir su contenido al Primary Surface (que es la
Superficie que el usuario ve en todo momento). La figura siguiente figura
muestra esta situación para una animación de 3 cuadros.
DIRECTDRAW: SU ARQUITECTURA
Bien, ahora que ya revisamos algunos conceptos generales, teóricamente estaríamos en condiciones de entrar a lo que todos queríamos entrar, la estructura de DirectDraw, vamos a ver que componentes lo componen, y para que sirven, el DD, así como el DirectX, son objetos COM (Component Object Model), no es imprescindible, pero recomiendo buscar alguna info sobre eso, solo para que sepan de que se trata, es algo que no explicare aquí.
Componentes:
Como todos los
demás componentes de DirectX, DirectDraw utiliza al Hardware para realizar
todas las operaciones soportadas, si no es posible, DirectX las simula por
Software. Los componentes de DirectDraw proveen servicio a través de las clases
definidas en la librería de DirectX para Visual Basic, que son
- DirectDraw7
- DirectDrawSurface7
- DirectDrawPalette
- DirectDrawClipper.
Además, la librería provee objetos para enumerar modos, drivers y superficies,
estos son
- DirectDrawEnum
- DirectDrawEnumModes
- DirectDrawEnumSurfaces
El objeto DirectDraw creado de la clase DirectDraw7 representa el adaptador de
video, para crear este objeto se debe usar el método DirectDrawCreate del
objeto global DirectX7.
Después de crear el objeto DirectDraw se pueden crear las Superficies llamando
al método DirectDraw7.CreateSurface. .. si te sentís mareado, no desesperes,
vas a ver que es muy simple y metódico.
De los objetos mencionados, nos limitaremos solo a explicar el DirectDraw7 y
DirectDrawSurface7, los demas no los cubriremos en este tutorial, de todas
formas, se pueden hacer cosas muy poderosas con estos dos, y son la base para
entender todos los demas.
DirectDraw
El objeto DirectDraw es el corazón de todas las aplicaciones de DirectDraw, es el primer objeto que se crea y el que se usa para crear todos los demás objetos. El objeto DirectDraw se crea llamando al método DirectX7.DirectDrawCreate. Estos exponen su funcionalidad a través de los métodos del objeto DirectDraw7.
DirectDrawSurface
El objeto DirectDrawSurface representa un área en memoria que almacena datos que serán mostrados en pantalla como imágenes, o movidos a otra superficie. Estos objetos se crean llamando al método DirectDraw7.CreateSurface del objeto DirectDraw con el cual estará asociado. Los objetos DirectDrawSurface exponen su funcionalidad a través de los métodos del objeto DirectDrawSurface7.
Cooperative Levels
Los Cooperative
Levels (cuya traducción seria Niveles Cooperativos, aunque aquí siempre nos
referiremos a ellos con su nombre original) describen como DirectDraw
interactuará con la pantalla y como reaccionara a eventos que puedan afectarla.
A través de los Cooperative Levels se define si una aplicación correrá como un
programa de pantalla completa (Full Screen) con un acceso exclusivo a la misma,
o como una ventana. De todas formas, los Cooperative Levels pueden tener los
siguientes efectos.
- Permitir a DirectDraw el uso de resoluuciones de Modo X (Mode X)
- Prevenir que DirectDraw suelte el conttrol exclusivo o que se resetee el
sistema si el usuario presiona CTRL+ALT+DEL
- Permitir a DirectDraw Minimizar o Maxiimizar la aplicación en respuesta a la
activación de eventos
El Cooperative Level normal indica que la aplicación de DirectDraw funcionara
en modo ventana, de esta forma no se podrá cambiar la paleta de la Superficie
Primaria (Primary Surface) o realizar Page Flipping.
Complex
Surfaces y Flipping Chains
Un Complex Surface
(Superficie Compleja) es una serie de superficies que se crean con un simple
llamado al método DirectDraw7.CreateSurface. Si la bandera DDSCAPS_COMPLEX esta
puesta cuando se llama a CreateSurface, DirectDraw crea implícitamente una o mas
superficies en suma a la especificada explícitamente. Las superficies complejas
se tratan como si fueran una simple. Una de las Superficies Complejas mas
útiles que se puede crear es la que se denomina Flipping Chain (cuya traducción
seria algo así como Cadena de Transferencia o cambio) que normalmente esta
compuesta por un Primary Surface y uno o mas Back Buffers, la bandera
DDSCAPS_FLIP indica que una superficie es parte de una cadena.
Una vez que la cadena esta creada, una simple llamada a DirectDrawSurface7.Flip
intercambia el contenido de la Superficie Primaria y el Back Buffer.
Si en lugar de crear un buffer secundario, creamos dos, podremos implementar lo
que se denomina Triple Buffering, pero aquí lo dejaremos de lado, debido a que
el Doble Buffering es igual de efectivo, y personalmente creo que no vale la
pena implementar el Triple.
Superficies Off-Screen
Las superficies
Off-Screen (o fuera de la pantalla) son a menudo utilizadas como imágenes cache
que luego serán transferidas al Back o Primary buffer (cuando nombramos buffer,
en este caso, nos referimos a Surface).
Por defecto DirectDraw crea una superficie en la memoria de video a menos que
estas no entre, en tal caso se creará en la memoria del sistema.
PROGRAMANDO CON DIRECTDRAW
fuif, eso fue bastante,
creo que ahora estamos en condiciones de empezar a programar un poco y mas o
menos entender que es lo que estamos haciendo.
Bien, supongo que a esta altura estarás pensando
"AARGGHHH! SUFICIENTE! QUIERO PROGRAMAR !!"
ok, let's do it, antes de empezar a programar, debemos incluir en el proyecto
de visual basic, una referencia al objeto DirectX 7.0, para eso, vamos al menu
Proyecto/Referencias, buscamos Microsoft DirectX 7.0 (o algo así), lo marcamos,
y ya estamos listos para empezar a codificar.
Para este tutorial, nos basaremos en el código que acompaña al mismo, que puede
ser bajado haciendo click AQUI, recomiendo
tenerlo abierto mientras se lee esto, así se puede ver todo en conjunto.
Ok, Una vez que tenemos el proyecto con la referencia indicada, lo primero que
debemos hacer es declarar los objetos de DirectDraw que usaremos, junto con el
objeto DirectX, que es de donde salen todos los demas (absolutamente todos,
DDraw, D3D, DSound, DPlay...), a continuación esta el código de declaración de
los objetos (en General/Declaraciones).
Dim DirectX As New DirectX7 ' Objeto DirectX
Dim DirectDraw As DirectDraw7 ' Objeto DirectDraw
Bien, a continuación, vamos a declarar las superficies que usaremos, en nuestro
ejemplo, tenemos 3 simpáticas (?) caritas que se mueven sobre un fondo BMP, en
total necesitaremos cuarto superficies, una para el Fondo, otra para el sprite
(la carita), y otros dos para el primary y back buffer.
Dim Fondo As DirectDrawSurface7
Dim Sprite As DirectDrawSurface7
Dim Primary As DirectDrawSurface7
Dim BackBuffer As DirectDrawSurface7
bueno, creo que el nombre lo dice todo, no? :P
Ahora bien, hay algo que no nombre hasta ahora, existe un tipo de estructura,
la DDSURFACEDESC2, que se usa en el proceso de creación de cualquier
superficie, para describir a la superficie siendo creada, uno llena la
descripción a la medida de las necesidades, y le dice a DD que quiere una
superficie así, pasandole como parámetro la descripción llenada.
Dim Desc1 As DDSURFACEDESC2
Dim Desc2 As DDSURFACEDESC2
Dim Desc3 As DDSURFACEDESC2
Dim Desc4 As DDSURFACEDESC2
Usaremos cuatro descripciones, una para cada superficie.
yeey!, ahora que tenemos esas cosas declaradas, vamos a inicializar todo, en la
subrutina iniciar(), se encuentra las funciones de inicialización.
' Creamos el dispositivo DD
Set DirectDraw = DirectX.DirectDrawCreate("")
' Indicamos que queremos pantalla completa en modo exclusive
Call DirectDraw.SetCooperativeLevel(Me.hWnd, DDSCL_FULLSCREEN Or DDSCL_ALLOWMODEX
Or DDSCL_EXCLUSIVE)
' Configuramos el modo de video
DirectDraw.SetDisplayMode 640, 480, 16, 0, DDSDM_DEFAULT
Aqui, lo que hacemos es, primero, creamos el dispositivo DirectDraw, llamando a
la función DirectDrawCreate, del objeto DirectX.
Cuando configuramos el Cooperative Level, le pasamos el manejador de la ventana
(me.hWnd), y le indicamos que queremos pantalla completa, con posibilidad de
Modo X (no lo voy a explicar, en internet esta lleno de información sobre
esto), y modo Exclusive, todo esto junto, hace que la aplicación sea la
principal entre todas las ventanas que se encuentren abiertas, por lo general
siempre vamos a configurar el Coop. Level de esta forma.
Por ultimo, seteamos el modo gráficos (640x480?!, en que estaba pensando?!
320x200 es lo mejor!)
Ahora, vamos a crear la flipping chain,
'Creamos el primary surface y el backbuffer
Desc1.lFlags = DDSD_CAPS Or DDSD_BACKBUFFERCOUNT
Desc1.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX
Desc1.lBackBufferCount = 1
Set Primary = DirectDraw.CreateSurface(Desc1)
'el Back Buffer
Dim Caps As DDSCAPS2
Caps.lCaps = DDSCAPS_BACKBUFFER
Set BackBuffer = Primary.GetAttachedSurface(Caps)
BackBuffer.GetSurfaceDesc Desc4
Lo que hacemos aqui es configurar la descripción del Primary Buffer, indicando
que será Primario, con capacidad de flip, y parte de una cadena, con 1 back
buffer, una vez creado, le atachamos (otro verbo creado? la traducción seria
adjuntar, pero bue..) el Back buffer, y así terminamos de crear la flipping chain.
No te preocupes si ves que este código es largo, o difícil de memorizar, o lo
que sea, por lo general, lo que hace uno es crearlo una vez sola, y después en
todos los programas, es un simple copy/paste.
BackBuffer.SetForeColor vbGreen
BackBuffer.SetFontTransparency True
por ultimo, indicamos que las letras del backbuffer serán verdes y
transparentes.
ahora bien, en la rutina iniciarsuperficies(), tenemos la inicialización de las
superficies off-screen, usadas para guardar el fondo y el sprite.
' Creamos la superficie y le Cargamos el Bmp.
' del mismo tamaño que el Back Buffer (desc4)
Desc2.lFlags = DDSD_CAPS Or DDSD_HEIGHT Or DDSD_WIDTH
Desc2.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN
Desc2.lWidth = Desc4.lWidth
Desc2.lHeight = Desc4.lHeight
Set Fondo = DirectDraw.CreateSurfaceFromFile(App.Path + "\nubes.bmp",
Desc2)
aqui creamos la superficie que almacena el fondo, en el miembro lFlags,
indicamos que miembros de la descripcion deben ser tomados en cuenta a la hora
de crear la superficie (me parece a mi, o es un método muy malo?), después,
indicamos el tamaño y decimos que será offscreen, una vez completada la
descripcion, creamos la superficie llamando a CreateSurfaceFromFile (un punto a
favor, no debemos programar nuestra rutina de carga de bmps).
Ahora, creamos la superficie que contendrá al sprite, una vez mas, llenamos la
descripcion, y llamamos al método de creación, notar que no especificamos las
dimensiones (nombre que si cargamos de un bmp no hace falta porque las saca de
ahí?), sips, cuando creamos el fondo, podríamos no haberlas especificado.
'Creamos y cargamos el bmp. en la superficie Sprite
Desc3.lFlags = DDSD_CAPS
Desc3.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN
Set Sprite = DirectDraw.CreateSurfaceFromFile(App.Path +
"\sprite.bmp", Desc3)
'Hacemos que el Sprite tenga color transparente (key color)
Dim Key As DDCOLORKEY
Key.low = 0
Key.high = 0
Sprite.SetColorKey DDCKEY_SRCBLT, Key
"hey!, hay algo nuevo ahí!"
ajaps, seteamos el color key, un color que cuando se dibuje la imagen, será
ignorado, teniendo un color transparente, el tipo DDCOLORKEY tiene un rango,
ente low y high, pero como queremos que el color negro sea el color key, le
pasamos 0 en ambos.
"ya esta? falto inicializar algo mas? quiero dibujar!"
no!, listo, ahora podemos escribir en el back buffer, y con un simple llamado a
Flip, veremos el contenido del mismo.
Copiando imágenes entre superficies
Antes de empezar
con esto, falta introducir un concepto mas, los Tipos RECT, los mismos tienen
cuatro miembros, e indican 2 coordenadas (sup-izq,inf-der) que forman un
rectángulo, usaremos estos tipos para indicar que porcion de una superficies
queremos copiar.
ahora si, vamos a ver la función BltFast, la misma tiene la siguiente forma
<sup
destino>.BltFast <x>, <y>, <supo origen>, <RECT
origen>, <opciones>
Por ejemplo, cuando copiamos los sprites al back buffer, hicimos lo siguiente
dim rSprite as RECT
rSprite.Top = 0
rSprite.Bottom = Desc3.lHeight
rSprite.Right = Desc3.lWidth
rSprite.Left = 0
BackBuffer.BltFast x, y, Sprite, rSprite, DDBLTFAST_SRCCOLORKEY Or
DDBLTFAST_WAIT
esto es, en el back buffer, copiamos en las coordenadas x e y, desde la
superficie Sprite, la porcion rectangular especificada por rSprite. Como en
opciones, incluimos DDBLTFAST_SRCCOLORKEY, entonces la imagen tendrá pixeles
transparentes, indicados por el color key, si no ponemos esta opción, entonces
la imagen se copiara tal cual es (que es lo que hicimos cuando copiamos el
fondo).
Y bien, una vez que dibujamos todo lo que queríamos en el back buffer, llamamos
a la función blit del primary buffer, para copiar a este el contenido del back
buffer, y permitir así que el usuario vea lo que esta pasando.
' Pasamos el BackBuffer al Primary Surface
Primary.Flip Nothing, DDFLIP_WAIT
Una vez mas, el código fuente puede ser bajado haciendo click AQUI, el mismo contiene algunas partes
de código que no son estrictamente necesarias, y que no se explican aqui, pero
no se preocupen, no es necesario conocerlas demasiado.
ESO ES TODO
Bueno, eso es todo
por ahora, con lo que hemos aprendido hoy, es suficiente para hacer algo
regularmente bueno, tal vez algún día me digne a escribir algo mas avanzado,
por ahora, estoy trabajando en diversas rutinas 3D en Visual C++ con OpenGL
para un simulador de vuelo, así que estoy un bit ocupado, pero, planeo volver..
Si desean un poco mas de código fuente con el uso de DD en V.B., pueden bajarse
algunos juegos que programe hace algún tiempo, en www.canalprogramadores.com.ar Espero
que esto les haya sido de ayuda, si es así (o no), por favor, contactenme y
cuentenme un poco que les pareció, que planean hacer, y esas cosas, siempre es
bueno conocer gente nueva.
Quiero dedicar este
tutorial a una chica muy especial que conoci hace un tiempo, y ocupa mi mente
la mayor parte del tiempo, Paola te quiero! :)
Cualquier duda, comentario, donación :), o lo que sea, no duden en contactarme
a
ICQ#
115506483
MSN & Mail: silencer_ar@hotmail.com
Pero lo mas seguro es en el canal #programadores de irc.ciudad.com.ar, cuando
estoy on-line, estoy ahí. Cuidense, suerte.
Assembly 21/11/2002 Buenos Aires, Argentina
Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Fisica | Tutoriales | Links