Partie 3 : Les touches

Développement amateur sur Nintendo DS Partie 3

La partie 2 de cette série de tutoriaux expliquait comment utiliser les modes graphiques de type framebuffer et l\'interruption VBL (Vertical BLank Interrupt).

Cette partie, la partie 3, va vous guider dans la façon de gérer les touches de la Nintendo DS.

Le registre KEYS

La Nintendo DS possède un registre contenant les bits modifiés lorsque les touches sont pressées sur la console. Ce registre, appelé KEYS dans \'libndsb\' et correspondant à l\'adresse mémoire 0x4000130, est en lecture seule et les bits suivant seront effacés (mis à \'0\') lorsque la touche correspondante est actionnée :

Bit KEYSTouchesdéfinition \'libnds\'Pressée si...Relâchée si ...
0AKEY_AEffacéPositionné
1BKEY_BEffacéPositionné
2SelectKEY_SELECTEffacéPositionné
3StartKEY_STARTEffacéPositionné
4PAD Flèche DroiteKEY_RIGHTEffacéPositionné
5PAD Flèche GaucheKEY_LEFTEffacéPositionné
6PAD Flèche HautKEY_UPEffacéPositionné
7PAD Fleche BasKEY_DOWNEffacéPositionné
8Bouton RKEY_REffacéPositionné
9BOuton LKEY_LEffacéPositionné

Le registre XKEYS

Vous avez du remarquer qu\'il manque 2 touches. Ce sont les touches \'X\' et \'Y\', qui sont nouvelles sur la Nintendo DS (elles n\'existaient pas sur Gameboy Advance).

Ces 2 touches sont lues avec un autre registre, XKEYS, se trouant à l\'adresse mémoire 0x04000136. Malheureusement, ce registre ne peut être lu que par l\'ARM7, alors que le registre KEYS peut être lu aussi bien par l\'ARM7 et l\'ARM9.

Pour permettre sa lecture par l\'ARM9, le template par défaut de l\'ARM7 livré avec \'ndslib\' le lit durant l\'interruption vertical blank et le stocke dans IPC->buttons. IPC est une structure contenant beaucoup de données utilisées, lues par l\'ARM7 et accessibles par l\'ARM9. Voici la section concernée tirée du code du template ARM7 :

void InterruptHandler(void)
{
    [...]

    but = XKEYS;

    [...]

    IPC->heartbeat = heartbeat;
    IPC->buttons   = but;
    IPC->touchX    = x;

    [...]
}

Le registre XKEYS a les bits correspondants effacés quand les boutons X ou Y sont appuyés. Il contient aussi des bits indiquant lorsque le stylet est en contact avec l\'écran tactile et lorsque le \'capot\' de DS est refermé ou pas :

Bit XKEYSTouchesDéfinition \'libnds\'Pressée si...Relâchée si ...
0X(1 << 0)EffacéMis
1Y(1 << 1)EffacéMis
2Stylet en contact(1 << 6)EffacéMis
3Capot(1 << 7)MisEffacé

Il faut noter que le \'capot\' est différent par le fait que le bit le concernant est positionné lorsqu\'il est fermé et effacé dans le cas contraire.

Lire les touches

La librairie \'libnds\' possède une macro bien utile, READ_KEYS, qui réalise un masque sur les bits non utilises de KEYS et renvoi son complément. Ceci permet d\'utiliser l\'opérateur \'&\' pour savoir si la touche est pressée. Donc, avec READ_KEYS, vous pouvez écrire du code comme :

if(READ_KEYS & KEY_UP)
    --shape_y;

Plutôt qque cette version moins intuitive :

if(!(KEYS & KEY_UP))
    --shape_y;

Je n\'ai pas trouvé d\'équivalent de READ_KEYS dans \'libnds\' pour le registre XKEYS. Donc, vous devez coder vous même la récupération des valeurs via IPC->buttons :

uint16 specialKeysPressed = ~IPC->buttons;

// Y Key
if(specialKeysPressed & (1 << 1))
    shape_color = RGB15(7, 7, 7);

// X Key
if(specialKeysPressed & (1 << 0))
    shape_color = RGB15(0, 15, 15);

// Pen Down
if(specialKeysPressed & (1 << 6))
    shape_color = RGB15(0, 31, 31);

// Hinge closed
if(!(specialKeysPressed & (1 << 7)))
    shape_color = RGB15(0, 0, 0);

Notez que le \'!\' est obligatoire pour le test du capot car c\'est l\'inverse des bits présents.

Déplacement d\'une forme avec les touches

Pour mettre en pratique l\'utilisation des touches, nous allons réaliser une petite modification de l\'exemple sur les formes dans le tutorial deux.

Plutôt que de laisser la forme se déplacer automatiquement sur l\'écran, nous allons la contrôler avec le PAD directionnel. Les autres boutons changerons la couleur de la forme. La fermeture du capot fera disparaître la forme (en changeant sa couleur en noir). Sa réouverture et l\'appuie sur une autre touche la fera réapparaître.

Pour modifier la couleur, nous allons utiliser une variable globale qui contient la couleur actuelle :

static uint16 shape_color = RGB15(31, 0, 0);

Et le code gérant l\'affichage va être modifier pour utiliser cette variable contenant la couleur :

void on_irq()
{
    if(IF & IRQ_VBLANK)
    {
        draw_shape(old_x, old_y, VRAM_A, RGB15(0, 0, 0));
        draw_shape(shape_x, shape_y, VRAM_A, shape_color);

        // Tell the DS we handled the VBLANK interrupt
        VBLANK_INTR_WAIT_FLAGS |= IRQ_VBLANK;
        IF |= IRQ_VBLANK;
    }
    else
    {
        // Ignore all other interrupts
        IF = IF;
    }
}

La forme est déplacée en utilisant le PAD. Nous allons utiliser pour cela la macro READ_KEYS et le test des touches voulues. Les tests sont réalisés par des traitements \'if\' séparés pour permettre la combinaison d\'appuis sur plusieurs touches en même temps, permettant ainsi des déplacements en diagonal :

uint16 keysPressed = READ_KEYS;

// Based on the key pressed, move the shape.
if(keysPressed & KEY_UP)
    --shape_y;
if(keysPressed & KEY_DOWN)
    ++shape_y;
if(keysPressed & KEY_LEFT)
    --shape_x;
if(keysPressed & KEY_RIGHT)
    ++shape_x;

Les touches utilisées pour changer la couleur sont testées via la valeur de IPC->buttons (qui contient aussi XKEYS), la première chose à faire étant de prendre le complément de ce dernier pour réaliser des tests de façon plus intuitive :

uint16 specialKeysPressed = ~IPC->buttons;

// Change the color of the shape if the relevant key was pressed.
if(keysPressed & KEY_A)
    shape_color = RGB15(31, 0, 0);

if(keysPressed & KEY_B)
    shape_color = RGB15(0, 31, 0);

if(keysPressed & KEY_SELECT)
    shape_color = RGB15(0, 0, 31);

if(keysPressed & KEY_START)
    shape_color = RGB15(31, 31, 31);

if(keysPressed & KEY_R)
    shape_color = RGB15(15, 0, 15);

if(keysPressed & KEY_L)
    shape_color = RGB15(7, 15, 7);

// Y Key
if(specialKeysPressed & (1 << 1))
    shape_color = RGB15(7, 7, 7);

// X Key
if(specialKeysPressed & (1 << 0))
    shape_color = RGB15(0, 15, 15);

// Pen Down
if(specialKeysPressed & (1 << 6))
    shape_color = RGB15(0, 31, 31);

// Hinge closed
if(!(specialKeysPressed & (1 << 7)))
    shape_color = RGB15(0, 0, 0);

Création de l\'exécutable pour la DS

Les étapes pour construire l\'application sont exactement les mêmes que celle décrites dans le second tutorial. J\'utilise par défaut le code pour l\'ARM7 dans un fichier arm7_main.cpp et celui de l\'ARM9 dans arm9_main.cpp. Un fichier Makefile est disponible pour exécuter les commandes du compilateur.

Le code source complet se trouve dans keys_demo1.zip et vous pouvez télécharger les fichiers keys_demo1.nds et keys_demo1.nds.gba pour l\'exécution sur émulateur et hardware.

Conclusion

Ce tutorial vous a montré comment détecter les différentes touches appuyés sur la DS. Il existe d\'autres moyens pour réaliser cette détection bien entendu.

L\'approche utilisée est de type \'pooling\', en scrutant la valeur du registre à un instant t. Nous réalisons cette action entre chaque attente d\'interruption VBL. Il est bien entendu possible que l\'utilisateur puisse appuyer sur une touche très rapidement avant que nous ayons le temps de tester le registre entre 2 attentes VBL. Notre programme n\'aura donc aucun moyen de savoir que la touche a été appuyée..

Bien qu\'il y ait peu de chance que cela se produise à 60 trames par seconde, cela peut être évité en utilisation les \'interruptions\'. Comme pour l\'interruption VBL, il existe une interruption pour la gestion des touches. Nous pouvons donc avoir une fonction d\'interruption appelée dés qu\'une touche est appuyée. Le tutorial sur les interruptions explique comme réaliser cela et les inconvénients de cette méthode. Le Tutorial six montre une autre méthode de gestion des appuis sur les touches qui est souhaitable dans les \'vrais applications\'.

Les mises à jour de ce tutorial peuvent être obtenues sur le weblog de l\'auteur (Chris Double) : http://radio.weblogs.com/0102385. Il peut être contacté à l\'adresse chris.double@double.co.nz

Copyright (c) 2005, Chris Double. Tous droits réservés. Traduction par AlekMaul (alekmaul@portabledev.com).