Aller au menu - Aller au contenu

Icône Le dessin

Avatar
Avatar
Mise à jour : 14/10/2009
Difficulté : Facile Facile
282 visites depuis 7 jours, classé 354/786
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 ^^ .
Sommaire du tutoriel :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

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) couleur est la valeur donnée par la macro PA_RGB8(r,g,b) .
R, G et B sont 3 indices correspondant respectivement à la quantité lumineuse de rouge, vert et bleu contenue dans la couleur désirée. Ces indices sont compris entre 0 et 255 inclus. Plus c'est proche de 0, plus c'est sombre et plus c'est proche de 255, plus c'est clair. Sur n'importe quel logiciel de dessin vous avez une interface avec laquelle vous pouvez créer votre couleur à partir des indices RGB. Vous pouvez utiliser ces logiciels pour vous procurer les indices correspondant à la couleur voulue.
Avec PALib, les indices ne sont pas compris entre 0 et 255, mais entre 0 et 31 (ça me rappelle le développement sur Gameboy Color :D ). Mais il existe une fonction qui nous facilite la vie :
Code : C
1
PA_RGB8(255,0,0); // Rouge

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) .
Remarquez que l'on peut utiliser le 16 bit, les sprites et les arrière-plans sur un même écran ! (À condition qu'ils ne soient pas sur la même "couche" de l'écran.)

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


Secret (cliquez pour afficher)
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#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


Secret (cliquez pour afficher)
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#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



Rappel :Citation
Un cercle est une figure géométrique plane constituée de tous les points équidistants d'un point donné, nommé centre.


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


Secret (cliquez pour afficher)
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#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).
Si un de vos y est négatif, il ne faut pas tracer la courbe, sinon elle piquera vers le bas :p .

Vous savez assez de notions pour le faire maintenant.

Correction


Encore une fois, voici ma correction :) :
Secret (cliquez pour afficher)
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#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: !
Chapitre précédent Sommaire Chapitre suivant

Partager

2 commentaires pour "Le dessin"
Note moyenne : 3.57 / 4 (65 votes)
Pseudo Commentaire
Hors ligne 11TLP # Posté le 21/03/2010 à 02:20:38
i'm a fag, a 東方 fag
Avatar

Ville : Limoges
Pays : France métropolitaine
Études : Lycée Gay-Lussac - Limoges

Citation : tuto
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...


J'dirais plutôt que c'est la méthode de calcul de la racine qui évite de trop ralentir. Si elle ne travaille pas sur des floats et renvoie une précision à l'unité près, elle prend à vue de nez une dizaine d'opérations élémentaires maximum.
Sinon on peut faire des disques si on veut, on balaye chaque pixel dans un carré qui contient ce disque, et on n'aura même pas besoin de calculer une racine. Mais bon ça a aussi un coût en nombre d'opérations. ^^

En train de développer un JV du type shoot'em up / jeu de cartes.

Pourquoi les Goto/Lbl en Ti-Basic c'est LE MAL ! (et partout d'ailleurs)
 
Hors ligne sephirotte # Posté le 24/02/2011 à 18:58:01
Avatar

Il existe une fonction qui revoie la couleur d'un pixel?

Voir tous les commentaires