Mi Desktop

Snapshot
G.I.R.

Impacto Visual

iv(203).jpg

Dicen los que saben

Quien mas sabe de toros... son las vacas.

Claudia Quijas

Encuesta

Paranoid Puppets
 

Syndicate

Estadisticas

OS: Linux
PHP: 5.2.11
MySQL: 5.0.84-percona-highperf-b18
Time: 20:29
Caching: Disabled
GZIP: Disabled
News: 177
Visitors: 171026
We have 3 guests online

Buscar en el sitio

Fedora 12 - Countdown


Start arrow Tutoriales arrow Pong [Tutorial DirectX #2]
Pong [Tutorial DirectX #2] E-mail
lunes, 31 marzo 2008
Pong

Este es el segundo tutorial sobre DirectX, en esta ocasión vamos hacer uno de los juegos mas famosos, Pong, aprenderemos a cargar texturas, manejar el “loop de juego”, un poquito de programación orientada a objetos entre otras cosas.

Para realizar este ejercicio se debe pasar por el anterior, este lo encuentran en este link o en la sección de Tutoriales con el nombre de “Primer F5”. Tambien necesitan las texturas, esas estan aqui: http://elias.exofire.net/downloads/Pong/TexturasPong.zip

Esto es necesario ya que en este utilizaremos el mismo proyecto y agregaremos código para realizar el Pong, y en este tutorial no describiremos el código anterior y solo lo pondremos en letra gris, aunque sigue siendo necesario.

Comencemos.

Lo primero que debemos hacer es abrir nuestro proyecto anterior y agregar las siguientes librerías.

#include <d3d9.h>

#include <d3dx9.h>

#include <d3dx9core.h>

Estas nuevas librerías contienen más estructuras y métodos que vamos a utilizar, por ejemplo las Texturas.

Después agregamos dos estructuras.

Nota: En C++ la estructura [struct] y la clase [class] son idénticas en comportamiento, solo que la clase por default define todos sus atributos como privados y las estructuras como públicos, ¿entonces, que debemos de usar?, fácil, si la interfaz es el objeto per se, entonces se usa una estructura de lo contrario se usa una clase.

#define null NULL

struct Objeto

{

      IDirect3DTexture9* Textura;

      D3DXVECTOR3 Posicion;

};

struct ObjetoBola : public Objeto

{

      int VelX, VelY;

};

 
La primera define lo que será un objeto dentro del juego [en este caso las paletas y la pelota] por eso contiene una D3DVECTOR3 de posición en la pantalla y una IDirect3DTexture9* que contendrá la imagen del objeto.

La segunda estructura hereda de la estructura Objeto para definir mas detalladamente a la pelota, la cual necesita velocidad, así tiene una textura y posición y además velocidades en los ejes X y Y.

Ahora agregamos un sprite

IDirect3D9* D3D9 = null;

IDirect3DDevice9* D3D9Dispositivo = null;

ID3DXSprite* Sprite;

 
El sprite es el encargado de dibujar todo el contenido en 2D en la pantalla, normalmente es usado para realizar gestos en los personajes, mostrar nueve o lluvia, además de que nos puede ayudar a mostrar menús o HUD’s.

Después de esto debemos crear los objetos del juego

Objeto* PaletaJugador;

Objeto* PaletaCPU;

ObjetoBola* Bola;


Como se lee, definimos la paleta del jugador y del CPU [contra quien jugaremos] y finalmente la pelota que llamaremos Bola.

A continuación van nuestras funciones prototipo, como podrás notar aquí habrá un gran cambio, el método:

__w64 long __stdcall Procedimientos(HWND hWindow,                                                                           unsigned int Mensaje,                                                                           __w64 unsigned int wParametros,
                                    __w64 long lParametros)

 
Y el método:

int __stdcall WinMain(HINSTANCE hInst, HINSTANCE, char*, int)

 
Serán movidos hasta la parte inferior del código, lo único que tienes que hacer es copiarlos y pegarlos en la última línea tu código fuente y así solo quedara una función prototipo:

void Liberar();

 
Y el siguiente método [o el primero] que tendrás será:

long Iniciar(HWND hWindow)

 
En el método Iniciar debemos agregar el código que se encargara de inicializar el sprite, las texturas.

long Iniciar(HWND hWindow)

{

      if (null == (D3D9 = Direct3DCreate9(D3D_SDK_VERSION)))

            return E_FAIL;

      D3DDISPLAYMODE ModosDisplay = {0};

      D3D9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &ModosDisplay);

 

      D3DPRESENT_PARAMETERS pPresentacion = {0};

      pPresentacion.Windowed = true;

      pPresentacion.SwapEffect = D3DSWAPEFFECT_DISCARD;

      pPresentacion.BackBufferFormat = ModosDisplay.Format;

      pPresentacion.EnableAutoDepthStencil = true;

      pPresentacion.AutoDepthStencilFormat = D3DFMT_D16;

 

      if (FAILED(D3D9->CreateDevice(D3DADAPTER_DEFAULT,

            D3DDEVTYPE_HAL,

            hWindow,

            D3DCREATE_SOFTWARE_VERTEXPROCESSING,

            &pPresentacion,

            &D3D9Dispositivo)))

      {

            return E_FAIL;

      }

 
Recuerda que el código en gris, ya lo tienes, desde el primer tutorial por eso esta en color, el resto es código nuevo.

Ahora debemos agregar un estado de renderización, activamos el canal alfa, ósea el canal de las imágenes, que permite las transparencias.

      ::D3D9Dispositivo->SetRenderState(D3DRS_ALPHABLENDENABLE, true);

 
Después de esto debemos de crear el sprite, llamando al método D3DXCreateSprite, claro, controlando errores con el macro FAILED.

      if (FAILED(D3DXCreateSprite(::D3D9Dispositivo, &Sprite)))

            return E_FAIL;

 
En seguida debemos instanciar nuestros objetos, usando el operador ‘new’ debido a que estamos manejando punteros.

Nota: La reservación de memoria es un tema inmenso y no lo abordaremos, solo aceptaremos el hecho de que estamos obteniendo memoria suficiente para la información de nuestro objeto.

      ::PaletaJugador = new Objeto();

 
Después de instanciar el objeto, debemos de definir la posición y cargar la textura.

      ::PaletaJugador->Posicion = D3DXVECTOR3(5.0f, 200.0f, 0.0f);

 
En la posición debemos de asignar las posiciones en ‘X’, ‘Y’ y ‘Z’, ya que este es un juego en 2D, la posición en Z es irrelevante así que le asignamos 0.

Ponemos la paleta del jugador a 5 pixeles en el eje ‘X’ y 200 en el eje ‘Y’.

La textura la usamos con el método [macro, correctamente] D3DXCreateTextureFromFile, al cual debemos de pasar el dispositivo que se encargara del juego; como segundo parámetro el nombre del archivo de la textura y como ultimo parámetro la referencia a memoria de la textura de nuestra paleta del jugador. Una vez más controlando el error que se pueda generar de esta operación.

      if (FAILED(D3DXCreateTextureFromFile(::D3D9Dispositivo, L"tJugador.jpg", &PaletaJugador->Textura)))

            return E_FAIL;

 
Repetimos estos 2 pasos con nuestros otros 2 objetos, que son la paleta del CPU y la pelota, cada una en su respetivo sitio y con su respectiva imagen.

      ::PaletaCPU = new Objeto();

      ::PaletaCPU->Posicion = D3DXVECTOR3(758.0f, 200.0f, 0.0f);

      if (FAILED(D3DXCreateTextureFromFile(::D3D9Dispositivo, L"tCPU.jpg", &PaletaCPU->Textura)))

            return E_FAIL;

     

      ::Bola = new ObjetoBola();

      ::Bola->Posicion = D3DXVECTOR3(350.0f, 250.0f, 0.0f);

 
A la pelota debemos de agregarle la velocidad que serán 2 pixeles en las dirección de ‘X’ y ‘Y’.

      ::Bola->VelX = ::Bola->VelY = 2;

      if (FAILED(D3DXCreateTextureFromFile(::D3D9Dispositivo, L"tBola.jpg", &Bola->Textura)))

            return E_FAIL;

 
Despues asignamos otro estado, solo que esta vez sera al sampleador, le asignamos un filto para las imágenes.

      ::D3D9Dispositivo->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

 

      return S_OK;

}

 
Este método es nuevo y un poco largo y complicado, pero mas que explicárselos paso a paso ustedes lo deben de analizar, es la lógica del juego.

void Update()

{

 
Lo primero es mover la pelota en los ejes ‘X’ y ‘Y’ con la ayuda de las velocidad que definimos anteriormente.

      ::Bola->Posicion.x += ::Bola->VelX;

      ::Bola->Posicion.y += ::Bola->VelY;

 
Después tenemos nuestra Inteligencia Artificial de la época de las cavernas, esta se basa en verificar la posición de la pelota y compararla con la posición de la paleta del CPU, si la pelota esta por encima de la mitad de la paleta se moverá hacia arriba, en caso que este por debajo, se moverá hacia abajo.

¿Merezco un premio novel no?

La neta no, por que con este método el CPU nunca pierde, ya que las velocidades nunca cambian y siempre se moverá mas rápido que la pelota, recuerda que la pelota se mueve a 2 pixeles y el CPU a 3, pero esto nos reduce un poco el código por que este tutorial no se enfoca en estos temas.     

      //IA

      if (::PaletaCPU->Posicion.y + 64 < Bola->Posicion. y)

            PaletaCPU->Posicion.y += 3.0f;

      else if (::PaletaCPU->Posicion.y + 64 > Bola->Posicion. y)

            PaletaCPU->Posicion.y -= 3.0f;

 Ahora los choques. Debemos de manejar los rebotes.

Primero controlamos que la pelona no se vaya hacia el infinito [o menos infinito] en el eje Y, así que cuando su posición sobrepase los limites de la pantalla, la hacemos rebotar, muy  fácil, multiplicando por -1 sus velocidades, claro, todo depende del eje que estemos checando, aunque en ‘X’ no se debería de manera ya que cuando rebasa las paletas el jugador pierde.

Notaras que a las posiciones se le suman varios valores, esto es para manejar el largo de la pelota

      //Colisiones

      if (::Bola->Posicion.y <= 0.0f)

            ::Bola->VelY *= -1;

      else if (::Bola->Posicion.y + 32.0f + 24.0f >= 600.0f)

            Bola->VelY *= -1;

 

      if (Bola->Posicion.x <= 0.0f)

            Bola->VelX *= -1;

      else if (Bola->Posicion.x + 32.0f >= 800.0f)

            Bola->VelX *= -1;

 
Ahora debemos de controlar que las paletas no salgan de pantalla, igualmente usamos la posición en ‘Y’ [en ‘X’ no hay movimiento] y lo verificamos con los limites de la pantalla. Debemos de controlar la colisión de la paleta del CPU y del jugador.

      if (::PaletaJugador->Posicion.y <= 0.0f)

            ::PaletaJugador->Posicion.y = 0.0f;

      if (::PaletaJugador->Posicion.y + 160.0f >= 600.0f)

            ::PaletaJugador->Posicion.y = 440.0f;

     

      if (::PaletaCPU->Posicion.y <= 0.0f)

            ::PaletaCPU->Posicion.y = 0.0f;

      if (::PaletaCPU->Posicion.y + 160.0f >= 600.0f)

            ::PaletaCPU->Posicion.y = 440.0f;

 
Y por ultimo creamos el código para los rebotes en las paletas, primero debemos de verificar la posición en ‘X’ de la pelota. Se sumaran 32 a la posición en ‘X’ de la paleta ya que esto representa el ancho, debido a que las posiciones están en la esquina superior izquierda de los objetos.

      if (Bola->Posicion.x < ::PaletaJugador->Posicion.x + 32)

      {

 
Si sobrepasa la paleta, debemos checar que la paleta este en ese lugar para hacer rebotar la pelota, esto se hace checando las posiciones en ‘Y’ de la pelota y la paleta. Hemos sumado 16 ya que este representa la mitad de la pelota.

            if (Bola->Posicion.y + 16.0f < ::PaletaJugador->Posicion.y ||

                  Bola->Posicion.y + 16.0f > ::PaletaJugador->Posicion.y + 128.0f)

            {

 
Si  no se encuentra entre los límites de la paleta, mandamos un mensaje de que se ha perdido el juego y en seguida, llamamos al método que liberara los recursos usados y se sale de la aplicación con el método ‘exit’ con código de salida 0, que significa que salió sin error.

Bueno, del jugador si, pero del código no.

                  MessageBox(null, L"Has perdido", L"Has perdido", MB_OK);

                  ::Liberar();

                  exit(0); // GAME OVER

            }

            else

 
En caso contrario multiplicamos por -1 la velocidad en ‘X’ de la pelota para hacerla rebotar.

                  Bola->VelX *= -1;

      }

 
Para terminar hacemos rebotar la bola en la paleta del CPU, no es necesario el código que controla el hecho de que el CPU pierda ya que esto no sucede en este ejemplo, como fue explicado anteriormente.

      if (Bola->Posicion.x + 33.0f > ::PaletaCPU->Posicion.x)

                        Bola->VelX *= -1;

 

     

}

void Renderear()

{

      if (::D3D9Dispositivo == null)

            return;

      ::D3D9Dispositivo->Clear(0, null, D3DCLEAR_TARGET, 0xFFFFFFFF, 1.0f, 0);

      if (SUCCEEDED(::D3D9Dispositivo->BeginScene()))

      {

 

Ya dentro del método que se encarga de renderear nuestro juego y dentro de la escena, debemos de iniciar el spirte llamando al método Begin, pasando como parámetro el modo de rendereo, que en este caso se usa el alpha-blending que permite transparencias y suaviza

            if (SUCCEEDED(::Sprite->Begin(D3DXSPRITE_ALPHABLEND)))

            {

 
Después de iniciar el sprite podemos iniciar a dibujar en pantalla todos los elementos del juego. Esto lo hacemos con la ayuda del método Draw, al cual hay que pasarle como primer parámetro la textura que se dibujara, después 2 parámetros como null, que son offsets para el rectángulo en la imagen y el centro de esta, el cuarto parámetro es la posición en la pantalla y el ultimo es la tinta de la textura, para conservar los colores originales se debe de pasar el color blanco en valor hexadecimal.

                  ::Sprite->Draw(::PaletaJugador->Textura, null, null, &::PaletaJugador->Posicion, 0xFFFFFFFF);

 
Repetimos esto para la paleta del CPU.

                  ::Sprite->Draw(::PaletaCPU->Textura, null, null, &::PaletaCPU->Posicion, 0xFFFFFFFF);

 
También para la pelota.

                  ::Sprite->Draw(::Bola->Textura, null, null, &::Bola->Posicion, 0xFFFFFFFF);

 
Finalizamos el sprite con el método End, finalizamos la escena y la presentamos.

                  ::Sprite->End();

            }

            ::D3D9Dispositivo->EndScene();

      }

      ::D3D9Dispositivo->Present(null, null, null, null);

}

 
Continuamos con la función que nos permitirá liberar los recursos de DirectX, es parecido al anterior, solo esta vez hay que liberar la memoria que uso el sprite y las texturas.

void Liberar()

{

 
Primero verificamos si el puntero va a la memoria y no a 0. En caso positivo llamamos al método Relase.

      if (::Sprite != null)

            ::Sprite->Release();

 
Repetimos estos pasos para las tres texturas y después liberamos el dispositivo, tal como en el tutorial anterior.

      if (::PaletaCPU->Textura != null)

            ::PaletaCPU->Textura->Release();

 

      if (::PaletaJugador->Textura != null)

            ::PaletaJugador->Textura->Release();

 

      if (::Bola->Textura != null)

            ::Bola->Textura->Release();

 

      if(::D3D9Dispositivo != null)

            ::D3D9Dispositivo->Release();

 

      if(::D3D9 != null)

            ::D3D9->Release();

 

}

 
Solo quedan 2 funciones mas, que ya teníamos solo fueron cambiadas a la parte baja del archivo, estas quedan iguales, salvo una excepción en la siguiente función.

__w64 long __stdcall Procedimientos(HWND hWindow,

                                   unsigned int Mensaje,

                                   __w64 unsigned int wParametros,

                                   __w64 long lParametros)

{

      switch(Mensaje)

    {

 
Al manejar el mensaje del sistema verificamos si existe el caso en que una tecla ha sido presionada, si es así verificamos si es ‘ARRIBA’, ‘ABAJO’ o ‘ESC’, con esto podremos salir del juego presionando la tecla ‘escape’, y movemos la paleta del jugador.

Nota: Este no era el método acostumbrado de manejar la entrada de datos, normalmente se usaba DirectInput aunque actualmente es como se maneja.

      case WM_KEYDOWN:

            {

                  switch(wParametros)

                  {

                  case VK_UP:

                        ::PaletaJugador->Posicion.y -= 4.0f; break;

                  case VK_DOWN:

                        ::PaletaJugador->Posicion.y += 4.0f; break;

                  case VK_ESCAPE:

                        {

                             Liberar();

                             PostQuitMessage(0);

                             return 0;

                        }

                  }

                  break;

            }

      case WM_DESTROY:

            {

                  Liberar();

                  PostQuitMessage(0);

                  return 0;

            }

      }

      return DefWindowProc( hWindow, Mensaje, wParametros, lParametros );

}

int __stdcall WinMain(HINSTANCE hInst, HINSTANCE, char*, int)

{

      WNDCLASSEX ClaseWnd = {0};

      ClaseWnd.cbSize = sizeof(ClaseWnd);

      ClaseWnd.lpszClassName = L"PrimerF5";

      ClaseWnd.lpfnWndProc = Procedimientos;

      ClaseWnd.style = CS_CLASSDC;

      ClaseWnd.hInstance = GetModuleHandle(null);

      RegisterClassEx(&ClaseWnd);

      HWND hWindow = CreateWindow(L"PrimerF5",

            L"PrimerF5",

            WS_SYSMENU,

            10,

            10,

            800,

            600,

            null,

            null,

            ClaseWnd.hInstance,

            null);

 

      if (SUCCEEDED(::Iniciar(hWindow)))

      {

            ShowWindow(hWindow, SW_SHOWDEFAULT);

            UpdateWindow(hWindow);

            MSG Mensaje;

            ZeroMemory(&Mensaje, sizeof(Mensaje));

            while (Mensaje.message != WM_QUIT)

            {

                  if (PeekMessage(&Mensaje, null, 0U, 0U, PM_REMOVE))

                  {

                        TranslateMessage(&Mensaje);

                        DispatchMessage(&Mensaje);

                  }

                  else

                  {

                        ::Update();

                        ::Renderear();                    

                  }

            }

      }

      UnregisterClass(L"PrimerF5", ClaseWnd.hInstance );

      return 0;

}

 Solo resta correr el programa y ver los resultados, el codigo fuente lo encuentras aqui: http://elias.exofire.net/downloads/Pong/Programa.cpp

 
< Prev   Next >
© 2009 scumbag > scmbg
Joomla! is Free Software released under the GNU/GPL License.