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