Texel
DirectX | Index | Contact

Le joystick



Pour comprendre ce tutorial, il faut avoir lu auparavant ceux sur le clavier et la souris. Ici, le terme joystick est général. Il désigne aussi bien un manche à balai qu'un joypad ou un volant.

Téléchargez un code source pour ce tutorial ICI.

Pour utiliser le joystick avec Direct Input, il faut passer par ces étapes:

1.Créer un objet DirectInput : Cet objet est l'interface principale qui permet d'utiliser une ou plusieurs unités d'entrées.
2.Enumérer les joysticks et créer un objet DirectInputDevice: C'est à dire rechercher tout les joysticks installés et branchés sur la machine de l'utilisateur. Lorsqu'un joystick est détecté, on lui crée une interface.
3.Définir un format de donné pour l'objet DirectInputDevice.
4.Définir le comportement de l'unité d'entée.
5.Définir les propriétés du joystick (Sa sensibilité …).
6.Obtenir un accès au joystick.
7.Récupérer les informations du joystick (position…).
8.Libérer toutes les interfaces DirectInput

Important: N'oubliez pas dans vos programmes d'inclure "dinput.h", de rajouter la ligne #define INITGUID devant l'inclusion des fichiers ( pour les identificateurs GUID), et de linker la librairie dinput8.lib.



Etape 1: Création d'un objet DirectInput

Exactement pareil que pour le clavier et la souris.



Etape 2: Enumérer les joysticks et créer un objet DirectInputDevice

Avant d'essayer de lire les données provenant d'un joystick, il faut être sûr que l'utilisateur en a installé et branché un. Il faut donc énumérer les joysticks installés et branchés par la méthode IDirectInput8::EnumDevices qui appel une fonction callback. Cette méthode peut très bien être utilisé pour détecter la présence d'un clavier ou d'une souris (mais qui n'en a pas?).

HRESULT EnumDevices(
  		DWORD dwDevType,      		// unité à énumérer
  		LPDIENUMCALLBACK lpCallback,  	// nom de la fonction callback
  		LPVOID pvRef,			// informations à passés à la fonction callback
  		DWORD dwFlags			// drapeau pour la portée de l'énumération
		);









Le premier argument est le type de matériel à énumérer: DI8DEVCLASS_GAMECTRL pour les joysticks.

Le deuxième argument est le nom de la fonction callback appelé par la méthode à chaque fois qu'elle énumère une entrée. Il vous faut créer cette fonction (comme nous le verrons plus tard).

Le troisième paramètre reçoit une éventuelle information que l'on voudrait passer à la fonction callback. Les programmes du SDK lui envoient l'adresse de l'objet DirectInput.

Le dernier paramètre reçoit des drapeaux qui définissent la portée de l'énumération:

DIEDFL_ALLDEVICES		Tous les périphériques installés
DIEDFL_ATTACHEDONLY 		Seuls les périphériques installés et branchés
DIEDFL_FORCEFEEDBACK 		Seuls les périphériques à retour de force
DIEDFL_INCLUDEHIDDEN 		périphériques cachés
DIEDFL_INCLUDEPHANTOMS 		périphériques fantômes








Voici le code d'une fonction callback d'énumération:
BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext )
{
	if(lpDI->CreateDevice( pdidInstance->guidInstance, &pJoystick, NULL)!=DI_OK)
		return DIENUM_CONTINUE;
	return DIENUM_STOP;
}










Cette fonction tente de créer une interface DirectInputDevice jusqu'à ce qu'elle y arrive. DIENUM_CONTINUE est retourné lorsque ça ne marche pas. DIENUM_STOP stop l'énumération.

Le premier argument de la fonction est un pointeur vers une instance d'interface qui est à envoyé à la méthode CreateDevice.
Le deuxième argument est l'avant dernier argument de la fonction EnumDevices.



Etape 3: Définir un format de donné pour l'objet DirectInputDevice

Comme pour le clavier et la souris : SetDataFormat + argument c_dfDIJoystick2



Etape 4 : Définir le comportement de l'unité d'entée

Comme pour le clavier et la souris : SetCooperativeLevel + les drapeaux DISCL_EXCLUSIVE | DISCL_FOREGROUND



Etape 5 : Définir les propriétés du joystick

Les joysticks (comme les volants) sont analogiques. Les mouvements de ce que vous dirigez à l'écran sont proportionnels à l'inclinaison du manche (ou du volant).

Pour définir la plage de mouvement joystick qui correspond à la valeur maximale à enregistrer lorsque le manche est à l'extrémité d'un axe, il faut remplir une structure de type DIPROPRANGE :
DIPROPRANGE diprg;
	diprg.diph.dwSize       = sizeof(DIPROPRANGE);
    	diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
    	diprg.diph.dwHow	= DIPH_BYOFFSET;
	diprg.diph.dwObj       	= DIJOFS_X;
	diprg.lMin             	= -1000;	// valeur minimum pour l'axe x
   	diprg.lMax		= +1000; 	// valeur maximum pour l'axe x











DIPH_BYOFFSET envoyé au champ dwHow nous permet d'utiliser la constante DIJOFS_X avec le champ dwObj pour indiquer que l'on veut définir les propriétés de l'axe X.
Vous pouvez mettre les valeurs que vous voulez pour les champs lMax et lMin.

Ensuite, on passe cette propriété à l'interface du joystick avec la constante DIPROP_RANGE pour indiquer que l'on s'intéresse à la plage :
pJoystick->SetProperty( DIPROP_RANGE, &diprg.diph );






Pour l'axe Y, on ne change que la valeur du champ dwObj (si la sensibilité est la même que pour l'axe des X) et l'on rappel la méthode SetProperty.

Maintenant il faut définir une zone neutre dans laquelle les mouvements du joystick ne sont pas pris en compte. Elle permet d'éviter une sensibilité trop grande et résous le problème des trimes mal réglés.

Ca ressemble beaucoup à ce qui a été fait au-dessus. On utilise une structure très similaire de type DIPROPDWORD et on envoi la constante DIPROP_DEADZONE à la méthode SetProperty :
DIPROPDWORD dipdw;
	dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
   	dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
    	dipdw.diph.dwHow        = DIPH_BYOFFSET;
	dipdw.diph.dwObj       	= DIJOFS_X;	// pour l'axe des X
	dipdw.dwData		=100;	// largeur de la zone neutre

	
pJoystick->SetProperty( DIPROP_DEADZONE, &dipdw.diph );













La valeur de la zone neutre est une valeur absolue. La zone est donc symétrique.



Etape 6 : Obtenir un accès à l'unité d'entrées.

La méthode IDirectInputDevice8::Acquire suffit. Elle n’a pas d’arguments.
Cette acquisition sera perdu si votre fenêtre n'est plus active. Il vous faut donc gérer ce problème avec le message WM_ACTIVATE de votre procédure de fenêtre (cf. code source).



Etape 7 : Récupérer les informations du joystick

Les étapes précédentes se faisaient à l’initialisation. La suite se fait dans la boucle de jeu.

Les informations relatives au joystick sont à stocker dans une structure de type DIJOYSTATE2 toujours grâce à la méthode GetDeviceState. Mais avant d'appeler cette méthode, il faut en appeler une autre: IDirectInputDevice8::Poll (sans argument). Celle-ci permet à GetDeviceState d'obtenir les données les plus récentes. Entre ces deux méthodes il est conseillé de vérifier que l'unité est toujours acquise.

Voici les champs les plus utilisé de la structure:

lX et lY contiennent la valeur de la position du manche sur la plage que vous avez définit pour les axes X et Y.

rgbButtons[nbr_de_boutons] pour les boutons. Tout comme pour le clavier et la souris, on utilise l'opérateur AND et 0x80.

Pour le reste lisez la doc du SDK.

Attention: ne récupérez les infos de la souris que si vous y avez accès.



Etape 8 : Libérer toutes les interfaces DirectInput

Avant la fermeture de l’application il ne faut surtout pas oublier:
1.De « déacquérir » les interfaces DirectInputDevice par la méthode IDirectInputDevice8::Unacquire (sans argument).
2.De libérer les interfaces DirectInputDevice par la méthode IDirectInputDevice8::Release (sans argument).
3.De libérer l'objet DirectInput.



Version originale: Avril 2001
Dernière mise à jour: Juin 2002
Par Grégory Smialek
www.texel.fr.fm