Ir al contenido

publicidad
publicidad

Foto

Curso MM: 2 Creando un motor para juegos


Este tema ha sido archivado. Esto significa que no puedes responder en este tema.
3 respuestas en este tema

#1

Escrito 02 agosto 2009 - 05:04

Creando un motor para juegos

Ahora lo que vamos a hacer es ocultar el API de Windows para no tener que tratar con él y hacerlo sólo con las tareas que tienen que ver con el juego. Es responsabilidad del motor configurar el juego, asegurarse de que funciona correctamente y por último cerrarlo. Los juegos tienen una forma especial de hacer éstas cosas. La idea es hacer que sea lo más fácil posible desarrollando rutinas una sola vez, colocándolas en el motor de juego y no tener que volver a codificarlas nunca más.

Vamos a crear dos archivos nuevos: GameEngine.h y GameEngine.cpp. Y ahora lo que teníamos en Skeleton.cpp que era trabajar con el API de Windows lo vamos a llevar a GameEngine.cpp. Los mensajes del sistema pasarán por la sentencia switch y allí vamos a tratar los mensajes que nos interesen y a hacer que llamen a funciones que tenemos en Skeleton.cpp.

- Rompiendo un juego en Eventos:

Inicialización: es un evento en el que se cargan los gráficos y los sonidos, se limpia la pantalla, se resetean las puntuaciones y se inicializan variables, se crea el motor de juego,etc.

Inicio: inicio de un juego, inicialización de variables de cada sesión de juego.

Final: fin de un juego, limpieza de memoria y variables.

Activación: se vuelve de una pausa, se maximiza la ventana o se trae al frente del escritorio.

Desactivación: se pone el juego en pausa, se minimiza o se pone detrás de otra ventana.

Pintar: cuando se necesita dibujar en la ventana, similar a WM_PAINT.

Bucle: lleva a cabo un ciclo del juego.

- Estableciendo el ritmo para juegos. Todos los juegos con animaciones utilizan alguna forma de cronometraje para romper su ejecución en fotogramas o ciclos. Un ciclo es una porción de tiempo que normalmente se corresponde con el tratamiento una vez de los gráficos y datos del juego. 30 ciclos por segundo es un valor alto y 12 es lo más bajo para crear sensación de movimiento suave. Este valor hay que darlo al inicializar el motor. A partir de ahí el motor se encarga de que 12 veces (por ejemplo) por segundo se produzca un ciclo. Se redibuja y se hacen cálculos. Para pausar un juego simplemente se dejan de enviar éstos mensajes.

Desarrollando un motor de juego

- lo primero es crear funciones que se correspondan con eventos del juego, están definidas en GameEngine.h:

[code:1]BOOL GameInitialize(HINSTANCE hInstance);
void GameStart(HWND hWindow);
void GameEnd();
void GameActivate(HWND hWindow);
void GameDeactivate(HWND hWindow);
void GamePaint(HDC hDC);
void GameCycle();[/code]

En la primera de ellas hInstance es del tipo HINSTANCE que se refiere a una instancia de una aplicación. Permite a un programa acceder a sus recursos ya que se almacenan con la aplicación en memoria.

- La clase GameEngine está también en GameEngine.h . Los manejadores de eventos del juego están separados del motor de juego que está en ésta clase.

[code:1]class GameEngine
{
protected:
// Member Variables
static GameEngine* m_pGameEngine;
HINSTANCE m_hInstance;
HWND m_hWindow;
TCHAR m_szWindowClass[32];
TCHAR m_szTitle[32];
WORD m_wIcon, m_wSmallIcon;
int m_iWidth, m_iHeight;
int m_iFrameDelay;
BOOL m_bSleep;

public:
// Constructor(s)/Destructor
GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass,
LPTSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth = 640,
int iHeight = 480);
virtual ~GameEngine();

// General Methods
static GameEngine* GetEngine() { return m_pGameEngine; };
BOOL Initialize(int iCmdShow);
LRESULT HandleEvent(HWND hWindow, UINT msg, WPARAM wParam,
LPARAM lParam);

// Accessor Methods
HINSTANCE GetInstance() { return m_hInstance; };
HWND GetWindow() { return m_hWindow; };
void SetWindow(HWND hWindow) { m_hWindow = hWindow; };
LPTSTR GetTitle() { return m_szTitle; };
WORD GetIcon() { return m_wIcon; };
WORD GetSmallIcon() { return m_wSmallIcon; };
int GetWidth() { return m_iWidth; };
int GetHeight() { return m_iHeight; };
int GetFrameDelay() { return m_iFrameDelay; };
void SetFrameRate(int iFrameRate) { m_iFrameDelay = 1000 /
iFrameRate; };
BOOL GetSleep() { return m_bSleep; };
void SetSleep(BOOL bSleep) { m_bSleep = bSleep; };
};[/code]

Aquí podemos ver como se nombra a las variables miembro de una clase empezando por m_ , las variables globales se nombran empezando con _ pero sin m. Así se distinguen las locales, miembro y globales.

m_pGameEngine es un puntero a si misma que es usado para acceder desde el juego. m_hInstance es la instancia de la aplicación y m_hWindow es el handle de la ventana. El alto y ancho se corresponde con el área de juego, no con el de la ventana que incluye bordes, título, menús, etc. m_iFrameDelay indica el tiempo entre ciclos del juego en milisegundos. m_bSleep es un valor booleano que indica si el juego está pausado.

El constructor acepta parámetros que afectan mucho al juego que se crea. Acepta un handle de instancia, nombre de clase de la ventana, título, icono, icono pequeño, ancho y alto. Éstos últimos tienen valores por defecto. Hay un destructor pero por ahora no hace nada.

El puntero a si misma de la clase se puede obtener en el código del juego llamando a GetEngine(). El método Initialize() inicializa el programa del juego una vez que se crea el motor. El método HandleEvent() maneja los mensajes standard de Windows en el motor de juego y es un buen ejemplo de cómo el motor esconde detalles del código genérico de Windows y lo aparta del código del juego. El resto de métodos son de acceso a variables de miembro. SetFrameRate() pone los ciclos por segundo.

En GameEngine.cpp tenemos la implementación de los métodos descritos en la cabecera. También están aquí WinMain() y WndProc(). Y se inicializa el puntero al motor:

[code:1]GameEngine *GameEngine::m_pGameEngine = NULL;[/code]

WinMain() hace llamadas a las funciones y métodos del motor y nos da una buena forma de separar el código standard de Windows del código del juego.

[code:1]int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
MSG msg;
static int iTickTrigger = 0;
int iTickCount;

if (GameInitialize(hInstance))
{
// Inicializar el motor del juego.
if (!GameEngine::GetEngine()->Initialize(iCmdShow))
return FALSE;

// Entrar en el bucle de mensajes principal.
while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Procesar el mensaje.
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Asegurarse de que el motor no está durmiendo.
if (!GameEngine::GetEngine()->GetSleep())
{
// Comprobar el contador para ver si ha pasado un ciclo del juego
iTickCount = GetTickCount();
if (iTickCount > iTickTrigger)
{
iTickTrigger = iTickCount + GameEngine::GetEngine()->GetFrameDelay();
GameCycle();
}
}
}
}
return (int)msg.wParam;
}

// Salir del juego.
GameEnd();

return TRUE;
}[/code]

Esta función WinMain() establece un bucle de juego que se encarga de generar eventos de ciclo de juego con el intervalo especificado. En un programa Windows la medida de tiempo más pequeña se llama Tick, que es un milisegundo. En éste caso WinMain() cuenta ticks para determinar cuándo debe avisar al juego que debe empezar un nuevo ciclo. Las variables iTickTrigger y iTickCount se usan para establecer el ritmo de ciclos del juego.

También tenemos WndProc() que se vuelve muy sencilla teniendo en cuenta que ahora el método HandleEvent() de la clase GameEngine se encarga de los mensajes de Windows.

[code:1]LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Enrutar todos los mensajes de Windows al motor del juego.
return GameEngine::GetEngine()->HandleEvent(hWindow, msg, wParam, lParam);
}[/code]

- Constructor y destructor de GameEngine(), el segundo está vacío y el primero inicializa las variables miembro del motor.

[code:1]GameEngine::GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass,
LPTSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth, int iHeight)
{
// poner las variables miembro del motor del juego.
m_pGameEngine = this;
m_hInstance = hInstance;
m_hindow = NULL;
if (lstrlen(szWindowClass) > 0) lstrcpy(m_szWindowClass, szWindowClass);
if (lstrlen(szTitle) > 0) lstrcpy(m_szTitle, szTitle);
m_wIcon = wIcon;
m_wSmallIcon = wSmallIcon;
m_iWidth = iWidth;
m_iHeight = iHeight;
m_iFrameDelay = 50; // 20 FPS por defecto.
m_bSleep = TRUE;
}

GameEngine::~GameEngine()
{
}[/code]

En el constructor m_iFrameDelay se pone a 50 milisegundos. Para determinar el número de ciclos por segundo se divide 1000 entre el retraso de ciclos que en éste caso nos da 20 ciclos por segundo; suficiente para muchos juegos.

El método Initialize() de la clase GameEngine se utiliza para inicializar el motor. Crea la clase ventana que vamos a utilizar y posteriormente crea la ventana.

[code:1]BOOL GameEngine::Initialize(int iCmdShow)
{
WNDCLASSEX wndclass;

// Crear la clase de ventana para la ventana principal.
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = m_hInstance;
wndclass.hIcon = LoadIcon(m_hInstance,MAKEINTRESOURCE(GetIcon()));
wndclass.hIconSm = LoadIcon(m_hInstance, MAKEINTRESOURCE(GetSmallIcon()));
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = m_szWindowClass;

// Registrar la clase de ventana.
if (!RegisterClassEx(&wndclass))
return FALSE;

// Calcular el tamaño y posición de la ventana basándose en el tamaño indicado por el juego.
int iWindowWidth = m_iWidth + GetSystemMetrics(SM_CXFIXEDFRAME) * 2,
iWindowHeight = m_iHeight + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 +
GetSystemMetrics(SM_CYCAPTION);
if (wndclass.lpszMenuName != NULL) iWindowHeight += GetSystemMetrics(SM_CYMENU);
int iXWindowPos = (GetSystemMetrics(SM_CXSCREEN) - iWindowWidth) / 2,
iYWindowPos = (GetSystemMetrics(SM_CYSCREEN) - iWindowHeight) / 2;

// Crear la ventana.
m_hWindow = CreateWindow(m_szWindowClass, m_szTitle, WS_POPUPWINDOW |
WS_CAPTION | WS_MINIMIZEBOX, iXWindowPos, iYWindowPos, iWindowWidth,
iWindowHeight, NULL, NULL, m_hInstance, NULL);
if (!m_hWindow)
return FALSE;

// Mostrar y actualizar la ventana.
ShowWindow(m_hWindow, iCmdShow);
UpdateWindow(m_hWindow);

return TRUE;
}[/code]

Todo ésto normalmente se hace en WinMain(). El tamaño se calcula según las dimensiones de la pantalla obteniendo los datos de GetSystemMetrics(). Se coloca la ventana en el centro de la pantalla. Ahora el estilo de la ventana es WS_POPUPWINDOW, WS_CAPTION y WS_MINIMIZEBOX. La ventana no se puede redimensionar ni maximizar, pero tiene un menú y puede ser minimizada. Es un buen ejemplo de como código genérico de Windows puede ser metido en un motor de juego. El siguiente método es el HandleEvent():

[code:1]LRESULT GameEngine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam,
LPARAM lParam)
{
// enrutar los mensajes de Windows a las funciones de miembro del motor del juego.
switch (msg)
{
case WM_CREATE:
// Poner la ventana del juego y iniciar el motor.
SetWindow(hWindow);
GameStart(hWindow);
return 0;

case WM_ACTIVATE:
// Activar/desactivar el juego y actualizar el estado de "dormido".
if (wParam != WA_INACTIVE)
{
GameActivate(hWindow);
SetSleep(FALSE);
}
else
{
GameDeactivate(hWindow);
SetSleep(TRUE);
}
return 0;

case WM_PAINT:
HDC hDC;
PAINTSTRUCT ps;
hDC = BeginPaint(hWindow, &ps);

// Dibujar lo que corresponda.
GamePaint(hDC);

EndPaint(hWindow, &ps);
return 0;

case WM_DESTROY:
// Terminar el juego y salir del programa.
GameEnd();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWindow, msg, wParam, lParam);
}[/code]

Este método se parece mucho a WndProc(). Contiene la sentencia switch que va a tratar con todos los mensajes. También maneja un par de mensajes más aparte de los de Windows y llama a funciones de juego que serán específicas de cada juego. Se llama a WM_CREATE cuando se crea la ventana principal del juego. Luego se llama a GameStart() para inicializar el juego. El mensaje WM_ACTIVATE informa al juego si la ventana está activada o desactivada. wParam se usa para determinar cuál de las dos opciones es. Se llama a GameActivate() o a GameDeactivate() según corresponda. El mensaje WM_PAINT llama a BeginPaint() (del API) y luego a GamePaint(). A la función EndPaint() la llamamos cuando terminamos el proceso de redibujado. WM_DESTROY llama a GameEnd() y termina el programa.

- Nuevo programa de ejemplo:

Resource.h y Skeleton.rc son iguales que en el anterior ejemplo.

Skeleton.h
[code:1]//-----------------------------------------------------------------
// Game Skeleton Application
// C++ Header - Skeleton.h
//-----------------------------------------------------------------

#pragma once

//-----------------------------------------------------------------
// Include Files
//-----------------------------------------------------------------
#include
#include "Resource.h"
#include "GameEngine.h"

//-----------------------------------------------------------------
// Global Variables
//-----------------------------------------------------------------
GameEngine* _pGame; //aquí almacenaremos el puntero al motor del juego.[/code]

Skeleton.cpp
[code:1]//-----------------------------------------------------------------
// Game Skeleton Application
// C++ Source - Skeleton.cpp
//-----------------------------------------------------------------

//-----------------------------------------------------------------
// Include Files
//-----------------------------------------------------------------
#include "Skeleton.h"

//-----------------------------------------------------------------
// Funciones del motor del juego.
//-----------------------------------------------------------------
BOOL GameInitialize(HINSTANCE hInstance)
{
// Crear el motor del juego.
_pGame = new GameEngine(hInstance, TEXT("Game Skeleton"),
TEXT("Game Skeleton"), IDI_SKELETON, IDI_SKELETON_SM);
if (_pGame == NULL)
return FALSE;

// poner los fotogramas por segundo.
_pGame->SetFrameRate(15);

return TRUE;
}

void GameStart(HWND hWindow)
{
// Poner la semilla al generador de número aleatorios.
srand(GetTickCount());
}

void GameEnd()
{
// Hacer limpieza.
delete _pGame;
}

void GameActivate(HWND hWindow)
{
HDC hDC;
RECT rect;

// Dibujar el texto de activación en la pantalla.
GetClientRect(hWindow, &rect);
hDC = GetDC(hWindow);
DrawText(hDC, TEXT("Activated!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
ReleaseDC(hWindow, hDC);
}

void GameDeactivate(HWND hWindow)
{
HDC hDC;
RECT rect;

// Dibujar el texto de desactivación en la pantalla.
GetClientRect(hWindow, &rect);
hDC = GetDC(hWindow);
DrawText(hDC, TEXT("Deactivated!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
ReleaseDC(hWindow, hDC);
}

void GamePaint(HDC hDC)
{
//en éste ejemplo no hacemos nada.
}

void GameCycle()
{
HDC hDC;
HWND hWindow = _pGame->GetWindow();

// Dibujamos el icono del esqueleto en posiciones aleatorias de la pantalla.
hDC = GetDC(hWindow);
DrawIcon(hDC, rand() % _pGame->GetWidth(), rand() % _pGame->GetHeight(),
(HICON)(WORD)GetClassLong(hWindow, GCL_HICON));
ReleaseDC(hWindow, hDC);
}[/code]

Las únicas funciones que tenemos ahora son las que actúan ante los eventos del juego. La función GameInitialize() crea un objeto GameEngine y lo asigna a la variable global _pGame. Pone el juego a 15 fps que es suficiente para éste. GameStart() inicializa los datos y empieza el juego. Las funciones de activar y desactivar ventana responden poniendo un mensaje en pantalla. En éste caso GamePaint() no hace nada, se dibuja en pantalla en GameCycle(). A ésta función la vamos a llamar 15 veces por segundo.
- Ahora ignoramos GameEngine.h y GameEngine.cpp porque nos centramos en el código del juego que está en Skeleton.cpp. De todas maneras pongo aquí los dos archivos también.

GameEngine.h
[code:1]//-----------------------------------------------------------------
// Game Engine Object
// C++ Header - GameEngine.h
//-----------------------------------------------------------------

#pragma once

//-----------------------------------------------------------------
// archivos incluidos.
//-----------------------------------------------------------------
#include

//-----------------------------------------------------------------
// Declaraciones de las funciones de Windows.
//-----------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow);
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

//-----------------------------------------------------------------
// Declaraciones de las funciones del motor del juego.
//-----------------------------------------------------------------
BOOL GameInitialize(HINSTANCE hInstance);
void GameStart(HWND hWindow);
void GameEnd();
void GameActivate(HWND hWindow);
void GameDeactivate(HWND hWindow);
void GamePaint(HDC hDC);
void GameCycle();

//-----------------------------------------------------------------
// La clase GameEngine
//-----------------------------------------------------------------
class GameEngine
{
protected:
// Variables miembro.
static GameEngine* m_pGameEngine;
HINSTANCE m_hInstance;
HWND m_hWindow;
TCHAR m_szWindowClass[32];
TCHAR m_szTitle[32];
WORD m_wIcon, m_wSmallIcon;
int m_iWidth, m_iHeight;
int m_iFrameDelay;
BOOL m_bSleep;

public:
// Constructor(s)/Destructor
GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass, LPTSTR szTitle,
WORD wIcon, WORD wSmallIcon, int iWidth = 640, int iHeight = 480);
virtual ~GameEngine();

// General Methods
static GameEngine* GetEngine() { return m_pGameEngine; };
BOOL Initialize(int iCmdShow);
LRESULT HandleEvent(HWND hWindow, UINT msg, WPARAM wParam,
LPARAM lParam);

// Accessor Methods
HINSTANCE GetInstance() { return m_hInstance; };
HWND GetWindow() { return m_hWindow; };
void SetWindow(HWND hWindow) { m_hWindow = hWindow; };
LPTSTR GetTitle() { return m_szTitle; };
WORD GetIcon() { return m_wIcon; };
WORD GetSmallIcon() { return m_wSmallIcon; };
int GetWidth() { return m_iWidth; };
int GetHeight() { return m_iHeight; };
int GetFrameDelay() { return m_iFrameDelay; };
void SetFrameRate(int iFrameRate) { m_iFrameDelay = 1000 /
iFrameRate; };
BOOL GetSleep() { return m_bSleep; };
void SetSleep(BOOL bSleep) { m_bSleep = bSleep; };
};[/code]

GameEngine.cpp
[code:1]//-----------------------------------------------------------------
// Game Engine Object
// C++ Source - GameEngine.cpp
//-----------------------------------------------------------------

//-----------------------------------------------------------------
// Include Files
//-----------------------------------------------------------------
#include "GameEngine.h"

//-----------------------------------------------------------------
// Static Variable Initialization
//-----------------------------------------------------------------
GameEngine *GameEngine::m_pGameEngine = NULL;

//-----------------------------------------------------------------
// Windows Functions
//-----------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
MSG msg;
static int iTickTrigger = 0;
int iTickCount;

if (GameInitialize(hInstance))
{
// Initialize the game engine
if (!GameEngine::GetEngine()->Initialize(iCmdShow))
return FALSE;

// Enter the main message loop
while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Process the message
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Make sure the game engine isn't sleeping
if (!GameEngine::GetEngine()->GetSleep())
{
// Check the tick count to see if a game cycle has elapsed
iTickCount = GetTickCount();
if (iTickCount > iTickTrigger)
{
iTickTrigger = iTickCount +
GameEngine::GetEngine()->GetFrameDelay();
GameCycle();
}
}
}
}
return (int)msg.wParam;
}

// End the game
GameEnd();

return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Route all Windows messages to the game engine
return GameEngine::GetEngine()->HandleEvent(hWindow, msg, wParam, lParam);
}

//-----------------------------------------------------------------
// GameEngine Constructor(s)/Destructor
//-----------------------------------------------------------------
GameEngine::GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass,
LPTSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth, int iHeight)
{
// Set the member variables for the game engine
m_pGameEngine = this;
m_hInstance = hInstance;
m_hWindow = NULL;
if (lstrlen(szWindowClass) > 0)
lstrcpy(m_szWindowClass, szWindowClass);
if (lstrlen(szTitle) > 0)
lstrcpy(m_szTitle, szTitle);
m_wIcon = wIcon;
m_wSmallIcon = wSmallIcon;
m_iWidth = iWidth;
m_iHeight = iHeight;
m_iFrameDelay = 50; // 20 FPS default
m_bSleep = TRUE;
}

GameEngine::~GameEngine()
{
}

//-----------------------------------------------------------------
// Game Engine General Methods
//-----------------------------------------------------------------
BOOL GameEngine::Initialize(int iCmdShow)
{
WNDCLASSEX wndclass;

// Create the window class for the main window
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = m_hInstance;
wndclass.hIcon = LoadIcon(m_hInstance,
MAKEINTRESOURCE(GetIcon()));
wndclass.hIconSm = LoadIcon(m_hInstance,
MAKEINTRESOURCE(GetSmallIcon()));
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = m_szWindowClass;

// Register the window class
if (!RegisterClassEx(&wndclass))
return FALSE;

// Calculate the window size and position based upon the game size
int iWindowWidth = m_iWidth + GetSystemMetrics(SM_CXFIXEDFRAME) * 2,
iWindowHeight = m_iHeight + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 +
GetSystemMetrics(SM_CYCAPTION);
if (wndclass.lpszMenuName != NULL)
iWindowHeight += GetSystemMetrics(SM_CYMENU);
int iXWindowPos = (GetSystemMetrics(SM_CXSCREEN) - iWindowWidth) / 2,
iYWindowPos = (GetSystemMetrics(SM_CYSCREEN) - iWindowHeight) / 2;

// Create the window
m_hWindow = CreateWindow(m_szWindowClass, m_szTitle, WS_POPUPWINDOW |
WS_CAPTION | WS_MINIMIZEBOX, iXWindowPos, iYWindowPos, iWindowWidth,
iWindowHeight, NULL, NULL, m_hInstance, NULL);
if (!m_hWindow)
return FALSE;

// Show and update the window
ShowWindow(m_hWindow, iCmdShow);
UpdateWindow(m_hWindow);

return TRUE;
}

LRESULT GameEngine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Route Windows messages to game engine member functions
switch (msg)
{
case WM_CREATE:
// Set the game window and start the game
SetWindow(hWindow);
GameStart(hWindow);
return 0;

case WM_ACTIVATE:
// Activate/deactivate the game and update the Sleep status
if (wParam != WA_INACTIVE)
{
GameActivate(hWindow);
SetSleep(FALSE);
}
else
{
GameDeactivate(hWindow);
SetSleep(TRUE);
}
return 0;

case WM_PAINT:
HDC hDC;
PAINTSTRUCT ps;
hDC = BeginPaint(hWindow, &ps);

// Paint the game
GamePaint(hDC);

EndPaint(hWindow, &ps);
return 0;

case WM_DESTROY:
// End the game and exit the application
GameEnd();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWindow, msg, wParam, lParam);
}[/code]


Código fuente

  • IsGreen

  • Neonate

  • vida restante: 100%
  • Registrado: 12 ene 2009
  • Mensajes: 73
#2

Escrito 02 agosto 2009 - 17:02

Aunque crear un motor de juego no sea una de mis prioridades, felicitaciones por el tremendo trabajo que estás realizando.

Sin duda servirá de ayuda a la hora de decidir, a quienes hayan pensado dedicarse profesionalmente a la programación.

Hasta ahora no había tratado nada sobre la programación en C++ con la API de windows, y la mitad de tipos de datos no los conozco. Tampoco he necesitado programar nada con esta biblioteca desde otros motores de juego como Esenthel o DarkGDK.

Pregunto, esta biblioteca nos permitiría configurar el aspecto de la ventana (transparencia, asignarle una imagen de fondo,... ), cosa que con otras bibliotecas de C++ más estándar no podríamos. Y si nuestro objetivo es simplemente la programación 3D, física o sonido, no sería necesariamente imprescindible saber utilizar la API de windows, pudiendo utilizar otras bibliotecas más comunes y con sintaxis más sencilla.

Otra pregunta, con la API de Windows es posible realizar programación gráfica, pero no tendría nada que ver con directX ni OpenGL.

Saludos.

#3

Escrito 02 agosto 2009 - 17:12

Éste motor está pensado para crear juegos simples en 2D. Después, si conoces la programación para Windows, se puede ampliar el motor para que haga cualquier cosa.

Para usar 3D es mejor usar DirectX o OpenGL y aprender a programar con esos sistemas. O usar otras librerías que facilitan la programación. O usar motores ya hechos que añaden de entrada muchas funciones útiles.

Tu eliges cómo programas con más comodidad y si te satisfacen los resultados siempre será mejor programar algo que entiendes bien para no comerte el coco con lo básico y poder aprender más cosas.

  • jonaSoft

  • Fledgling

  • vida restante: 100%
  • Registrado: 09 oct 2009
  • Mensajes: 55
#4

Escrito 10 diciembre 2009 - 02:28

Sin duda se felicita al personaje de esta serie de tutoriales , pero lo malo es que no es muy bien visto ya que los principiantes prefieren programar con un motor , en ves de programar tu propio motor ( aunque eso ultimo si o si despues de un tiempo lo terminas haciendo igual).


Este tema ha sido archivado. Esto significa que no puedes responder en este tema.
publicidad
publicidad