Parce qu'il faut bien commencer quelque part, cette partie vous expliquera les notions de base de PAlib. Il n'y aura pas que du hello world, sinon ça ne serait pas marrant, avouez :D .
Note : vous devez avoir lu et compris les deux premières parties du cours sur le C de M@teo21 ! La partie consacrée à la SDL est facultative mais ça peut toujours aider.
Avant propos
Vous vous doutiez sûrement que les outils nécessaires à la programmation sur DS ne se trouvaient pas déjà sur votre ordinateur et qu'il ne suffisait pas de dire abracadabra pour que vous puissiez utiliser vos programmes sur votre DS :p . C'est pourquoi dans ce chapitre nous allons vous expliquer en détail ce dont vous avez besoin.
Vous n'apprendrez donc "rien" dans ce chapitre mais ne vous inquiétez pas ça arrivera plus vite que vous ne le pensez :-° .
Parlons hardware
À ce stade-ci deux chemins qui s'offrent à vous :
vous programmez des applications et jeux pour Nintendo DS mais vous ne les utilisez que sur votre PC avec un émulateur ;
ou alors vous décidez de mettre vos programmes directement dans votre DS pour pouvoir les utiliser partout :) .
Le deuxième choix semble le plus intéressant, cela va de soi ! Toutefois, renseignez-vous sur son utilisation, car vous pouvez rapidement tomber dans l'illégalité. Voilà pourquoi nous n'expliquerons pas ici comment fonctionne un linker (tel que la R4, M3, etc.).
C'était juste à titre d'information, rien de plus. ;)
Installation des outils nécessaires
Bien ! Après vous avoir expliqué les deux possibilités qui s'offraient à vous, il est enfin temps d'installer PAlib et tout le nécessaire pour enfin programmer pour notre chère NDS. En avant :pirate: !
Pour ce faire, nous allons utiliser un auxiliaire de programmation utilisant le BATCH pour sa compilation. Il est donc important d'avoir un ordinateur sous WINDOWS. Pour les utilisateurs de Linux, ne vous inquiétez pas, on va arriver à votre cas ;) ! Pour ceux qui utilisent autre chose, il faudra, bien entendu, vous adapter au tutoriel.
L'auxiliaire de programmation
Nous utiliserons DevkitPro pour ce tutoriel. Téléchargez la dernière version (à l'heure où j'écris ces lignes c'est la version 1.5.0).
La bibliothèque : PALib
Pour information, PA veut dire Programmer's Arsenal et Lib veut dire Library (qui signifie "bibliothèque") tout simplement. Il nous sera très utile : c'est la bibliothèque que nous utiliserons pour la programmation sur DS. Télécharger PALib.
Actuellement c'est la version la plus récente est la 100707, selon le site elle serait maintenue jusqu'à ce qu'un autre développeur reprenne le projet...
L'installation
Ça y est, nous allons enfin installer les éléments nous permettant de programmer !
Tout d'abord lancez l'installateur devkitpro pour qu'il aille rechercher les fichiers nécessaires à l'installation.
Lors de celle-ci vous pouvez laisser le chemin par défaut (C:\devkitPro ; ce qui est conseillé si vous n'êtes pas très à l'aise avec les changements de répertoire et de variables d'environnement qui risquent d'arriver), et au choix des composants à installer vous pouvez ne cocher que les cases correspondant à la DS (si vous avez peur de faire une bêtise, laissez tout coché ce n'est pas gênant :) ).
Si vous avez téléchargé DevkitArm 20, alors remplacez la nouvelle version par l'ancienne (présente dans votre dossier devkitpro).
Ensuite il faut installer .Net framework pour que PAlib fonctionne correctement, il se peut qu'il soit déjà installé sur votre ordinateur, mais si vous hésitez téléchargez-le ici et (ré)installez-le ! Ne vous inquiétez pas si vous avez une erreur disant qu'il n'a pas pu l'installer, la raison est souvent qu'il est déjà installé.
Il nous reste plus que PALib ! Installez-la dans le même le dossier devkitpro, c'est très important.
Pour vérifier que tout a bien été installé rendez-vous dans C:\devkitPro\palib\examples\Text\Normal, choisissez un dossier (HelloWorld par exemple) et double cliquez sur build.bat. Une fenêtre de console devrait apparaître (le projet HelloWorld se compile en fait), s'il n'y a pas d'erreur vous devriez trouver un fichier HelloWorld.nds et HelloWorld.ds.gba dans le répertoire :) !
Citation
make[1]: *** [/c/devkitPro/palib/examples/Text/Normal/HelloWorld/HelloWorld.elf]
Error 1
make: *** [build] Error 2
Il vous faudra remplacer DevkitArm que vous avez par un DevkitARM antérieur.
Si vous avez une erreur qui s'apparente à celle-ci :
Citation
arm-eabi-gcc.exe: CreateProcess: No such file or directory
Il faudra modifier la valeur de la variable Path. Pour y accéder sous Vista, faites Démarrer -> Panneau de configuration -> Système -> Paramètres Système Avancés (colonne à gauche). Cliquez sur le bouton "Variable d'environnement", puis sur "PATH" et "Modifier...". Une fenêtre apparaîtra et, à la fin du texte éditable, mettez un point-virgule et ajoutez ce code :
Citation
C:\devkitPro\devkitARM\arm-eabi\bin;C:\devkitpro\devkitARM\bin;\msys\bin;c:\devkitpro\msys\bin;c:\devkitpro\devkitarm;C:\devkitPro\devkitARM\libexec\gcc\arm-eabi\4.1.1;
Vous pouvez remplacer C:\ par le chemin jusqu'à devkitpro (exemple : C:\bidule\).
Si votre compilateur se met à vous injurier comme ce n'est pas permis, c'est peut-être que la libnds ne correspond pas à PALib. Il faudra donc la changer en remplaçant le dossier "libnds" de devkitpro par le contenu de libnds (si le lien est mort vous pouvez recomposer le dossier à l'aide de : libnds, le dossier ; libfat, à ajouter dans le même dossier et dswifi, toujours dans le même dossier).
Il se peut que la compilation se fasse avec beaucoup de warnings, ignorez-les.
Si vous avez un autre type d'erreur, recommencez l'installation à zéro, ou cherchez sur le Web une solution (n'oubliez pas les forums du site) :) .
L'émulateur
Un émulateur est une application servant à visualiser un jeu (de DS ou autre). Il vous servira beaucoup pour tester vos jeux, c'est plus pratique que de prendre chaque fois sa DS :p . Sauf comme expliqué plus tôt, les émulateurs ne gèrent pas / gèrent mal les fichiers et le Wifi.
Nous vous conseillons un excellent émulateur : No$GBA que vous pouvez télécharger ici (plus bas choisissez "Download windows version" et non DOS).
Sinon pour votre gouverne sachez qu'il en existe pleins d'autres : PicoDriveDS, Spec DS, DeSmuMe, DSEMU, SnezziDS, GeoSIDeaS, Dualis, NDS NeoPop, etc.
Malgré le nombre d'émulateurs, nous vous conseillons no$gba qui est actuellement l'un des émulateurs DS (et GBA) les plus performants.
L'éditeur
Il est évident qu'il nous en faut un. Et bien que Bloc-Note pourrait suffire, pour une meilleure lisibilité du code (on parlera d'indentation et de coloration syntaxique du code), nous vous conseillons d'utiliser un éditeur plus évolué. En premier choix, nous vous proposons Notepad++ (version zip) / Notepad++ (version installateur, plus complet que le zip).
Toutefois vous pouvez continuer sous votre IDE préféré si ça vous chante, de toute façon nous ne compilerons pas avec eux, mais comme dit plus haut avec un fichier .bat. Si vous prenez un IDE, pensez bien à changer de Makefile !
Tiens d'ailleurs ça me fait penser qu'il est enfin temps de se jeter dans le vif du sujet, allons-y ! :pirate:
Création d'un projet
Juste avant de nous lancer dans la programmation nous devons créer un projet ! Voici les étapes (très simples) :
pour commencer, créez un dossier "projets" dans le répertoire de devkitpro ;
ensuite, dans ce dossier, créez un dossier NomDeMonProjet (ne mettez pas d'espace) ;
puis allez dans le dossier palib/template et copiez son contenu à l'exception des dossiers data, include et source ;
retournez dans votre dossier et collez ces fichiers ;
rajoutez-y un dossier nommé "source" (il est important de garder le nom "source" sinon, au moment de la compilation, votre fichier source ne sera pas trouvé o_O ).
Et voilà, il ne vous restera plus qu'à renouveler cette action pour créer un nouveau projet !
A partir de là il suffit de créer un fichier main.c (ou .cpp si vous programmez en C++) dans le dossier source.
Comment compiler un projet ?
Il faut lancer build.bat (en double-cliquant dessus, ça suffit). Tiens en parlant de programme, ça vous dirait qu'on s'y mette ? Alors allons-y !! :D
Et sous Linux ?
L'installation
Téléchargez le devkitpro pour Linux (tout est réuni dans un gros fichier).
Décompressez-le dans votre dossier personnel (soit /home/nomdutilisateur/) :
tar -jxvf devkitpro-pour-linux.tar.bz2
Normalement, le chemin vers le devkitpro est donc /home/NOMDUTILISATEUR/devkitpro/. Allez donc dans ce dossier et décompressez-y les fichiers devkitARM-r21-linux.tar.bz2 et PAlib.tar.bz2 comme nous venons de le faire.
tar -jxvf devkitARM-r21-linux.tar.bz2
tar -jxvf PAlib.tar.bz2
Vous devriez avoir cette arboresence :
/
|--home
|--NOMDUTILISATEUR
|--devkitpro
|-devkitARM
|-libnds
|-Other Libs
|-PAlib
Modifiez maintenant le fichier .bashrc (situé dans votre dossier personnel) en y ajoutant les 3 lignes suivantes :
export DEVKITPRO=/home/NOMDUTILISATEUR/devkitpro
export DEVKITARM=$DEVKITPRO/devkitARM
export PAPATH=$DEVKITPRO/PAlib/lib
Exécutez la commande suivante dans un terminal pour appliquer les changements :
source .bashrc
Pour vérifier que les changements ont été appliqués, exécutez ensuite cette commande :
env | grep PAPATH
Si vous voyez quelque chose s'afficher à l'écran (un chemin, comme PAPATH=/home/rayman3640/devkitpro/PAlib/lib
), alors les changements ont été appliqués. Sinon, redémarrez votre terminal et réessayez.
Maintenant, il ne reste plus qu'à tester ! Le mieux est de compiler un exemple.
Allez dans le dossier du devkitpro, puis dans /PAlib/examples/Text/Normal/HelloWorld.
Exécutez cette commande :
make
Vous pouvez obtenir ceci :
bash: make : commande introuvable
Cela signifie que make n'est pas installé. Pour l'installer, exécutez donc cette commande avant de réessayer :
sudo apt-get install build-essential
Si vous obtenez quelque chose comme ceci après avoir exécuté make (noté que j'ai légèrement raccourci le code) :
arm-eabi-g++ -g -mthumb-interwork -mno-fpu -L/home/rayman3640/devkitpro/PAlib/lib/lib -Wl
Nintendo DS rom tool 1.36 - Oct 23 2007 23:03:47
by Rafael Vuijk, Dave Murphy, Alexei Karpenko
built ... HelloWorld.ds.gba
dsbuild 1.21 - Oct 23 2007
using default loader
Alors vous avez réussi ! :)
L'émulateur
Sous Linux, le choix d'émulateurs est un peu plus réduit. Je vous recommande desMuMe, car il en existe une version pour Linux. Sur certaines distributions, il est possible de l'installer via un dépôt (c'est le cas notamment d'Ubuntu). Si votre distribution utilise des paquets DEB, essayez comme ceci :
apt-get install desmume
Si votre distribution utilisez des paquets RPM, essayez ceci :
yum install desmume
Sinon, allez dans le dossier du devkitpro puis dans /PAlib/emulators/desmume-linux et exécutez ces commandes pour installer desMuMe :
chmod +x desmume-gtk-0.7.0.x86.package
./desmume-gtk-0.7.0.x86.package
Enfin, il est possible d'utiliser no$gba (qui se trouve dans le dossier /PAlib/emulators/no$gba/ avec Wine en faisant :
wine no\$gba.exe
Vous pouvez même essayer d'utiliser les autres émulateurs du dossier avec Wine, mais desMuMe et no$gba sont meilleurs ;) .
Un grand merci à Rastagong d'avoir complété notre tutoriel :) !
Nous voilà fin prêts pour attaquer la programmation :lol: !
Notre premier programme
Maintenant, nous allons enfin commencer à programmer :) .
Le code par défaut
Dernières préparations
Il faut que vous créiez un fichier main.c (fichier C) ou main.cpp (fichier C++), selon le langage de programmation que vous avez choisi, dans le dossier "source". Si vous n'avez pas nommé le fichier main ou que vous ne l'avez pas mis dans le dossier source, alors il ne sera pas trouvé lors de la compilation.
Le code de base
Alors le voici, vous l'attendiez tous le code qu'il faut retenir par coeur :D .
#include <PA9.h>
int main (int argc, char **argv)
{
PA_Init();
while (1)
{
PA_WaitForVBL();
}
return 0;
}
:euh: Heu...
Pas de panique, je vais tout vous expliquer :D .
Explications
#include <PA9.h>
Inclusion du header de PAlib, je suppose que vous l'aviez deviné. ;) .
PA_Init();
Cette ligne est très importante ! Elle initialise PAlib (donc l'utilisation de ses fonctions), c'est pourquoi il ne faut surtout pas l'oublier :p .
while (1)
Boucle blanche (ou plus communément appelée boucle infinie), c'est ici que nous mettrons nos instructions. Elle s'exécutera jusqu'à ce que l'on appuie sur le bouton power.
PA_WaitForVBL();
En parlant de rafraîchissement ! Cette fonction permet de synchroniser le rafraîchissement de l'écran à 60 frames par secondes (abrégé fps) dans la boucle. Il y a 60 frames par secondes, donc on peut en déduire que les écrans de la Nintendo DS ont une fréquence de 60 Hz. N'oubliez surtout pas de la mettre sinon il se peut que votre programme soit rafraîchi beaucoup trop vite, et là c'est le plantage :-° .
Hello world !
On va y aller doucement pour les textes, sinon, on va vite se mélanger les pinceaux :lol: .
L'initialisation du texte
Il faut initialiser le texte avec la fonction PA_LoadDefaultText.
void PA_LoadDefaultText ( u8 ecran, u8 background ) ;
Voici ce qu'il faut donner comme arguments à cette fonction :
ecran : c'est l'écran où vous voulez afficher du texte (généralement, on initialise les deux pour être tranquille) ;
- 0 : écran tactile
- 1 : écran du hautbackground : C'est la surface sur laquelle vous voulez écrire votre texte (il y en a 4).
- 0 : haute couche
- 1 : la suivante (plus on descend, moins on est prioritaire et plus on on a de risques d'être recouvert :) )
- 2 : la suivante
- 3 : la dernière couche (celle qui a le plus de chance d'être recouverte)
Pour le background, cela marche de la même façon que les calques sous Photoshop.
Le texte !
Il existe la fonction u16 PA_OutputSimpleText(u8 ecran, u16 colonne, u16 ligne, const char *texte)
mais elle n'est pas comparable à printf. En effet, cette fonction sert à créer un texte statique. Autrement dit, on ne peut pas afficher de variables ("%d", "%f", "%c", "%s", etc...). C'est pour cette raison que nous la laissons tomber pour privilégier la fonction :
void PA_OutputText(u8 ecran, u16 x, u16 y, const char *texte, ...) ;
Cette fonction présente deux avantages : elle permet de créer un texte dynamique (grâce à l'affichage de variables modélisées par l'argument ...), et elle nous fait de la place sur le disque dur comparée à sa frangine :p .
Alors, analysons cette fonction :
u8 ecran : on choisit l'écran sur lequel écrire
- 0 : écran tactile
- 1 : écran du hautu16 x :
- valeur comprise entre 0 et 31 (puisque 256 / 8 = 32, n'oubliez pas de réfléchir en tiles !).
Note : 31 n'est pas trop recommandé puisque votre texte est censé faire plus d'un caractère.. Et si vous dépassez, le texte sera remis à la ligne.u16 y :
- valeur comprise entre 0 et 23 (192 / 8 = 24)
Ici si vous dépassez, votre texte ne sera pas affiché...char *text : votre chaîne.
... : toutes vos variables.
Ce qui donnerait :
#include <PA9.h>
int main(int argc, char **argv)
{
const char *pseudo="PODS";
PA_Init();
PA_LoadDefaultText(1,0);//on initialise l'écran du haut à la première couche
PA_LoadDefaultText(0,3);//on initialise l'écran tactile à une couche peu profonde (on peut se le permettre ici, on n'affichera qu'un texte)
//Le texte ne changeant pas, pas besoin de le mettre dans la boucle
PA_OutputText(1, 2, 2, "Hello World !");
PA_OutputText(0,5,5,"Mon pseudo est %s.",pseudo);
while(1)
{
PA_WaitForVBL();
//Boucle infinie (inutile de l'utiliser puisqu'il n'y aura pas de changement)
}
return 0;
}
Avec No$GBA :

#include <PA9.h>
int main(int argc, char **argv)
{
int compteur=0;
PA_Init();
PA_LoadDefaultText(0,0);
for(compteur = 0; compteur < 2147483647; compteur++)//On limite à la taille d'un int, c'est suicidaire mais en même temps ce n'est qu'un exemple
{
PA_OutputText(0,0,0,"%d",compteur);
PA_WaitForVBL();
PA_ClearTextBg(0);
/* Il faudrait attendre plus longtemps pour éviter que le texte ne clignote, mais comme je l'ai dit ce n'est qu'un exemple ;) */
}
return 0;
}
Tadaaaa, nous y sommes arrivés :D !
Nous allons voir comment utiliser le stylet et les touches...
Les événements
Essayez de créer un jeu sans gérer l'appui des touches et le stylet... Difficile, hein ? :D Nous allons donc nous attaquer à cette tâche qui, comme nous allons le voir, est trop très facile !
Configurer l'émulateur
On va d'abord configurer les touches de no$gba, de DeSmuMe et de iDeaS. Si vous avez un autre émulateur, il faudra vous débrouiller ! :p
Pour no$gba
Faites F11 ou Options->Controls Setup.
Regardez la colonne de droite. Cliquez dans l'espace blanc à droite de la commande (ex : Up) et appuyez sur la touche de clavier désirée pour appeler cette commande.

Configuration de snake_48 :-°
Pour DeSmuMe
Faites Config->Control Config.
Cliquez dans le menu déroulant à droite de la commande et appuyez sur la touche de clavier désirée pour appeler cette commande.
Pour iDeaS
Faites Ctrl+K ou Options->Key Config.
Appuyez sur le bouton correspondant à la commande et appuyez sur la touche du clavier désirée pour l'appeler.
Les touches
Les touches sont représentées par la structure nommée "Pad". Cette structure en renferme d'autres :
Newpress : nouvel appui (elle était dure, la traduction :p ) ;
Held : touche enfoncée ;
Released : touche relâchée.
Et chacune de ces structures-membres ont des membres correspondant aux touches :
Up
Down
Left
Right
A
B
X
Y
L
R
Start
Select
Anykey : n'importe quelle touche
Ces membres renvoient 0 ou 1 en fonction de la structure-membre qu'on appelle et, bien sûr, de l'action du joueur ;) . On peut ainsi avoir ceci :
/*Mettre le début du code*/
while(1)
{
if(Pad.Newpress.A==1) //Si l'utilisateur fait un NOUVEL appui sur A
/* instruction */
else if(Pad.Held.R==1) //Si l'utilisateur laisse son doigt appuyé sur la touche R
/* instruction */
else if(Pad.Released.X==1) //Si l'utilisateur relâche la touche X
/* instruction */
PA_WaitForVBL();
}
/*Mettre la fin du code*/
if(Pad.Newpress.A==1)
Par :
if(Pad.Newpress.A)
Ainsi que :
if(Pad.Newpress.Y==0)
Par :
if(!Pad.Newpress.Y)
Mais je pense que l'utilisation des booléens est déjà acquise pour la plupart ;) .
Le stylet !
Vous vous demandiez tous "Comment gère-t-on le stylet ?", eh bien ouvrez grand vos yeux !

Le stylet est appelé par la structure "Stylus".
Celle-ci contient cinq membres : X et Y (vous avez sûrement deviné que ceci renvoie les coordonnées du stylet), Newpress, Held et Released :
/*Mettre le début du code*/
PA_LoadDefaultText(0,0);
while(1)
{
PA_OutputText(0,0,0,"( %d ; %d ) ", Stylus.X, Stylus.Y);
PA_WaitForVBL();
}
/*Mettre la fin du code*/
Avouez tout de même qu'on ne pouvait pas faire plus simple :D !
Le DS Motion Pak
Le DS Motion quoi ? o_O
Vous avez très bien lu, le DS Motion Pak (non il ne manque pas un "c" :p ).
Qu'est-ce que c'est ? Il s'agit d'un accéléromètre (un appareil qui mesure les mouvements de l'objet sur lequel il est fixé) pour votre Nintendo DS. Il se met dans le SLOT 2 (port GBA) et ressemble à ceci (cliquez pour agrandir) :
Il existe une version qui se met dans le SLOT 1 (port NDS),que je n'utiliserai pas dans ce tutoriel, appelé DS Motion Card (pour ceux qui mettent leurs homebrews sur un linker GBA) :
Bien sûr, ce n'est pas gratuit >_ . En effet, il faut compter 30 à 40 € pour l'un ou l'autre.
Mais je ne regrette pas l'achat. On peut contrôler des éléments, et tout ça sans bouton (donc ça nous libère des possibilités pour créer un jeu certes moins compatible, mais plus poussé). De plus, ça met une ambiance à la Wii ^^ . Un conseil, évitez de vous faire filmer quand vous jouez à un jeu nécessitant le Motion :-° ...
Maintenant que les présentations sont faites, je vais vous annoncer une bonne nouvelle : PALib implémente totalement le Motion !
Tout d'abord, il faut initialiser le système Motion avec void PA_MotionInit(void);
.
Ensuite, il faut savoir si le DS Motion Pak est inséré. Pour ça il existe la fonction u8 PA_CheckDSMotion();
. Cette fonction retourne 0 si le DS Motion Pak n'est pas inséré ou 1 s'il l'est.
Et comment faire pour récupérer les données de l'accéléromètre ?
Il existe une variable Motion de type motion_struct.
Voici ses attributs :
X : la rotation dans les X. Cette valeur varie entre -128 (droite) et 128 (gauche).
Y : la rotation dans les Y. Cette valeur varie environ entre -134 (console verticale, vous ne voyez pas les écrans) et 114 (console verticale, écrans vers vous).
Z.
Vx.
Vy.
Vz.
AccelX.
AccelY.
AccelZ.
Zrot.
Vzrot.
Angle.
Force.
Sachez que dans tous les codes utilisant Motion, seuls les membres X et Y sont utilisés. Vous pouvez cependant tester les membres avec PA_OutputText en tournant et en déplaçant votre Nintendo DS dans tous les sens :D !
Attendre un événement
Vous avez sûrement vu les fausses valeurs indiquées pour les coordonnées du stylet lorsque l'on n'a pas encore touché l'écran.
On va utiliser PA_WaitFor(evenement)
prenant pour argument l'événement à attendre.
Voici un petit exemple :
#include <PA9.h>
int main(int argc, char ** argv)
{
PA_Init();
PA_LoadDefaultText(1,0);
while(1)
{
PA_WaitFor(Stylus.Held); //Si on appuie ou si on reste appuyé
PA_OutputText(1,0,0,"( %d ; %d )", Stylus.X, Stylus.Y);
PA_ClearTextBg(1);
}
return 0;
}
On se retrouve quand même avec de fausses valeurs ! o_O Comment faut-il faire ?
Bonne question ^^ , en fait il est toujours bon de mettre 3 ou 4 rafraîchissements (PA_WaitForVBL();
) avant le la boucle principale, pour "dérouiller" en quelque sorte la NDS :
#include <PA9.h>
int main(int argc, char ** argv)
{
PA_Init();
PA_LoadDefaultText(1,0);
PA_WaitForVBL();PA_WaitForVBL();PA_WaitForVBL();
while(1)
{
PA_WaitFor(Stylus.Held);
PA_ClearTextBg(1);
PA_OutputText(1,0,0,"( %d ; %d )", Stylus.X, Stylus.Y);
}
return 0;
}
Mais vous pouvez aussi initialiser le stylet comme ceci par exemple :
Stylus.X=0;
Stylus.Y=0;
Exercice
Pour vous faire pratiquer avant le TP, je vous propose un petit exercice :) .
Présentation
Votre programme doit récupérer les dernières coordonnées du stylet (utilisez simplement des conditions).
Il doit les afficher si l'on appuie sur la touche B.
Si l'utilisateur n'a pas encore utilisé le stylet, on affiche :
Utilise le stylet !
sinon :
x : 10, y : 211
par exemple.
À vous de jouer ! ;)
Correction
#include <PA9.h>
int main(int argc, char ** argv)
{
PA_Init();
PA_InitText(1,0);
while(1)
{
if(Pad.Held.B)
{
PA_OutputText(1,0,0,"Utilise le stylet !");
if(Stylus.Held)
{
PA_ClearTextBg(1); //On efface l'écran
PA_OutputText(1,0,0,"x : %d, y: %d",Stylus.X,Stylus.Y);
}
}
PA_WaitForVBL();
PA_ClearTextBg(1); //On efface l'écran
}
return 0;
}
C'était pas si dur, et un jeu est composé de beaucoup d'événements !
TP: Plus ou Moins, votre premier jeu
Hé hé ! Si vous pensiez vous être débarrassés de ce maudit TP après la première partie du cours de M@teo21, détrompez-vous ! Il est de retour et désormais il compte bien s'attaquer à votre Nintendo DS ! :D
Préparatifs
Gestion du nombre aléatoire
Cette fois-ci nous n'allons pas utiliser time.h puisque ça ne fonctionnera pas avec la DS, il faut utiliser une autre fonction qui utilise l'horloge interne de la console, sans que vous ayez besoin de connaître les éléments qui permettent de récupérer l'heure. Nous allons juste utiliser des fonctions qui nous facilitent la vie en récupérant déjà l'heure.
Tout d'abord dire à la console que nous allons lui demander un nombre aléatoire :
u32 PA_InitRand ();
Et utiliser la fonction qui renvoie le nombre:
u32 PA_RandMax (u32 max);
Cette fonction renvoie un nombre en 0 et max INCLUS.
Voilà une autre fonction utile :
u32 PA_RandMinMax (u32 min, u32 max);
Vous remarquerez que la fonction renvoie et demande des u32 (32 bits), nos variables pourront être de type int ou long, il n'y a pas de soucis de ce coté. ;)
La gestion du clavier
Ne vous êtes-vous pas demandés comment diable l'utilisateur rentrera-t-il son nombre ? La solution : utiliser le clavier de la Nintendo DS bien sûr ;) !
Initialisation du clavier
Il faut d'abord initialiser le clavier avec :
void PA_InitKeyboard(numero_bg);
Apparition du clavier
Sachez que le clavier part du bas pour venir à la position voulue. Il faut juste que vous rentriez les coordonnées idéales du clavier pour voir une belle animation.
On utilisera :
void PA_KeyboardIn (s16 x, s16 y);
Mais le clavier vient du bas et un seul mouvement est possible (du bas vers le haut).
Je vais vous donner une fonction toute faite pour faire un déplacement comme vous le voulez :
//PROTOTYPE
void defiler_clavier(int,int,int,int,int);
//FONCTION
void defiler_clavier(int x,int y,int x1,int y1,int nvbl)
{
/*Le clavier va de (x;y) à (x1;y1) avec nvbl VBL pour chaque étape du déplacement*/
int i=0;
PA_ScrollKeyboardXY(x,y); //On met le clavier à la position (x;y)
while(1)
{
/*On se rapproche des coordonnées (x1;y1)*/
if(x<x1)
x++;
else if(x>x1)
x--;
if(y<y1)
y++;
else if(y>y1)
y--;
if(x==x1 && y==y1)
break;
PA_ScrollKeyboardXY(x,y); //On replace le clavier à la nouvelle position
/*On attend en fonction du nombre de VBL donné par la variable nvbl*/
for(i=0;i<nvbl;i++)
PA_WaitForVBL();
}
}
Le clavier passera de (x;y) à (x1;y1) et à chaque tour de boucle, il y aura nvbl PA_WaitForVBL.
Traitement du clavier
Ça, c'est une toute autre histoire ! :p
Il faut opérer en ce sens :
créer un entier qui va contenir le nombre progressivement entré par l'utilisateur (caractère par caractère).
Mais comment allons nous ajouter des chiffres ou en retirer ?
Bonne question... A chaque fois qu'on ajoute un chiffre, il doit aller tout à droite (à la place des unités). Donc on multiplie le nombre par 10 et on y ajoute tout simplement le chiffre entré. Pour retirer le dernier chiffre (lorsqu'on appuie sur Backspace) il suffira de diviser le nombre par 10 car le résultat sera tronqué à l'unité.
créer une variable de type int contenant le nombre de tentatives ;
créer une variable s32 qui contiendra le nombre de lettres ;
créer une variable char qui contiendra la lettre actuelle ;
coder la boucle en reconnaissant la lettre entrée et en l'analysant :
char lettre=0; /*INITIALISATIONS A METTRE*/ while(1) { lettre=PA_CheckKeyboard(); if(lettre>='0' && lettre<='9') PA_OutputText(1,0,3,"%d",lettre-'0'); else if(lettre==PA_BACKSPACE) //ON EFFACE LE DERNIER CARACTÈRE, PENSEZ A FAIRE UNE FONCTION else if(lettre=='\n') //ON VALIDE /*RESTE DE LA BOUCLE*/ }
</puce>
Bon, côté interface, on écrira tout sur l'écran supérieur.
En premier, la console affichera :
Entrez un nombre entre 1 et 100 inclus :
Au-dessous, (ligne 3) vous écrirez l'entrée du joueur.
Si le nombre entré est plus petit que le nombre à trouver, on effacera l'écran supérieur pour écrire :
Le nombre %s est trop petit. Veuillez attendre 2 secondes...
Où %s est la chaîne entrée par l'utilisateur.
Si le nombre est trop grand, on écrira :
Le nombre %s est trop grand. Veuillez attendre 2 secondes...
Et si le nombre est trouvé, on écrira :
Bravo ! Vous avez réussi à trouver le nombre %s en %d tentative%s ! Veuillez attendre 2 secondes...
Où %d est le nombre de tentatives et le 2ème %s est le résultat d'une ternaire pour savoir si on met le pluriel ou pas.
N'oubliez pas de faire l'attente pour les 2 secondes.
Au passage, on effacera l'écran à chaque tour de boucle (si on efface le dernier caractère, la mise à jour sera automatique). Et si le joueur a trouvé le nombre, on quitte la boucle (utiliser while(1)
avec un break
lorsque l'objectif est atteint, afin d'attendre que l'utilisateur appuie sur Entrée). Et, après celle-ci, on affichera :
Bravo ! Vous avez réussi à trouver le nombre %s en %d tentative%s !
Disparition du clavier
La simple fonction
void PA_KeyboardOut (void);
nous "rangera" automatiquement notre clavier ;) . Si vous ne voulez pas l'utiliser, vous pouvez utiliser notre fonction defiler_clavier.
Customisation du clavier :)
Vous pouvez personnaliser votre clavier. Vous devez créer d'abord un fichier image qui contiendra votre clavier. Ce fichier mesurera 256*1024 pixels. Aux coordonnées (0;0) dessinez votre clavier qui doit mesurer 208x96 pixels. La contrainte est que les touches doivent être de même dimensions que le clavier par défaut (il y a un exemple ci-dessous pour que vous puissiez bien voir les dimensions). Aux coordonnées (0;512) dessinez votre clavier (quand le joueur appuiera sur la touche Shift ou Caps).
Voici un exemple de clavier :
![]() ![]() | Il s'agit de l'image devkitPro\PAlibExamples\Input\Keyboard\KeyboardCustom\source\gfx ^^ . Citation
Pour utiliser un clavier personnalisé, on utilise
Voilà, vous savez tout à présent sur les claviers ;) . |
Correction
Voici une correction parmi d'autres :) :
#include <PA9.h>
void defiler_clavier(int,int,int,int,int);
int main(int argc, char ** argv)
{
int nombre,entre=0,i,compteur=0;
char lettre;
/*INITIALISATIONS*/
PA_Init();
PA_LoadDefaultText(1,0);
PA_InitRand(); //On initialise le Rand
PA_InitKeyboard(2); //On initialise le clavier sur le fond 2
defiler_clavier(50,48,25,96,1); //Un défilement du clavier pour le faire apparaître, vous pouvez le changer
nombre=PA_RandMinMax(1,100); //La variable nombre contient un nombre compris entre 1 et 100 inclus
while(1) //Tant que le nombre entré par le joueur est différent de la variable nombre
{
PA_OutputText(1,0,0,"Entrez un nombre entre 1 et 100 inclus :");
lettre=PA_CheckKeyboard(); //On récupère la valeur numérique de l'entrée de l'utilisateur
if(lettre>='0'&&lettre<='9'&&entre<100) //Il faut que la touche pressée soit comprise entre 0 et 9 (et rien d'autre), et que le nombre entré soit inférieur à 100, car le nombre maximum est 100
{
entre*=10; //On décale tous les chiffres d'un cran vers la gauche (une dizaine)
entre+=lettre-'0'; //On ajoute les nouvelles "unités"
}
else if(lettre==PA_BACKSPACE) //Si on appuie sur Backspace
entre/=10; //On décale tous les chiffres d'un cran vers la droite
else if(lettre=='\n') //Si on appuie sur l'équivalent de la touche Entrée
{
compteur++; //Le compteur de tentatives est augmenté
PA_ClearTextBg(1); //On efface le texte de l'écran supérieur (celui où sont écrits tous les textes à afficher)
if(entre<nombre)
{
PA_OutputText(1,0,0,"Le nombre %d est trop petit. Veuillez attendre 2 secondes...",entre);
entre=0;
}
else if(entre>nombre)
{
PA_OutputText(1,0,0,"Le nombre %d est trop grand. Veuillez attendre 2 secondes...",entre);
entre=0;
}
else
break;
for(i=0;i<120;i++)
PA_WaitForVBL(); //On attend 2 secondes
}
if(entre!=0) //On écrit pas le nombre s'il est "vide"
PA_OutputText(1,0,3,"%d",entre); //On laisse le nombre affiché
PA_WaitForVBL();
PA_ClearTextBg(1); //On efface l'écran supérieur
}
PA_OutputText(1,0,0,"Bravo ! Vous avez réussi à trouver le nombre %d en %d tentative%s !",entre,compteur-1,compteur-1<2?"":"s"); //Ternaire pour vérifier si, en fonction de compteur-1, on doit mettre du singulier ou du pluriel. Et je mets compteur-1 car je fais cadeau de la bonne tentative
PA_KeyboardOut(); //On range notre clavier
while(1)
PA_WaitForVBL();
return 0;
}
void defiler_clavier(int x,int y,int x1,int y1,int nvbl)
{
/*Le clavier va de (x;y) à (x1;y1) avec nvbl VBL pour chaque étape du déplacement*/
int i=0;
PA_ScrollKeyboardXY(x,y); //On met le clavier à la position (x;y)
while(1)
{
/*On se rapproche des coordonnées (x1;y1)*/
if(x<x1)
x++;
else if(x>x1)
x--;
if(y<y1)
y++;
else if(y>y1)
y--;
if(x==x1&&y==y1)
break;
PA_ScrollKeyboardXY(x,y); //On replace le clavier à la nouvelle position
/*On attend en fonction du nombre de VBL donné par la variable nvbl*/
for(i=0;i<nvbl;i++)
PA_WaitForVBL();
}
}
Merci à daminator pour son optimisation du code !
Améliorations
On peut :
créer plusieurs niveaux de jeu ;
créer une aide qui réagit en fonction du nombre à trouver et du nombre entré par l'utilisateur comme ceci, par exemple :
Le nombre à trouver se trouve entre %d et %d.
On va créer plusieurs niveaux de jeu ^^ . Ce sera utile pour la suite... Ça déborde un peu sur la deuxième partie mais ce n'est pas grave, on va faire un menu très léger. On a 3 choix : un nombre entre 1 et 100 ; entre 1 et 500 et enfin entre 1 et 1000.
Le choix courant sera en rouge par exemple, et les autres seront écrits dans la couleur par défaut.
Bien sûr, il faudra créer une variable max qui sera affichée pour Entrez un nombre entre 1 et [...] inclus
.
Le mieux est de créer 2 boucles (une pour le menu, l'autre pour le jeu).
Voilà, vous avez assez de renseignements pour le coder tout seul ^^ .
Mais voici mon amélioration ^^ :
#include <PA9.h>
void defiler_clavier(int,int,int,int,int);
int main(int argc, char ** argv)
{
int nombre,entre=0,i,compteur=0,max,menu=0;
char lettre;
PA_Init();
PA_LoadDefaultText(1,0);
PA_InitRand();
//Boucle de menu
while(!Pad.Held.A&&!Pad.Held.Start)
{
switch(menu)
{
case 0:
PA_OutputText(1,5,9,"%c11 -> Entre 1 et 100");
PA_OutputText(1,5,11,"2 -> Entre 1 et 500");
PA_OutputText(1,5,13,"3 -> Entre 1 et 1000");
break;
case 1:
PA_OutputText(1,5,9,"1 -> Entre 1 et 100");
PA_OutputText(1,5,11,"%c12 -> Entre 1 et 500");
PA_OutputText(1,5,13,"3 -> Entre 1 et 1000");
break;
default:
PA_OutputText(1,5,9,"1 -> Entre 1 et 100");
PA_OutputText(1,5,11,"2 -> Entre 1 et 500");
PA_OutputText(1,5,13,"%c13 -> Entre 1 et 1000");
break;
}
menu+=Pad.Newpress.Down-Pad.Newpress.Up;
if(menu<0)
menu=2;
else if(menu>2)
menu=0;
PA_WaitForVBL();
PA_ClearTextBg(1);
}
switch(menu)
{
case 0:
nombre=PA_RandMinMax(1,100);
max=100;
break;
case 1:
nombre=PA_RandMinMax(1,500);
max=500;
break;
default: //Il est bon de mettre un defalut, car si le choix n'est pas 3 (un bug ou alors un problème dans le code), une valeur est quand-même attribuée à nombre et à max. De plus, pour être désolé de ce problème, la valeur par défaut est celle comprise entre 1 et 1000 ! *rire sadique*
nombre=PA_RandMinMax(1,1000);
max=1000;
break;
}
PA_InitKeyboard(2);
defiler_clavier(50,48,25,96,1);
//Boucle du jeu
while(1)
{
PA_OutputText(1,0,0,"Entrez un nombre entre 1 et %d inclus :",max);
lettre=PA_CheckKeyboard();
if(lettre>='0'&&lettre<='9'&&entre<=max)
{
entre*=10;
entre+=lettre-'0';
}
else if(lettre==PA_BACKSPACE)
entre/=10;
else if(lettre=='\n')
{
compteur++;
PA_ClearTextBg(1);
if(entre<nombre)
{
PA_OutputText(1,0,0,"Le nombre %d est trop petit. Veuillez attendre 2 secondes...",entre);
entre=0;
}
else if(entre>nombre)
{
PA_OutputText(1,0,0,"Le nombre %d est trop grand. Veuillez attendre 2 secondes...",entre);
entre=0;
}
else
break;
for(i=0;i<120;i++)
PA_WaitForVBL();
}
if(entre!=0)
PA_OutputText(1,0,3,"%d",entre);
PA_WaitForVBL();
PA_ClearTextBg(1); //On efface l'écran supérieur
}
PA_OutputText(1,0,0,"Bravo ! Vous avez réussi à trouver le nombre %d en %d tentative%s !",entre,compteur-1,compteur-1<2?"":"s");
PA_KeyboardOut(); //On range notre clavier
while(1)
PA_WaitForVBL();
return 0;
}
void defiler_clavier(int x,int y,int x1,int y1,int nvbl)
{
int i=0;
PA_ScrollKeyboardXY(x,y);
while(1)
{
if(x<x1)
x++;
else if(x>x1)
x--;
if(y<y1)
y++;
else if(y>y1)
y--;
if(x==x1&&y==y1)
break;
PA_ScrollKeyboardXY(x,y);
for(i=0;i<nvbl;i++)
PA_WaitForVBL();
}
}
Voilà, vous avez assez de bases maintenant pour faire de petits jeux en console sur votre Nintendo DS !
La 2D de base
Les jeux de DS sont autre chose que des écrans noirs :lol: . Nous allons donc apprendre, à ce stade, à créer des arrière-plans et des sprites.
Et pourquoi pas des Coca-Cola ? :-°
Les sprites ne se boivent pas, mais ce sont des images comme celle du personnage principal d'un jeu, par exemple.
Outils nécessaires
On va travailler en 2D.
Pour cela, rendez-vous dans devkitpro\PAlib\Tools\PAGfx et copiez PAGfx.exe.
Allez dans le dossier de votre jeu et créez-y un répertoire nommé gfx et copiez-y l'exécutable. Dans ce même dossier, créez un fichier nommé PAGfx.ini, il contiendra les instructions pour convertir vos images.
Syntaxe du fichier PAGfx.ini
Ce fichier doit être composé de 3 parties commençant par un dièse (#) :
Couleur transparente (#TranspColor NomDeLaCouleur). La couleur transparente que nous utiliserons toujours est Magenta (#TranspColor Magenta). Sa valeur RGB est 255 ; 0 ; 255.
Sprites (dessins) : #Sprites :. Pour ajouter des sprites, il suffit d'aller à la ligne après cette démarquation et de faire cette syntaxe :
nom_du_sprite.extension 256colors nom_du_futur_fichier_contenant_le_sprite. Nom_du_sprite.extension est votre image. 256colors est le maximum pour des sprites, pour avoir un rendu excellent. Nom_du_futur_fichier_contenant_le_sprite est le fichier que PAGfx.exe créera. Vous pouvez mettre plusieurs sprites dedans.Arrière-plans (#Backgrounds :). Pour ajouter des arrière-plans, il suffit d'aller à la ligne après cette démarquation et de faire cette syntaxe : nom_de_l_arriere_plan.extension type_d_arriere_plan. Nom_de_l_arriere_plan.extension est l'image que vous avez créée. Type_d_arriere_plan déterminera le code source (que nous apprendrons plus tard) :
-EasyBg : arrière-plan aux dimensions de l'écran (256*192) ou plus, peut subir un défilement
-LargeMap : arrière-plan aux grandes dimensions (les plus courantes sont 1024*1024), surtout utilisé pour les DualBackgrounds (arrière-plan sur les deux écrans simultanément o_O )
-TileBg : arrière-plan principalement utilisé pour les polices, composé d'une multitude de "tuiles" (tiles) de 8*8
-RotBg : arrière-plan utilisé pour être tourné dans tous les sens, pour subir des zooms, voire même de la 3D :) .
Exemple de PAGfx.ini :
Citation
#TranspColor Magenta
#Sprites :
hero.PNG 256colors sprites
jouer.PNG 256colors sprites
njeu.PNG 256colors sprites
fleche.PNG 256colors sprites
oui.PNG 256colors sprites
non.PNG 256colors sprites#Backgrounds :
newfont.GIF TileBg
fontscreen1.GIF TileBg
bg_menu.PNG LargeMap
bg1.PNG LargeMap
confirmer.PNG EasyBg
Les arrière-plans
On va s'attaquer aux arrière-plans. :pirate:
Après avoir utilisé PAGfx, il faut coder (on est bien là pour ça :lol: ).
Au code minimal, il faut rajouter, dans la partie des includes :
#include "all_gfx.h"
Ce simple header permet d'inclure les images que vous avez faites auparavant.
Code pour EasyBg
Voici la fonction PA_LoadBackground :
void PA_LoadBackground(u8 ecran,u8 numero_bg,const PA_BgStruct* nom_bg);
#include <PA9.h>
#include "all_gfx.h"
int main (int argc, char **argv)
{
PA_Init();
PA_LoadBackground(0,1,&bg);
while (1)
{
PA_WaitForVBL();
}
return 0;
}
Nous allons nous amuser à faire défiler l'arrière-plan (scroll). Voici le prototype de la fonction PA_EasyBgScrollXY :
void PA_EasyBgScrollXY(u8 ecran,u8 numero_bg,s32 x,s32 y);
Voici le code pour un petit défilement :
#include <PA9.h>
#include "all_gfx.h"
int main (int argc, char **argv)
{
int scrollx=0,scrolly=0;
PA_Init();
PA_LoadBackground(0,1,&bg);
while (1)
{
scrollx++;
scrolly++;
PA_EasyBgScrollXY(0,1,scrollx,scrolly);
PA_WaitForVBL();
}
return 0;
}
Code pour TileBg (seulement pour les polices)
Voici la fonction de PA_LoadText :
void PA_LoadText(u8 ecran, u8 numero_bg, const PA_BgStruct* police);
#include <PA9.h>
#include "all_gfx.h"
int main(int argc, char ** argv)
{
PA_Init();
PA_LoadText(1,0,&police);
PA_OutputText(1,0,0,"Pas mal, la nouvelle police :) ");
while (1)
{
PA_WaitForVBL();
}
return 0;
}
Les sprites
A présent, passons aux sprites.
Il faut toujours inclure :
#include "all_gfx.h"
Code pour sprites standards
Tout d'abord, il faut charger la palette de sprites. On va utiliser cette fonction :
void PA_LoadSpritePal(u8 ecran,u8 numero_palette,(void*)fichier_palette_cree_par_PAGfx);
Mais comment connaître Fichier_palette_cree_par_PAGfx ?
C'est simple, supposons que votre PAGfx.ini comporte :
Citation
#TranspColor Magenta
#Sprites :
mario.PNG 256colors sprites
wario.PNG 256colors sprites#Backgrounds :
Le troisième mot est la palette. Pour le code, il suffit de rajouter _Pal.
Ce qui donne :
#include <PA9.h>
#include "all_gfx.h"
int main(int argc, char ** argv)
{
PA_Init();
PA_LoadSpritePal(0,0,(void*)sprites_Pal);
while(1)
{
PA_WaitForVBL();
}
return 0;
}
Et maintenant, on va créer le sprite avec PA_CreateSprite :
void PA_CreateSprite(u8 ecran,u8 numero_sprite,(void*)nom_sprite_cree_par_PAGfx,u8 forme,u8 taille,u8 mode_couleurs,u8 palette,s16 position_x,s16 position_y);
Pour obtenir Nom_sprite_cree_par_PAGfx, il faut regarder ça dans PAGfx.ini :
Citation
#TranspColor Magenta
#Sprites :
mario.PNG 256colors sprites
wario.PNG 256colors sprites#Backgrounds :
Il s'agit en fait du nom des images sans l'extension.
À ces noms, il faut rajouter _Sprite.
Il y a plusieurs tailles différentes (variables forme et taille), voici la liste complète :
OBJ_SIZE_8X8 : pour un sprite de 8*8 :)
OBJ_SIZE_16X16
OBJ_SIZE_32X32
OBJ_SIZE_64X64
OBJ_SIZE_16X8
OBJ_SIZE_32X8
OBJ_SIZE_32X16
OBJ_SIZE_64X32
OBJ_SIZE_8X16
OBJ_SIZE_8X32
OBJ_SIZE_16X32
OBJ_SIZE_32X64
Et si mon image faisait 20*10 ? Il n'existe pas de constante adaptée...
Il suffit de prendre la constante qui est juste au-dessus de la valeur (hauteur ou largeur) la plus grande. Ici, 20 est la plus grande valeur, donc il faudra créer un sprite de 32*16, et les pixels qui ne seront pas utilisés (après 20 dans les x et après 10 dans les y) devront être de la couleur transparente définie dans PAGfx.ini.
Gnein ? o_O
Bon, je vais faire une image :

C'est un rectangle de 20*10, qui doit être mis en 32*16 ou plus.
Et si une des valeurs de mon image dépassait 64 ?
Vous avez trois solutions :
Réduire l'image en format 64*64 (en conservant le ratio largeur/hauteur, mais la qualité diminuera).
Le transformer en arrière-plan (plus rare, mais possible).
Mettre plusieurs sprites côte à côte contenant chacun un morceau de l'image et la reconstituant une fois regroupés (pas très conseillé car il n'y a que 128 sprites disponibles par écran).
Ensuite, mode_couleur peut prendre deux valeurs : soit 0 ; soit 1.
Mais comment on fait pour savoir s'il faut mettre 1 ou 0 ?
Il faut regarder :
Citation
#TranspColor Magenta
#Sprites :
mario.PNG 256colors sprites
wario.PNG 256colors sprites#Backgrounds :
Il faut mettre 1 pour 256 couleurs, et 0 pour 16 couleurs.
Palette est le numéro de la palette (définie plus haut).
Voici un code récapitulatif (avec notre rectangle :p ) :
#include <PA9.h>
#include "all_gfx.h"
int main(int argc, char ** argv)
{
PA_Init();
PA_LoadSpritePal(0,0,(void*)sprites_Pal);
PA_CreateSprite(0,0,(void*)rectangle_Sprite,OBJ_SIZE_32X16,1,0,0,0);
/* On crée un sprite sur l'écran tactile (0), son numéro est 0, c'est un sprite semblable à rectangle.png
Vu qu'il fait 20*10, il faut le mettre en 32*16, 256 couleurs donc 1, utilise la palette 0, sa position dans
les X est 0, comme dans les y. */
while(1)
{
PA_WaitForVBL();
}
return 0;
}
On peut changer les coordonnées d'un sprite avec PA_SetSpriteXY :
void PA_SetSpriteXY(ecran,numero_sprite,position_x,position_y);
Voici un petit jeu :
#include <PA9.h>
#include "gfx/all_gfx.h"
int main(int argc, char ** argv)
{
int x=100,y=100;
PA_Init();
PA_LoadSpritePal(0,0,(void*)sprites_Pal);
PA_CreateSprite(0,0,(void*)rectangle_Sprite,OBJ_SIZE_32X16,1,0,0,0);
while(1)
{
PA_SetSpriteXY(0,0,x,y);
PA_WaitForVBL();
}
return 0;
}
Si vous devez gérer une IA, et que chaque élément contrôlé par ordinateur (ou par vous) est le même, vous pouvez utiliser la macro PA_CloneSprite(ecran,numero_du_sprite_a_creer,numero_du_sprite_qui_va_etre_cloné)
pour cloner le sprite que vous voulez. Ainsi, si vous faites PA_CloneSprite(0,1,0);
, vous créerez, dans l'écran 0, un clone du sprite 0 ayant pour numéro 1.
Vous pouvez aussi libérer la mémoire en détruisant des sprites qui ne servent plus à rien avec :
void PA_DeleteSprite(u8 ecran,u8 numero_sprite);
C'était un gros chapitre, qui pose les bases de la 2D, le suivant nous permettra de découvrir d'autres fonctions et utilités de la 2D ! :)
La 2D avancée
Nous allons voir comment créer des sprites qui vont sur les deux écrans (DualSprites), des sprites animés, des arrière-plans sur les deux écrans, ainsi que des rotations et des zooms.
Les arrière-plans
Je savais que vous vous rueriez sur cette partie pour faire de jolis arrière-plans sur les deux écrans simultanément :p .
De plus en plus de monde en raffole :) !
Sachez que nous utiliserons toujours des LargeBg pour faire des arrière-plans sur les deux écrans...
On va donc regarder la fonction PA_DualLoadBackground :
void PA_DualLoadBackground(u8 numero_bg, const PA_BgStruct* bg);
Voici un code complet :
#include <PA9.h>
#include "all_gfx.h"
int main (int argc, char **argv)
{
PA_Init();
PA_DualLoadBackground(1,&bg);
while (1)
{
PA_WaitForVBL();
}
return 0;
}
Pour le faire défiler, c'est simple, on utilisera cette fonction :
void PA_DualEasyBgScrollXY(u8 bg_select, s32 x, s32 y);
On en tirera donc ce code :
#include <PA9.h>
#include "all_gfx.h"
int main (int argc, char **argv)
{
int scrollx=0,scrolly=0;
PA_Init();
PA_DualLoadBackground(1,bg);
while (1)
{
scrollx++;
scrolly++;
PA_DualEasyBgScrollXY(1,scrollx,scrolly);
PA_WaitForVBL();
}
return 0;
}
Vous pouvez aussi modifier le nombre de pixels de décalage (dans les y) entre les deux écrans avec :
void PA_SetScreenSpace(s16 nombre_de_pixels);
Si vous voulez inverser les deux écrans, vous pouvez utiliser :
void PA_SwitchScreens(void);
Les sprites
Les DualSprites
Les sprites utilisables sur les deux écrans s'appellent les DualSprites.
Pour les DualSprites, vous n'avez pas besoin d'utiliser une autre syntaxe que les sprites en ce qui concerne PAGfx.ini.
D'abord, il faut charger la palette, mais à la différence des sprites normaux, on va utiliser PA_DualLoadSpritePal :
void PA_DualLoadSpritePal(u8 numero_palette,(void*)palette_cree_par_PAGfx);
Pour charger un DualSprite, nous utiliserons PA_DualCreateSprite :
void PA_DualCreateSprite(u8 numero_sprite,(void*)sprite_cree_par_PAGfx,u8 forme,u8 taille,u8 mode_couleurs,u8 numero_palette,u8 position_x,u8 position_y);
Voici un code complet :
#include <PA9.h>
#include "all_gfx.h"
int main(int argc, char ** argv)
{
PA_Init();
PA_DualLoadSpritePal(0,(void*)sprites_Pal);
PA_DualCreateSprite(0,(void*)rectangle_Sprite,OBJ_SIZE_32X32,1,0,0,0);
while(1)
{
PA_WaitForVBL();
}
return 0;
}
Pour déplacer des DualSprites, on va utiliser PA_DualSetSpriteXY :
void PA_DualSetSpriteXY(u8 numero_sprite,s16 position_x,s16 position_y);
Et un nouveau code ;) :
#include <PA9.h>
#include "all_gfx.h"
int main(int argc, char ** argv)
{
int x=100,y=100;
PA_Init();
PA_DualLoadSpritePal(0,(void*)sprites_Pal);
PA_DualCreateSprite(0,(void*)rectangle_Sprite,OBJ_SIZE_32X32,1,0,0,0);
while(1)
{
y++;
PA_DualSetSpriteXY(0,x,y);
PA_WaitForVBL();
}
return 0;
}
Vous pouvez cloner des DualSprites avec :
void PA_DualCloneSprite(u8 numero_sprite_a_creer,u8 numero_sprite_cloné);
Ou encore les supprimer :
void PA_DualDeleteSprite(u8 numero_sprite);
Les animations de Sprites
Animations de sprites standards
Vous aurez sûrement besoin d'animer les sprites. Par exemple, si vous avez un personnage à faire marcher, il doit bouger :) !
Bon, pour cette partie, on va prendre notre joli rectangle ;) . L'animation sera la coloration de celui-ci.
Mais comment on va s'y prendre ?
On va créer une planche de sprites. En fait, on va multiplier la hauteur l'image contenant notre sprite par le nombre de frames (état d'une animation). Si on veut faire 3 frames (rectangle jaune, rectangle rouge et rectangle noir), il faudra avoir créé un sprite de 32*96 (puisque 32*3=96).
Voici notre planche de sprites (contenant 3 frames) :

le rectangle jaune (0;0 -> 32;32) est le frame 0 ;
le rectangle rouge (0;32 -> 32;64) est le frame 1 ;
le rectangle noir (0;64 -> 32;96) est le frame 2.
Après avoir chargé la palette et le sprite, on va l'animer avec PA_StartSpriteAnim :
void PA_StartSpriteAnim(u8 ecran,u8 numero_sprite,s16 frame_de_depart,s16 frame_d_arrivee,s16 fps);
Fps est le nombre de frames par seconde.
Voici un code complet :
#include <PA9.h>
#include "all_gfx.h"
int main(int argc, char ** argv)
{
PA_Init();
PA_LoadSpritePal(0,0,(void*)sprites_Pal);
PA_CreateSprite(0,0,(void*)rectangle_Sprite,OBJ_SIZE_32X32,1,0,0,0);
PA_StartSpriteAnim(0,0,0,2,1);
while(1)
{
PA_WaitForVBL();
}
return 0;
}
Pour arrêter l'animation d'un sprite, on peut utiliser :
void PA_StopSpriteAnim(u8 ecran, u8 numero_sprite);
Pour choisir le type d'animation, on peut utiliser :
void PA_StartSpriteAnimEx(u8 ecran, u8 numero_sprite, s16 frame_de_depart, s16 frame_d_arrivee, s16 fps, u8 type, s16 nombre_de_cycles);
Les arguments sont les mêmes que PA_StartSpriteAnim
, sauf que l'on rajoute type et nombre_de_cycles. La variable type est le type d'animation. Elle peut prendre pour valeur ANIM_UPDOWN (joue l'animation dans un sens puis dans l'autre) ou ANIM_LOOP (animation normale). La variable nombre_de_cycles correspond au nombre d'exécutions de l'animation. Elle peut prendre pour valeur -1 (infini) ou tout autre nombre positif (n boucles). Il y a une valeur spéciale, qui donne une valeur aux deux dernièrs paramètres (au lieu de 7 ; il n'y en a plus que 6 à fournir) : ANIM_ONESHOT (une seule fois), ce qui correspond à ANIM_LOOP pour type et à 1 pour nombre_de_cycles.
Pour choisir l'image de l'animation à afficher, on utilise :
void PA_SetSpriteAnimFrame(u8 ecran, u8 numero_sprite, s16 frame);
Où frame est le numéro de frame que l'on veut appliquer au sprite.
Pour savoir le numéro de frame courant d'un sprite, on utilise :
u16 PA_GetSpriteAnimFrame(u8 ecran, u8 numero_sprite);
La valeur retournée est un u16 !
Pour (re)définir la rapidité de l'animation d'un sprite, on peut utiliser :
void PA_SetSpriteAnimSpeed(u8 ecran, u8 numero_sprite, s16 fps);
Pour obtenir le nombre de FPS de l'animation d'un sprite, on utilise :
u16 PA_GetSpriteAnimSpeed(u8 ecran, u8 numero_sprite)
La valeur retournée est un u16 !
Pour définir le nombre d'exécutions de l'animation d'un sprite, on utilise :
void PA_SetSpriteNCycles(u8 ecran, u8 numero_sprite, s16 numero_de_cycles);
Pour obtenir le nombre d'exécutions restantes de l'animation d'un sprite, on peut utiliser :
u16 PA_GetSpriteNCycles(u8 ecran, u8 numero_sprite);
La valeur retournée est un u16 !
Pour mettre en pause ou reprendre l'animation d'un sprite, on utilise :
void PA_SpriteAnimPause(u8 ecran, u8 numero_sprite, u8 pause);
La variable pause a une fonction de booléen, donc il faut mettre 1 pour mettre en pause ou 0 pour reprendre l'animation.
Animations de DualSprites
Là aussi, il faut déjà avoir créé la DualPalette et le DualSprite. Ensuite, pour animer, c'est simple avec PA_DualStartSpriteAnim (non, ce n'est pas de la propagande :p ) :
void PA_DualStartSpriteAnim(u8 numero_sprite,s16 frame_de_depart,s16 frame_d_arrivee,s16 fps);
Et voici notre dernier code complet :
#include <PA9.h>
#include "all_gfx.h"
int main(int argc, char ** argv)
{
PA_Init();
PA_DualLoadSpritePal(0,(void*)sprites_Pal);
PA_DualCreateSprite(0,(void*)rectangle_Sprite,OBJ_SIZE_32X32,1,0,0,0);
PA_DualStartSpriteAnim(0,0,2,1);
while(1)
{
PA_WaitForVBL();
}
return 0;
}
Toutes les fonctions énoncées pour les animations de sprites standards sont les mêmes avec les dualsprites sauf que l'on met Dual_ devant et que l'on enlève le paramètre ecran.
Faire des rotations et des zooms
Voilà : on a appris comment créer un sprite, charger sa palette, etc. mais on va s'attaquer aux techniques avancées des sprites : les rotations et les zooms :pirate: .
Les rotations
Les arrière-plans
Pour les arrière-plans, il faut mettre le mode vidéo de la Nintendo DS en 2 sur l'écran tactile avec PA_SetVideoMode(0,2);
Ensuite, au lieu de charger normalement l'arrière-plan, on va utiliser la macro :
void PA_LoadPAGfxRotBg(ecran,numero_bg,nom_bg,taille)
On connaît déjà à quoi correspondent les 3 premiers arguments, mais pas le dernier : taille. Il est défini par une autre macro. On distingue :
BG_ROT_128X128 ;
BG_ROT_256X256 ;
BG_ROT_512X512 ;
BG_ROT_1024X1024.
Il n'en existe pas d'autre ! Il faut donc que votre arrière-plan soit à une de ces dimensions : 128*128 ; 256*256 ; 512*512 ou 1024*1024 pixels.
Pour effectuer notre rotation (la fonction fait zoom aussi :-° ), nous allons utiliser :
void PA_SetBgRot(u8 ecran, u8 numero_bg, s32 defilement_x, s32 defilement_y, s32 defilement_centre_x, s32 defilement_centre_y, s16 angle, s32 zoom);
La mesure d'un angle est comprise entre 0 et 511 inclus. Pour convertir des degrés en unités d'angle de palib, il faut les multiplier par 511 puis à les diviser par 360, c'est juste une question de proportionnalité !
Pourquoi avoir comme arguments defilement_x/defilement_centre_x et defilement_y/defilement_centre_y ? Les arguments defilement_x et defilement_y suffisent, non ?
Je vais vous expliquer avec des images. Supposons que nous ayons cette image comme arrière_plan :
Citation : Légende
Rouge = defilement_x
Bleu = defilement_y
Vert = defilement_centre_x
Jaune = defilement_centre_y

Ensuite, voilà à quoi ça ressemble avec une rotation quelconque (par exemple 44°) :

Voilà, vous venez de vous rendre compte : malgré la rotation, defilement_centre_x et defilement_centre_y restent dans les mêmes axes, alors que defilement_x et defilement_y subissent la rotation de l'image...
Voici un exemple, avec la première image par exemple que vous pouvez renommer "bg" par exemple :
#include <PA9.h>
#include "gfx/all_gfx.c"
#include "gfx/all_gfx.h"
int main(int argc, char ** argv)
{
PA_Init();
PA_InitVBL();
PA_SetVideoMode(0, 2);
PA_LoadPAGfxRotBg(0,3,bg,1);
PA_InitText(1, 0);
s32 scrollx = 0;
s32 scrolly = 0;
s32 rotcenterx = 0;
s32 rotcentery = 0;
s16 angle = 0;
PA_OutputSimpleText(1, 2, 3, "ScrollX : Gauche/Droite");
PA_OutputSimpleText(1, 2, 4, "Scrolly : Haut/Bas");
PA_OutputSimpleText(1, 2, 5, "RotCenterX : A/Y");
PA_OutputSimpleText(1, 2, 6, "RotCenterY : B/X");
PA_OutputSimpleText(1, 2, 7, "Angle : R/L");
while (1)
{
if(Pad.Held.Right)
scrollx++;
else if(Pad.Held.Left)
scrollx--;
else if(Pad.Held.Down)
scrolly++;
else if(Pad.Held.Up)
scrolly--;
else if(Pad.Held.A)
rotcenterx++;
else if(Pad.Held.Y)
rotcenterx--;
else if(Pad.Held.B)
rotcentery++;
else if(Pad.Held.X)
rotcentery--;
else if(Pad.Held.R)
angle++;
else if(Pad.Held.L)
angle--;
PA_SetBgRot(0, 3, scrollx, scrolly, rotcenterx, rotcentery, angle,256);
PA_WaitForVBL();
}
return 0;
}
Les sprites
Après avoir créé votre sprite, il faut dire que l'on veut faire tourner un sprite.
On utilise :
void PA_SetSpriteRotEnable(ecran,sprite,rotset);
ecran : le numéro de l'écran ;
sprite : le numéro du sprite ;
rotset : le numéro du rotset. Il doit être compris entre 0 et 31 inclus. Les sprites ayant le même numéro de rotset subiront la même rotation.
Ensuite pour effectuer la rotation on utilise :
void PA_SetRotsetNoZoom(u8 ecran, u8 rotset, s16 angle);
angle : souvenez-vous que c'est la mesure d'un angle compris entre 0 et 511 inclus et que pour convertir des degrés en unités d'angle de palib, il faut les multiplier par 511 puis à les diviser par 360, c'est juste une question de proportionnalité ^^ .
Pour dire que l'on ne veut plus faire tourner un sprite, on utilise :
void PA_SetSpriteRotDisable(ecran, sprite);
Les zooms
Les arrière-plans
On a vu que la fonction qui fait des rotations d'arrière-plans fait aussi leur zoom.
L'indice de zoom peut être de 256 : pas de zoom (zoom x1), de 512 : 2x plus petit (zoom x0.5), de 128 : 2x plus grand (zoom x2), etc.
#include <PA9.h>
#include "gfx/all_gfx.c"
#include "gfx/all_gfx.h"
int main(int argc, char ** argv)
{
PA_Init();
PA_InitVBL();
PA_SetVideoMode(0, 2);
PA_LoadPAGfxRotBg(0,3,Rot,BG_ROT_256X256);
PA_InitText(1, 0);
s32 zoom=256;
PA_OutputSimpleText(1, 2, 3, "Zoom : Haut/Bas");
while (1)
{
if(Pad.Held.Up)
zoom--;
else if(Pad.Held.Down)
zoom++;
PA_SetBgRot(0, 3,0,0,0,0, 0,zoom);
PA_WaitForVBL();
}
return 0;
}
Les sprites
Pour effectuer la rotation on utilise :
void PA_SetRotsetNoAngle(u8 ecran, u8 rotset, u16 zoomx, u16 zoomy);
Si vous voulez faire des rotations et des zooms en même temps, il faut dire que l'on veut faire des rotations comme décrit dans la partie sur les rotations et utiliser cette fonction raccourci :
void PA_SetRotset(u8 ecran, u8 rotset, s16 angle, u16 zoomx, u16 zoomy);
Exercice : sortez l'Aspirine !
On a accumulé beaucoup de connaissances en deux chapitres seulement ! Il est temps de les tester un faisant un petit jeu : le jeu de l'Aspirine...
Présentation du projet
On va créer un jeu (très bête :lol: ), dans lequel le joueur va diriger une pastille effervescente (pour guérir vos maux de tête engendrés par cet exo :) ) uniquement pour aller à gauche ou à droite.
Comme une vraie, elle devra descendre du haut de l'écran tactile et tomber au fond du verre (comme si on la lâchait dans un verre d'eau). Cette pastille dégagera des bulles de différentes tailles qui se dirigeront vers le haut (il y en a 255). Une fois toutes les bulles parties, la pastille remontera.
Notions à utiliser et préparatifs
Tout d'abord, on va apprendre à créer des nombres aléatoires. Il faut initialiser le système avec PA_InitRand(); sinon les nombres que vous aurez seront les même ! En fait, PA_InitRand() se base sur l'heure de votre DS, il sera donc impossible de retrouver la même série de nombres.
Ensuite, il existe trois fonctions pour trouver des nombres aléatoires (si vous avez fait le TP du Plus ou Moins, vous pouvez passer au paragraphe ci-dessous) :
PA_Rand(); mais cette fonction renvoie un nombre exorbitant :p ;
PA_RandMax(max); sert à tirer un nombre entre 0 et max inclus ;
PA_RandMinMax(min,max); sert à tirer un nombre entre min et max inclus.
Ensuite, voici le dossier gfx : gfx.zip (100707) ; gfx.zip (080823).
N'oubliez pas d'inclure all_gfx, de charger la palette, de créer les sprites...
Je vous conseille de créer deux structures : la première pour la pastille et la deuxième pour les bulles.
Créez un tableau pour les bulles.
Définissez (à l'aide de defines) la largeur de l'écran (256), sa hauteur (192), la largeur et la hauteur de la pastille (regardez dans le fichier png), le diamètre maximal des bulles (6 ; elles sont toutes de largeur égale à la hauteur) et le nombre maximum de bulles.
Correction
Voici ma correction :
//INCLUDES
#include <PA9.h>
//IMAGES
#include "all_gfx.h"
//DEFINES
#define LARGEUR_ECRAN 256
#define HAUTEUR_ECRAN 192
#define MAX_BULLES 256
#define LARGEUR_PASTILLE 32
#define HAUTEUR_PASTILLE 9
#define DIAMETRE_BULLE 6
typedef struct
{
int x,y;
/*X : position dans les x de l'aspirine
Y : position dans les y de l'aspirine*/
}pastille;
typedef struct
{
int x,y,cree,tps_creation;
/*X : position de la bulle dans les x
Y : position de la bulle dans les y
cree : sert à savoir si la bulle est créée et encore dans l'écran
tps_creation : sert à savoir si une bulle n'est simplement pas créée ou si elle est sortie de l'écran*/
}bulle;
int main(int argc, char ** argv)
{
int i , actionx=0, gravite=2, bulle_courante, nb_out=0, min=0, max=MAX_BULLES-1;
/* i est le compteur général
actionx sert à déterminer la direction de la pastille
bulle_courante servira à créer les bulles
nb_out compte le nombre de bulles sorties de l'écran
min est le numéro de la première bulle pas créée
max est le numéro de la dernière bulle pas créée*/
pastille aspirine;
bulle bubbles[MAX_BULLES];
aspirine.x=(LARGEUR_ECRAN-LARGEUR_PASTILLE)/2; //ON LA CENTRE
aspirine.y=0;
for(i = 0; i <MAX_BULLES ; i++)
{
bubbles[i].x=0;
bubbles[i].y=0;
bubbles[i].cree=0;
bubbles[i].tps_creation=0;
}
PA_Init();
PA_InitVBL();
PA_InitRand();
PA_LoadSpritePal(0,0,(void*)sprites_Pal);
PA_CreateSprite(0,0,(void*)cachet_Sprite,OBJ_SIZE_32X32,1,0,aspirine.x,aspirine.y);
for(i = 0; i < MAX_BULLES - 1; i++)
{
PA_CreateSprite(0,i+1,(void*)bulle_Sprite,OBJ_SIZE_8X8,1,0,-8,-8);
PA_StartSpriteAnim(0,i+1,PA_RandMax(2),0,0); //Bulle plus ou moins grosse
}
while (1)
{
aspirine.x+=Pad.Held.Right-Pad.Held.Left; //gestion des touches fléchées gauche et droite
aspirine.y+=gravite; //gestion de la gravité
if(nb_out==MAX_BULLES-1)
gravite=-4; //si toutes les bulles sont parties
nb_out=0;
bulle_courante=PA_RandMinMax(min,max);
if(!bubbles[bulle_courante].cree&&!bubbles[bulle_courante].tps_creation) //on crée une bulle si elle n'est pas créée
{
bubbles[bulle_courante].x=PA_RandMinMax(aspirine.x,aspirine.x+LARGEUR_PASTILLE-DIAMETRE_BULLE);
bubbles[bulle_courante].y=aspirine.y;
bubbles[bulle_courante].cree=1;
}
for(i=0;i<MAX_BULLES-1;i++)
{
if(bubbles[i].cree)
{
actionx=PA_RandMax(1);
if(!actionx)
bubbles[i].x--;
else
bubbles[i].x++;
if(bubbles[i].x<0)
bubbles[i].x=0; //on ne dépasse pas de l'écran
else if(bubbles[i].x+DIAMETRE_BULLE>LARGEUR_ECRAN)
bubbles[i].x=LARGEUR_ECRAN-DIAMETRE_BULLE; //idem
bubbles[i].y--; //les bulles remontent
if(bubbles[i].y+DIAMETRE_BULLE<0)
bubbles[i].cree=0; //si elle est sortie on la considère comme morte
bubbles[i].tps_creation++; //son ancienneté augmente
PA_SetSpriteXY(0,i+1,bubbles[i].x,bubbles[i].y); //on place la bulle
}
else
nb_out++;//on augmente le nombre de bulles parties
}
for(i=0;i<MAX_BULLES-1;i++) //on restreint le minimum à la première bulle non créée
{
if(!bubbles[i].cree && !bubbles[i].tps_creation)
{
min=i;
break;
}
}
for(i=MAX_BULLES-1;i>=0;i--) //on restreint le maximum à la dernière bulle non créée
{
if(!bubbles[i].cree && !bubbles[i].tps_creation)
{
max=i;
break;
}
}
if(aspirine.x<0)
aspirine.x=0;//on ne dépasse pas de l'écran
else if(aspirine.x+LARGEUR_PASTILLE>LARGEUR_ECRAN)
aspirine.x=LARGEUR_ECRAN-LARGEUR_PASTILLE;//on ne dépasse pas de l'écran
if(aspirine.y+HAUTEUR_PASTILLE<0) //Si la pastille est remontée, on arrête le jeu
{
PA_DeleteSprite(0,0);
break;
}
else if(aspirine.y+HAUTEUR_PASTILLE>HAUTEUR_ECRAN)
aspirine.y=HAUTEUR_ECRAN-HAUTEUR_PASTILLE; //on ne passe pas sous l'écran
PA_SetSpriteXY(0,0,aspirine.x,aspirine.y); //on place la pastille
PA_WaitForVBL();
}
return 0;
}
Amélioration (exercice)
Pour notre première amélioration, on va mettre l'animation sur les deux écrans simultanément (pensez aux DualSprites !). N'oubliez pas d'enlever les indications d'écran pour les DualSprites !
Correction de l'amélioration
Voilà :
//INCLUDES
#include <PA9.h>
//IMAGES
#include "all_gfx.h"
//DEFINES
#define LARGEUR_ECRAN 256
#define HAUTEUR_ECRAN 192
#define MAX_BULLES 128
#define LARGEUR_PASTILLE 32
#define HAUTEUR_PASTILLE 9
#define DIAMETRE_BULLE 6
typedef struct
{
int x,y;
/*X : position dans les x de l'aspirine
Y : position dans les y de l'aspirine*/
}pastille;
typedef struct
{
int x,y,cree,tps_creation;
/*X : position de la bulle dans les x
Y : position de la bulle dans les y
cree : sert à savoir si la bulle est créée et encore dans l'écran
tps_creation : sert à savoir si une bulle n'est simplement pas créée ou si elle est sortie de l'écran*/
}bulle;
int main(int argc, char ** argv)
{
int i , actionx=0, gravite=2, bulle_courante, nb_out=0, min=0, max=MAX_BULLES-1;
/* i est le compteur général
actionx sert à déterminer la direction de la pastille
bulle_courante servira à créer les bulles
nb_out compte le nombre de bulles sorties de l'écran
min est le numéro de la première bulle pas créée
max est le numéro de la dernière bulle pas créée*/
pastille aspirine;
bulle bubbles[MAX_BULLES];
aspirine.x=(LARGEUR_ECRAN-LARGEUR_PASTILLE)/2; //ON LA CENTRE
aspirine.y=0;
for(i=0;i<MAX_BULLES;i++)
{
bubbles[i].x=0;
bubbles[i].y=0;
bubbles[i].cree=0;
bubbles[i].tps_creation=0;
}
PA_Init();
PA_InitVBL();
PA_InitRand();
PA_DualLoadSpritePal(0,(void*)sprites_Pal);
PA_DualCreateSprite(0,(void*)cachet_Sprite,OBJ_SIZE_32X32,1,0,aspirine.x,aspirine.y);
for(i=0;i<MAX_BULLES-1;i++)
{
PA_DualCreateSprite(i+1,(void*)bulle_Sprite,OBJ_SIZE_8X8,1,0,-8,-8);
PA_DualStartSpriteAnim(i+1,PA_RandMax(2),0,0); //Bulle plus ou moins grosse
}
PA_SetScreenSpace(0);
while (1)
{
aspirine.x+=Pad.Held.Right-Pad.Held.Left; //gestion des touches fléchées gauche et droite
aspirine.y+=gravite; //gestion de la gravité
if(nb_out==MAX_BULLES-1)
gravite=-4; //si toutes les bulles sont parties
nb_out=0;
bulle_courante=PA_RandMinMax(min,max);
if(!bubbles[bulle_courante].cree&&!bubbles[bulle_courante].tps_creation) //on crée une bulle si elle n'est pas créée
{
bubbles[bulle_courante].x=PA_RandMinMax(aspirine.x,aspirine.x+LARGEUR_PASTILLE-DIAMETRE_BULLE);
bubbles[bulle_courante].y=aspirine.y;
bubbles[bulle_courante].cree=1;
}
for(i=0;i<MAX_BULLES-1;i++)
{
if(bubbles[i].cree)
{
actionx=PA_RandMax(1);
if(!actionx)
bubbles[i].x--;
else
bubbles[i].x++;
if(bubbles[i].x<0)
bubbles[i].x=0; //on ne dépasse pas de l'écran
else if(bubbles[i].x+DIAMETRE_BULLE>LARGEUR_ECRAN)
bubbles[i].x=LARGEUR_ECRAN-DIAMETRE_BULLE; //idem
bubbles[i].y--; //les bulles remontent
if(bubbles[i].y+DIAMETRE_BULLE<0)
bubbles[i].cree=0; //si elle est sortie on la considère comme morte
bubbles[i].tps_creation++; //son ancienneté augmente
PA_DualSetSpriteXY(i+1,bubbles[i].x,bubbles[i].y); //on place la bulle
}
else
nb_out++;//on augmente le nombre de bulles parties
}
for(i=0;i<MAX_BULLES-1;i++) //on restreint le minimum à la première bulle non créée
{
if(!bubbles[i].cree && !bubbles[i].tps_creation)
{
min=i;
break;
}
}
for(i=MAX_BULLES-1;i>=0;i--) //on restreint le maximum à la drnière bulle non créée
{
if(!bubbles[i].cree && !bubbles[i].tps_creation)
{
max=i;
break;
}
}
if(aspirine.x<0)
aspirine.x=0;//on ne dépasse pas de l'écran
else if(aspirine.x+LARGEUR_PASTILLE>LARGEUR_ECRAN)
aspirine.x=LARGEUR_ECRAN-LARGEUR_PASTILLE;//on ne dépasse pas de l'écran
if(aspirine.y+HAUTEUR_PASTILLE<0) //Si la pastille est remontée, on arrête le jeu
{
PA_DualDeleteSprite(0);
break;
}
else if(aspirine.y+HAUTEUR_PASTILLE>HAUTEUR_ECRAN*2)
aspirine.y=HAUTEUR_ECRAN*2-HAUTEUR_PASTILLE; //on ne passe pas sous l'écran
PA_DualSetSpriteXY(0,aspirine.x,aspirine.y); //on place la pastille
PA_WaitForVBL();
}
return 0;
}
Voici une vidéo :
Vidéo du TP.
Voilà, vous maîtrisez les techniques avancées de la 2D :) !
Le dessin
Vous cherchez à dessiner sur un écran de votre Nintendo DS ? Alors ce chapitre est fait pour vous ;) .On va apprendre à dessiner sur l'écran en le mettant en mode 16 bit. Certes c'est peu, mais ça peut être bien ^^ .
Le mode 16 bit
Pourquoi avoir choisi le 16 bit ?
Le 16 bit offre une grande gamme de couleurs, et avec ce mode (ainsi qu'avec le 8 bit, mais avec ce-dernier nous serions obligés de sélectionner 256 couleurs), on peut ainsi dessiner tout ce que l'on veut sur l'écran : allant du simple pixel à des disques (ou mieux :-° ).
Comme pour chaque partie de PALib, il faut initialiser l'écran avec void PA_Init16bitBg(u8 ecran,u8 arriere_plan)
.
Pour afficher un pixel à l'écran, nous utiliserons void PA_Put16bitPixel (u8 ecran,s16 x,s16 y,u16 couleur)
où couleur est la valeur donnée par la macro PA_RGB8(r,g,b)
.
Pour tracer une ligne en mode 16 bit, on utilisera void PA_Draw16bitLine (u8 ecran,u16 x1,u16 y1,u16 x2,u16 y2,u16 couleur)
.
Pour dessiner un rectangle en mode 16 bit, il faut utiliser void PA_Draw16bitRect (u8 ecran,s16 debutX,s16 debutY,s16 finX,s16 finY,u16 color)
.
Si vous voulez effacer l'écran, on utilisera la macro PA_Clear16bitBg(ecran)
.
Exercices
La base était facile, nous allons donc nous attaquer à la pratique ;) !
Coloration du passage du stylet
Nous allons coder un programme qui colore le passage du stylet sur l'écran !
Si vous ne vous souvenez plus du stylet (ce dont je doute ;) ), vous pouvez à tout moment retourner au chapitre sur les événements...
Petite fantaisie : le dessin sera sur les deux écrans !
Mais comment on va faire ? Les deux écrans ne sont pas tactiles ! o_O
Le joueur va dessiner sur l'écran tactile, et le dessin sera reproduit sur l'écran supérieur.
Correction
#include <PA9.h>
int main(int argc, char ** argv)
{
PA_Init();
PA_InitVBL();
PA_Init16bitBg(0,0);
PA_Init16bitBg(1,0);
while(1)
{
PA_WaitFor(Stylus.Newpress||Stylus.Held); //On attend un nouvel appui du stylet ou un maintient de celui-ci sur l'écran tactile
PA_Put16bitPixel(0,Stylus.X,Stylus.Y,PA_RGB8(255,0,0)); //On dessine le pixel sur l'écran tactile
PA_Put16bitPixel(1,Stylus.X,Stylus.Y,PA_RGB8(0,255,255)); //On dessine le pixel sur l'écran supérieur
PA_WaitForVBL();
}
return 0;
}
Vous avez vu que le tracé est saccadé, il serait donc mieux d'utiliser des lignes.
Il faut utiliser un compteur, attendre un événement du stylet et bien sûr tracer des lignes ^^ .
Correction
#include <PA9.h>
int main(int argc, char ** argv)
{
int i=0;
s16 x1=0,y1=0,styletX=0,styletY=0;
PA_Init();
PA_InitVBL();
PA_Init16bitBg(0,0);
PA_Init16bitBg(1,0);
while(1)
{
PA_WaitFor(Stylus.Newpress||Stylus.Held);
styletX=Stylus.X;
styletY=Stylus.Y;
if(i==1)
{
x1=styletX;
y1=styletY;
}
else
{
PA_Draw16bitLine(0,x1,y1,styletX,styletY,PA_RGB8(255,0,0));
PA_Draw16bitLine(1,x1,y1,styletX,styletY,PA_RGB8(0,255,255));
x1=styletX;
y1=styletY;
}
i++;
if(i>2)
i=2; // Permet d'éviter que i prenne une valeur trop importante
PA_WaitForVBL();
}
return 0;
}
Bon, je suis sûr que vous allez me détester, mais il existe une fonction toute faite et meilleure que la nôtre pour le faire : void PA_16bitDraw (u8 ecran,u16 couleur)
. Ce n'est pas grave, c'est le besoin d'accomplissement personnel de la pyramide de Maslow qui vient de se combler :p .
Dessiner des cercles
De cette définition (ainsi que de la représentation graphique qui en découle), on peut se dire qu'on ne travaillera que sur un quart, et qu'on reportera les pixels sur les trois autres par miroir. Je travaille toujours sur le quart en haut à gauche, mais vous pouvez en choisir un autre ;) .
Vous le savez peut-être déjà, dans un repère orthonormé (ce qui est le cas des écrans de votre Nintendo DS), l'équation du cercle de centre O(a;b) et de rayon r est (x-a)^2+(y-b)^2=r^2.
On va exprimer x en fonction de y (on peut faire l'inverse aussi ^^ ).
Donc nous avons :
(x-a)^2+(y-b)^2=r^2(x-a)^2=r^2-(y-b)^2(x-a)^2=(r-y+b)(r+y-b)
Donc x-a=sqrt{(r-y+b)(r+y-b)}
et x-a=-sqrt{(r-y+b)(r+y-b)}.
On peut donc dire que x=a+sqrt{(r-y+b)(r+y-b)}
et x=a-sqrt{(r-y+b)(r+y-b)}.
Mais vu que l'on ne travaillera que sur le côté gauche, nous ne retiendrons que x=a-sqrt{(r-y+b)(r+y-b)}.
Ne vous arrêtez pas à la racine carrée, je sais que la racine carrée est une opération lourde, mais la technique employée est légère. En effet, pour un cercle de 100 pixels de diamètre, il n'y a que 50 tours de boucles contenant chacune 2 racines carrées, soit 100 racines carrées. De plus, j'ai testé le fps, il reste à 60 donc on ne peut pas dire que ça prenne beaucoup de CPU...
Comme on l'a fait dans l'exercice de la coloration du passage du stylet, il faudra utiliser les lignes et non les points.
Voici ce que le programme doit faire : par défaut, le centre du cercle doit être au centre de l'écran, et son rayon doit être de 100 px. En appuyant sur L, le rayon doit être augmenté de 1 px ; sur R, le rayon doit être diminué de 1 px ; et sur les touches multidirectionnelles, le centre doit se déplacer dans la direction voulue de 1 px. Bien sûr, à chaque fois que vous appuyez sur ces touches, l'écran doit être effacé. Le cercle ne doit pas dépasser de l'écran, sinon vous aurez une belle surprise :p ...
Je vous laisse coder :) .
Correction
#include <PA9.h>
#include <math.h>
#define LARGEUR_ECRAN 255
#define HAUTEUR_ECRAN 191
typedef struct
{
int x,y;
}Point;
void dessiner_cercle(Point,int);
int main(int argc, char ** argv)
{
Point centre;
centre.x=LARGEUR_ECRAN/2;
centre.y=HAUTEUR_ECRAN/2;
int rayon=50;
PA_Init();
PA_InitVBL();
PA_Init16bitBg(0,0);
while(1)
{
if(Pad.Held.L)
{
rayon++;
PA_Clear16bitBg(0);
}
if(Pad.Held.R)
{
rayon--;
PA_Clear16bitBg(0);
}
if(Pad.Held.Left)
{
centre.x--;
PA_Clear16bitBg(0);
}
else if(Pad.Held.Right)
{
centre.x++;
PA_Clear16bitBg(0);
}
if(Pad.Held.Up)
{
centre.y--;
PA_Clear16bitBg(0);
}
else if(Pad.Held.Down)
{
centre.y++;
PA_Clear16bitBg(0);
}
if(rayon<10)
rayon=10;
else if(rayon>HAUTEUR_ECRAN/2)
rayon=HAUTEUR_ECRAN/2;
if(centre.x-rayon<0)
centre.x=rayon;
else if(centre.x+rayon>LARGEUR_ECRAN)
centre.x=LARGEUR_ECRAN-rayon;
if(centre.y-rayon<0)
centre.y=rayon;
else if(centre.y+rayon>HAUTEUR_ECRAN)
centre.y=HAUTEUR_ECRAN-rayon;
dessiner_cercle(centre,rayon);
PA_WaitForVBL();
}
return 0;
}
void dessiner_cercle(Point centre,int rayon)
{
int x,x1,y;
for(y=centre.y-rayon;y<centre.y;y++)
{
x=(int)(centre.x-sqrt((rayon+y-centre.y)*(rayon-y+centre.y)));
x1=(int)(centre.x-sqrt((rayon+y+1-centre.y)*(rayon-y-1+centre.y)));
PA_Draw16bitLine(0,x,y,x1,y+1,PA_RGB8(255,0,0)); // En haut à gauche
PA_Draw16bitLine(0,2*centre.x-x,y,2*centre.x-x1,y+1,PA_RGB8(255,0,0)); // En haut à droite
PA_Draw16bitLine(0,x,2*centre.y-y,x1,2*centre.y-y-1,PA_RGB8(255,0,0)); // En bas à gauche
PA_Draw16bitLine(0,2*centre.x-x,2*centre.y-y,2*centre.x-x1,2*centre.y-y-1,PA_RGB8(255,0,0)); // En bas à droite
}
}
Dessiner la fonction PA_RandMax !
Vous connaissez sûrement le principe des fonctions mathématiques.
Je ne vais pas vous faire un cours de maths là-dessus ^^ , mais nous allons dessiner PA_RandMax.
Pour cela, nous allons dessiner une suite de lignes qui se suivent pour notre courbe. Nous allons d'abord créer un compteur que l'on incrémentera à chaque tour de boucle. Il représentera nos x. Son image (PA_RandMax(192)
) sera nos y.
Ce qui est logique puisque c'est comme ça que l'on dessine une fonction.
Les mathématiciens (et pas qu'eux ^^ ) mettent l'origine de leur repère orthonormé en bas à gauche. Il faudra donc soustraire à la hauteur de l'écran les y puisque ceux-ci sont inversés (le 0 en haut).
Vous savez assez de notions pour le faire maintenant.
Correction
Encore une fois, voici ma correction :) :
#include <PA9.h>
#define RGB(r,g,b) PA_RGB((int)(r*31./255.),(int)(g*31./255.),(int)(b*31./255.))
#define LARGEUR_ECRAN 256
#define HAUTEUR_ECRAN 192
int main(int argc, char ** argv)
{
int x=0,y=0,y1=0;
PA_Init();
PA_InitVBL();
PA_Init16bitBg(0,0);
PA_InitRand();
while(x<LARGEUR_ECRAN)
{
if(!x)
y=PA_RandMax(HAUTEUR_ECRAN);
else
{
y1=PA_RandMax(HAUTEUR_ECRAN);
if(y>=0&&y1>=0)
PA_Draw16bitLine(0,x-1,y,x,y1,RGB(255,0,0));
y=y1;
}
x++;
PA_WaitForVBL();
}
return 0;
}
Et voilà, vous obtiendrez une courbe rouge sur un fond noir qui se trace en direct ^^ !
Voilà, vous savez comment dessiner sur votre écran :) . N'hésitez pas à relire le chapitre si vous n'avez pas bien assimilé une notion. Nous allons étudier un système indispensable pour tout jeu : le son :magicien: !
Le son
Vous avez déjà vu un jeu sans son ? Bon oui d'accord ça s'est déjà vu par le passé, mais à l'heure actuelle un jeu sans son serait assez mal vu. C'est pourquoi nous allons aborder cette notion tout de suite ;) !
Outils nécessaires
Les Nintendo DS savent lire deux types de fichiers : les .raw et les .mod . On va uniquement créer des fichiers .raw parce qu'ils sont plus faciles à créer.
Mais nous devons télécharger un programme qui peut transformer des fichiers .mp3, .wav, etc. en fichiers .raw.
Pour cela, je vous conseille Switch.
Vous aurez peut-être aussi besoin d'éditer les fichiers sons avec Audacity (petit tuto sur le sujet :) ). Mais je ne vais pas vous en dire plus sur ce dernier.
Mais comment on transforme un fichier .wav en fichier .raw, alors ?
Lancez Switch. Glissez le fichier à transformer dans le grand panel blanc où il est écrit :
Citation
Add Files to Convert into this list by clicking the "Add Files" button or pressing Alt+A on your keyboard.
Ecrivez le chemin du dossier (dans lequel sera mis le fichier .raw) à droite de "Output Folder".
Ensuite, dans "Output Format", sélectionnez ".raw".
Puis, cliquez sur "Encoder Options..." et mettez :
Format : 8 bit signed ;
Sample rate : 11025 ;
Channels : Stereo.
Et cliquez sur ok.
Enfin, cliquez sur le gros bouton "Convert".
Et voilà :) .
Créez ensuite un dossier nommé "data" dans le répertoire de votre jeu et copiez-y le fichier .raw.
Le code, enfin
Il faut d'abord importer le son. Lorsque vous cliquez sur build.bat, le compilateur met le fichier raw et tous les fichiers mp3 dans du code : si vous avez le_nom_du_fichier.raw, vous devrez inclure le_nom_du_fichier.h, idem pour les fichiers mp3.
Il faut initialiser le son avec :
AS_Init(AS_MODE_MP3 | AS_MODE_SURROUND | AS_MODE_16CH);
AS_SetDefaultSettings(AS_PCM_8BIT, 11025, AS_SURROUND);
.
Et vous aurez un son en surround.
Les sons MP3
Les sons MP3 ont une façon spécifique de fonctionner. Pour jouer un MP3, il faut écrire (par exemple pour data/test.mp3) :
AS_MP3DirectPlay((u8*)test, (u32)test_size);
Si vous voulez jouer le son en boucle, vous utiliserez la fonction :
AS_SetMP3Loop(1);
A l'inverse, si vous ne voulez plus que le son soit joué en boucle, mettez 0.
Pour mettre un MP3 en pause, on utilisera AS_MP3Pause();
, pour arrêter un MP3 AS_MP3Stop();
.
Si vous voulez obtenir des indications sur le système MP3 (si un son est joué, en pause, arrêté, etc.), la fonction AS_GetMP3Status();
nous renvoie des flags pouvant être :
MP3ST_STOPPED (canal stoppé) ;
MP3ST_PAUSED (canal en pause) ;
MP3ST_PLAYING (canal occupé) ;
MP3ST_OUT_OF_DATA (on a dépassé le nombre maximal de données de la musique, ne devrait pas arriver) ;
MP3ST_DECODE_ERROR (le système n'a pas pu décoder le MP3) ;
MP3ST_INITFAILED (l'initialisation du système MP3 a échoué).
Les fichiers RAW
Si vous voulez jouer un son RAW (supposons que vous ayez data/boum.raw) :
AS_SoundQuickPlay(boum);
Et votre son sera joué ;) !
Le micro !
Vous attendiez sûrement ce moment : contrôler le micro :p !
Le mieux dans tout ça, est que c'est super facile.
Il y a quatre étapes :
déclarer globalement la variable qui contiendra la voix du joueur (cette variable est aussi appelée "buffer") ;
enregistrer la voix avec
void PA_MicStartRecording(u8 *buffer,s32 longueur)
;réécouter la voix avec
void PA_MicReplay(u8 *buffer,s32 longueur)
;afficher l'intensité de la voix avec la macro
PA_MicGetVol();
.
Voici donc ce que l'on peut faire :
#include <PA9.h>
u8 buffer[100000];
int main(int argc, char ** argv)
{
PA_Init();
PA_InitVBL();
PA_InitText(1,0);
AS_Init(AS_MODE_SURROUND | AS_MODE_16CH);
AS_SetDefaultSettings(AS_PCM_8BIT, 11025, AS_SURROUND);
PA_OutputText(1,0,0,"Appuyez sur A pour enregistrer et sur B pour réécouter");
while (1)
{
if(Pad.Newpress.A)
PA_MicStartRecording(buffer,sizeof(buffer)/sizeof(u8));
else if(Pad.Newpress.B)
PA_MicReplay(buffer,sizeof(buffer)/sizeof(u8));
PA_OutputText(1,0,10,"%d ",PA_MicGetVol());
PA_WaitForVBL();
}
return 0;
}
Avouez que ce n'était pas bien compliqué :p
Voilà, vous vous coucherez encore plus intelligent ce soir :p .
Des fonctionnalités utiles
Tous les jeux officiels (de DS) auxquels vous avez joués se mettent en pause lorsque vous refermez votre DS, récupèrent le nom de votre utilisateur, etc.
Nous allons apprendre à récupérer toutes ces données.
Les fonctions et les structures !
Vérifier si l'écran supérieur est rabattu
PAlib a créé une fonction pour ce cas, c'est void PA_CheckLid()
. Cette fonction renvoie 1 si c'est fermé, et la Nintendo DS se met automatiquement en pause...
/*Mettre le code de début*/
while(1)
{
PA_CheckLid();
PA_WaitForVBL();
}
/*Mettre le code de fin*/
Si vous voulez simplement savoir si la Nintendo DS est fermée (sans pause), vous pouvez utiliser la macro PA_LidClosed()
.
Si vous voulez savoir si la Nintendo DS est fermée, avec pause et en jouant un son (que vous choisissez), vous pouvez utiliser la macro PA_CloseLidSound(canal,son)
, et si vous voulez mettre un son quand elle est fermée et un quand elle se réouvre, il faut utiliser la macro PA_CloseLidSound2(canal,son_ferme,son_ouvert)
.
Récupérer le temps
La structure "PA_RTC" continent des membres exprimant la date :
Seconds : les secondes ;
Minutes : les minutes ;
Hour : les heures ;
Day : le jour ;
Month : le mois ;
Year : l'année, renvoie 0 pour 2000, 1 pour 2001, etc.
On peut écrire :
#include <PA9.h>
int main(int argc, char ** argv)
{
PA_Init();
PA_InitVBL();
PA_InitText(0,0);
while (1)
{
PA_OutputText(0, 2, 10, "%02d/%02d/%02d", PA_RTC.Day, PA_RTC.Month, PA_RTC.Year); // Date
PA_OutputText(0, 2, 12, "%02d:%02d %02d secondes", PA_RTC.Hour, PA_RTC.Minutes, PA_RTC.Seconds); // Time
PA_WaitForVBL();
}
return 0;
}
Récupérer les données de l'utilisateur
La structure "PA_UserInfo" a pour membres :
Name : le nom de l'utilisateur ;
BdayDay : le jour de l'anniversaire ;
BdayMonth : le mois de l'anniversaire ;
Language : la langue de la DS, utile pour faire de bons jeux :
- 0 : japonais ;
- 1 : anglais ;
- 2 : français ;
- 3 : allemand ;
- 4 : italien ;
- 5 : espagnol.Message : le message de l'utilisateur ;
AlarmHour : l'heure de l'alarme programmée ;
AlarmMinute : la minute de l'alarme programmée ;
Color : la couleur préférée de l'utilisateur.
Faire vibrer votre Nintendo DS
Pour ceux qui auraient un Rumble Pak (un composant qui permet des vibrations), vous pouvez l'utiliser dans vos homebrews, et simplement :soleil: ! Ce que nous allons voir ne dépend pas de PALib, mais de la libnds.
Il faut vérifier si le Rumble Pak est inséré avec bool isRumbleInserted(void);
. Cette fonction renvoie vrai lorsque le Rumble Pak est inséré, et faux dans le cas contraire.
A présent, on peut faire vibrer notre Rumble Pak avec void setRumble(bool position);
. Si position est vraie, votre Nintendo DS se met à vibrer. A l'inverse, si position est fausse, votre Nintendo DS s'arrête de vibrer.
Jouer avec la luminosité de l'écran
Une seule fonction permet de régler la luminosité d'un écran, applaudissons void PA_SetBrightness(u8 ecran, s8 brillance);
:p ! La valeur de brillance doit varier entre -31 et 31. Si sa valeur est négative, l'écran penchera vers le noir (-31 est le noir complet). A l'inverse, si sa valeur est plus grande que 0 l'écran penchera vers le blanc(31 est le blanc total). Mettez brillance à 0 et vous aurez un éclairage normal. Cette fonction est souvent utilisée dans les transitions, les splashes et les menus.
Exercice
Présentation
Maintenant, on va faire un jeu qui annonce à l'utilisateur dans combien de jours son anniversaire viendra ou alors depuis combien de jour il est passé.
Il faut que la DS affiche :
Votre anniversaire est dans [1] [2].
Ou :
Votre anniversaire est passé de [1] [2].
Ou encore :
Joyeux anniversaire !
Aide
Il faudra d'abord créer une variable de type double qui va transformer la date actuelle (mois et jour) en nombre de jours. Vous devez donc trouvez un système pour convertir des mois en jours (d'où le double).
Ensuite, il faudra appliquer ce même système pour la date d'anniversaire.
Puis, on créera une fonction pour arrondir des doubles.
On mettra dans une variable de type int le résultat arrondi (d'où la fonction) de la différence entre la date d'anniversaire et la date actuelle.
Si cette différence est plus grande que 1 ou plus petite que -1, il faudra mettre "jour" au pluriel, sinon on le met au singulier.
Si la différence est négative, on affiche :
Votre anniversaire est passé de [1] [2].
Si la différence est positive, on affiche :
Votre anniversaire est dans [1] [2].
Si la différence est égale à zéro, on affiche :
Joyeux anniversaire !
Correction
#include <PA9.h>
#include <string.h>
int arrondir(double);
int main(int argc, char ** argv)
{
PA_Init();
PA_InitVBL();
PA_InitText(1,0);
double date_actuelle=((double)PA_RTC.Month/12.*365.)+(double)PA_RTC.Day;
double date_anniv=((double)PA_UserInfo.BdayMonth/12.*365.)+(double)PA_UserInfo.BdayDay;
int difference=arrondir(date_anniv-date_actuelle);
char jour[6]="";
if(difference<-1||difference>1)
sprintf(jour,"jours");
else
sprintf(jour,"jour");
if(difference<0)
PA_OutputText(1,0,0,"Votre anniversaire est passé de %d %s.",-difference,jour);
else if(difference>0)
PA_OutputText(1,0,0,"Votre anniversaire est dans %d %s.",difference,jour);
else
PA_OutputText(1,0,0,"Joyeux anniversaire !");
while (1)
{
PA_WaitForVBL();
}
return 0;
}
int arrondir(double nombre)
{
if(nombre-(int)nombre>=.5)
nombre+=.5;
return (int)nombre;
}
Explications pour la fonction arrondir :
La variable nombre est un double. Si l'on fait (int)nombre, on obtiendra sa troncature à l'unité. Autrement dit, vous aurez la partie entière de nombre. La règle de l'arrondi est :
Citation
Si la décimale qui suit la décimale de la limite de l'arrondi est plus grand que 4, la décimale de la limite de l'arrondi est augmentée de 1.
En gros, si vous avez 4,45 ; son arrondi au dixième sera 4,5 ; et son arrondi à l'unité sera 4.
Donc si la différence entre nombre et sa troncature à l'unité (on obtient sa partie décimale) est supérieure ou égale à 0,5 alors on augmente nombre de 0,5 pour que (int)nombre prenne la valeur entière +1 de nombre.
On aurait aussi très bien pu faire :
int arrondir(double nombre)
{
if(nombre-(int)nombre>=.5)
return (int)nombre+ 1;
else
return (int)nombre;
}
Il ne faut pas oublier le prototype de la fonction "arrondir".
Explications du main :
Tout d'abord, on initialise PAlib et le texte sur l'écran supérieur (ça aurait pu être l'écran tactile aussi).
Pour mettre un mois en jour, il faut utiliser la proportionnalité. Il y a douze mois et trois cent soixante-cinq jours (au diable les années bissextiles :p ). Il faut donc diviser le numéro du mois par douze et multiplier le résultat par trois cent soixante-cinq. A ce résultat, il faut bien sûr ajouter le numéro du jour.
Bon, voilà c'était quand même assez facile :) .
Nous avons acquis les bases, maintenant attaquons-nous aux techniques avancées :pirate: !