Ir al contenido

publicidad

Foto

Curso MM: 6 Joysticks


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

#1

Escrito 08 agosto 2009 - 15:47

Mejorando la entrada de datos con joysticks

Un joystick es un dispositivo que permite el movimiento en varios ejes y además tiene botones. Las direcciones aquí no son sólo dos. Un joystick puede tener hasta 6 ejes (izq-der,adel-atrás, abajo-arriba,torsión der-torsión izq, y dos más para el movimiento en el espacio del joystick entero como un guante de realidad virtual). En Windows los joysticks pueden tener hasta 32 botones. En éste motor de juego nos limitaremos a dos ejes y a 4 botones. Antes de trabajar con un joystick hay que ir a panel de control - controladores de juegos y calibrarlo.

Siguiendo los movimientos del joystick

Casi se puede decir que es más complicado determinar si hay un joystick conectado y disponible para usar que seguir sus movimientos. Hay que aprender el concepto de capturar un joystick, que da al programa el uso exclusivo del mismo.

Con la siguiente función podemos saber el número de joysticks disponibles para usar en el sistema:

[code:1]UINT uiNumJoysticks = joyGetNumDevs();[/code]

Pero ésta función no dice cuántos hay instalados. Con joyGetPos() pedimos información sobre uno determinado:

[code:1]JOYINFO jiInfo;
if (joyGetPos(JOYSTICKID1, &jiInfo) != JOYERR_UNPLUGGED)
// el joystick está conectado y preparado para funcionar[/code]

La estructura JOYINFO es la siguiente:

[code:1]typedef struct {
UINT wXpos;
UINT wYpos;
UINT wZpos;
UINT wButtons;
} JOYINFO;[/code]

La variable wButtons puede almacenar el estado de cuatro botones: JOY_BUTTON1, JOY_BUTTON2, JOY_BUTTON3 y JOY_BUTTON4. También se ve que sólo almacena la posición en 3 ejes. Para usar los demás ejes/botones hay que usar directx.

Para ver si el primer botón de un joystick está pulsado:

[code:1]JOYINFO jiInfo;
if (joyGetPos(JOYSTICKID1, &jiInfo) == JOYERR_NOERROR)
if (jiInfo.wButtons & JOY_BUTTON1)
// El botón 1 está pulsado.[/code]

Con joyGetDevCaps() podemos obtener los rangos en los que se va a mover el joystick, es decir, cuánto se puede mover a cada lado. Esta llamada llena una estructura JOYCAPS. Ejemplo:

[code:1]JOYCAPS jcCaps;
joyGetDevCaps(JOYSTICKID1, &jcCaps, sizeof(JOYCAPS));
DWORD dwXCenter = ((DWORD)jcCaps.wXmin + jcCaps.wXmax) / 2;
DWORD dwYCenter = ((DWORD)jcCaps.wYmin + jcCaps.wYmax) / 2;[/code]

Aquí vemos cómo obtener los rangos x e y desde el mínimo hasta el máximo. Obtenemos además el centro de cada eje, lo que nos da un punto de partida para determinar si el joystick se ha movido en alguno de esos ejes.

Introduciendo el manejo de joysticks en el motor de juego

Hay que poner éste include: #include

La función para tratar con el joystick a través del motor de juego será HandleJoystick(), y su definición:

[code:1]void HandleJoystick(JOYSTATE jsJoystickState);[/code]

Como argumento sólo acepta un tipo de datos : JOYSTATE que también hay que definir:

[code:1]typedef WORD JOYSTATE;
const JOYSTATE JOY_NONE = 0x0000L,
JOY_LEFT = 0x0001L,
JOY_RIGHT = 0x0002L,
JOY_UP = 0x0004L,
JOY_DOWN = 0x0008L,
JOY_FIRE1 = 0x0010L,
JOY_FIRE2 = 0x0020L;[/code]

JOYSTATE es un dato de tipo WORD que puede contener una o más banderas de estado que indican varios aspectos del joystick.

Ahora viene otro concepto más. Los ejes del joystick tienen un rango, pueden moverse cierto espacio de izquierda a derecha y de adelante a atrás. Con esos valores podemos calcular el centro, punto en el que el joystick estaría en reposo. Ahora bien, si con los rangos de las dos dimensiones construimos un rectángulo alrededor del centro, podemos crear otro rectángulo más pequeño dentro y sólo generar eventos si el joystick se mueve a un lugar entre el rectángulo pequeño y el grande. Si el joystick se mueve sólo dentro del pequeño no generamos eventos. Necesitamos dos variables más:

[code:1]UINT m_uiJoystickID; //identificación del joystick.
RECT m_rcJoystickTrip; //rectángulo interior.[/code]

El motor de juego también necesita algunos métodos para dar soporte al joystick.

[code:1]BOOL InitJoystick(); //inicializar el joystick.
void CaptureJoystick(); //el joystick afecta sólo a la ventana que lo captura.
void ReleaseJoystick(); //se libera para que pueda ser usado por otro programa.
void CheckJoystick(); //se comprueba y se convierten los datos en movimientos.[/code]

[code:1]BOOL GameEngine::InitJoystick()
{
// Asegurarse de que el controlador del joystick está presente.
UINT uiNumJoysticks;
if ((uiNumJoysticks = joyGetNumDevs()) == 0)
return FALSE;

// Asegurarse de que el joystick está conectado.
JOYINFO jiInfo;
if (joyGetPos(JOYSTICKID1, &jiInfo) != JOYERR_UNPLUGGED)
m_uiJoystickID = JOYSTICKID1;
else
return FALSE;

// Calcular los valores del movimiento.
JOYCAPS jcCaps;
joyGetDevCaps(m_uiJoystickID, &jcCaps, sizeof(JOYCAPS));
DWORD dwXCenter = ((DWORD)jcCaps.wXmin + jcCaps.wXmax) / 2;
DWORD dwYCenter = ((DWORD)jcCaps.wYmin + jcCaps.wYmax) / 2;
m_rcJoystickTrip.left = (jcCaps.wXmin + (WORD)dwXCenter) / 2;
m_rcJoystickTrip.right = (jcCaps.wXmax + (WORD)dwXCenter) / 2;
m_rcJoystickTrip.top = (jcCaps.wYmin + (WORD)dwYCenter) / 2;
m_rcJoystickTrip.bottom = (jcCaps.wYmax + (WORD)dwYCenter) / 2;

return TRUE;
}

void GameEngine::CaptureJoystick()
{
// Capture the joystick
if (m_uiJoystickID == JOYSTICKID1)
joySetCapture(m_hWindow, m_uiJoystickID, NULL, TRUE);
}

void GameEngine::ReleaseJoystick()
{
// Release the joystick
if (m_uiJoystickID == JOYSTICKID1)
joyReleaseCapture(m_uiJoystickID);
}

void GameEngine::CheckJoystick()
{
if (m_uiJoystickID == JOYSTICKID1)
{
JOYINFO jiInfo;
JOYSTATE jsJoystickState = 0;
if (joyGetPos(m_uiJoystickID, &jiInfo) == JOYERR_NOERROR)
{
// Check horizontal movement
if (jiInfo.wXpos < (WORD)m_rcJoystickTrip.left)
jsJoystickState |= JOY_LEFT;
else if (jiInfo.wXpos > (WORD)m_rcJoystickTrip.right)
jsJoystickState |= JOY_RIGHT;

// Check vertical movement
if (jiInfo.wYpos < (WORD)m_rcJoystickTrip.top)
jsJoystickState |= JOY_UP;
else if (jiInfo.wYpos > (WORD)m_rcJoystickTrip.bottom)
jsJoystickState |= JOY_DOWN;

// Check buttons
if(jiInfo.wButtons & JOY_BUTTON1)
jsJoystickState |= JOY_FIRE1;
if(jiInfo.wButtons & JOY_BUTTON2)
jsJoystickState |= JOY_FIRE2;
}

// Pasar los datos del joystick al juego para que haga algo.
HandleJoystick(jsJoystickState);
}
}[/code]

La función CheckJoystick() al final llama a HandleJoystick() pasándole como parámetro el estado para que en el código específico de cada juego hagamos lo que queramos.

Programa de ejemplo: (ya no incluyo el código aquí, sólo las cosas que cambian. El código completo está en el zip).
Ufo2.h , Ufo2.cpp

En Ufo2.h vemos las siguientes variables:

[code:1]Bitmap* _pSaucer[2]; //en vez de un sprite ahora tenemos dos, el segundo es para el fuego de los motores.
BOOL _bSaucerFlame; //indica si debemos dibujar con llama el platillo o sin ella.[/code]

En GameInitialize() añadimos la siguiente línea: pGame->InitJoystick();

[code:1]void GameStart(HWND hWindow)
{
// Seed the random number generator
srand(GetTickCount());

// Create and load the background and saucer bitmaps
HDC hDC = GetDC(hWindow);
_pBackground = new Bitmap(hDC, IDB_BACKGROUND, _hInstance);
_pSaucer[0] = new Bitmap(hDC, IDB_SAUCER, _hInstance);
_pSaucer[1] = new Bitmap(hDC, IDB_SAUCERFLAME, _hInstance);

// Set the initial saucer position and speed
_iSaucerX = 250 - (_pSaucer[0]->GetWidth() / 2);
_iSaucerY = 200 - (_pSaucer[0]->GetHeight() / 2);
_iSpeedX = 0;
_iSpeedY = 0;
}[/code]

Capturar y liberar el joystick tiene lugar en las siguientes funciones:

[code:1]void GameActivate(HWND hWindow)
{
// Capture the joystick
_pGame->CaptureJoystick();
}

void GameDeactivate(HWND hWindow)
{
// Release the joystick
_pGame->ReleaseJoystick();
}[/code]

Y aquí es donde dibujamos el platillo que nos interesa: con/sin llama.

[code:1]void GamePaint(HWND hWindow)
{
// Draw the background and saucer bitmaps
_pBackground->Draw(hDC, 0, 0);
_pSaucer[_bSaucerFlame ? 1:0]->Draw(hDC, _iSaucerX, _iSaucerY, TRUE);
}[/code]

En la siguiente controlamos lo que se hace con el platillo y cambiamos la variable que indica si se dibuja o no con llama:

[code:1]void HandleJoystick(JOYSTATE jsJoystickState)
{
// Comprobar el movimiento horizontal.
if (jsJoystickState & JOY_LEFT)
_iSpeedX = max(-_iMAXSPEED, _iSpeedX - 2);
else if (jsJoystickState & JOY_RIGHT)
_iSpeedX = min(_iMAXSPEED, _iSpeedX + 2);

// Comprobar el movimiento vertical.
if (jsJoystickState & JOY_UP)
_iSpeedY = max(-_iMAXSPEED, _iSpeedY - 2);
else if (jsJoystickState & JOY_DOWN)
_iSpeedY = min(_iMAXSPEED, _iSpeedY + 2);

// Comprobar el botón primario del joystick.
// aquí se asigna 0 o 1 a la variable para dibujar con o sin llama.
_bSaucerFlame = (jsJoystickState & JOY_FIRE1);

// Comprobar el botón secundario del joystick.
if (jsJoystickState & JOY_FIRE2)
{
// llevar al platillo volante hacia el hiperespacio.
_iSaucerX = rand() % (500 - _pSaucer[0]->GetWidth());
_iSaucerY = rand() % 320;
}
}[/code]

Antes de compilar hay que añadir una librería para el joystick. A la izquierda pinchamos en el nombre del proyecto con el botón derecho y en menú contextual pinchamos en opciones del proyecto. Seleccionamos la pestaña "parámetros" y le damos al botón de añadir biblioteca u objeto. Vamos a la carpeta Dev-cpp/lib y añadimos el archivo libwinmm.a . Aceptamos todo y ya podemos compilar.

Código fuente


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