10-10 Gestion du sprite de la voiture

10-10-1 Introduction

 

Nous voici donc devant un sujet délicat qui est celui des sprites. Tous les jeux d’actions ont leur lot de petits bonhommes, de vaisseaux ou autres petits dessins qui s’affichent rapidement à l’écran. Au travers de la voiture, nous allons voir comment gérer et afficher de tels dessins.

 

 

10-10-2 Représentation graphique des sprites

 

Comme nous l’avons vu dans le chapitre 10-2, la voiture comprend 8 angles + 4 positions par angle. Ces 4 positions correspondent aux offsets des pixels dans les octets.

 

 

Afin d’uniformiser et donc de simplifier le traitement de ces sprites, ils ont tous une taille de 16x10 pixels, soient 4x10 octets (en mode 1, 1 octet = 4 pixels). Ainsi, quand on les dessine on prend en compte tous les 16x10 pixels. Mais alors une question se pose : la voiture frôle les bords de piste et passe sur les marquages verts sans que l’on voit qu’il s’agit d’un rectangle, comment cela se peut-il ? et bien dans tous les sprites, il y a toujours une couleur « transparente » ou couleur de fond. Cette couleur n’a bien sure pas de réalité physique. En fait quand on dessine la voiture pixels par pixels, on remplace cette couleur par celle du fond de l ‘écran, d’où l’impression de transparence. Cette couleur est de préférence la n°0, car on se rappelle bien que le microprocesseur est plus sensible au 0 qu’à n’importe quel autre nombre. Ici c’est le orange qui servira donc de transparence et c’est pour cela que la voiture et l’explosion n’en contiennent pas.

 

Cette couleur transparente possède aussi une autre fonction qui intervient dans la détection de collision. Quand la voiture rentre dans le décor, c’est que l’un de ses pixels vert, blanc ou rouge s’est superposé au bord de piste. On ne calculera donc pas les collisions sur les pixels oranges et donc plus généralement sur la couleur n°0.

 

 

10-10-3 Calcul des adresses et offsets écran

 

Certes, les vecteurs systèmes offrent tout un tas de solutions pour dessiner des pixels ou tester des couleurs de fond en fonction de coordonnées (X,Y). Mais ces vecteurs ne sont généralement pas assez rapides pour des jeux gourmands en graphismes et en vitesse. Dans ce cas, il faut directement attaquer la mémoire vidéo avec les contraintes que ça implique. Si encore la voiture se déplaçait en fonction des caractères texte ça irait, mais là elle se déplace de pixels en pixels.

 

Ici la voiture est bien gérée en (X,Y), mais avant de la dessiner on convertit ces coordonnées en 3 valeurs d’adresses :

 

1.       PLFOND : pointeur sur l’adresse de la ligne écran du bord haut/gauche du sprite.

2.       SEGMENT : ou n° d’octet sur lequel se trouve le bord haut/gauche du sprite.

3.       PIXEL : n° du pixel sur lequel se trouve le bord dans l’octet courant (0,1,2 ou 3).

 

PLFOND est un pointeur sur l’adresse de la ligne écran du bord haut/gauche du sprite. C’est un peu compliqué à expliquer comme ça, mais il contient une adresse dans laquelle on trouve l’adresse de la ligne écran. Pour mieux comprendre, prenons le tableau suivant :

 

LIGNES   DW &C000,&C800,&D000,&D800,&E000,&E800,&F000,&F800 ; 0-7

         DW &C050,&C850,&D050,&D850,&E050,&E850,&F050,&F850 ; 8-15

         DW &C0A0,&C8A0,&D0A0,&D8A0,&E0A0,&E8A0,&F0A0,&F8A0 ; 16-23

         DW &C0F0,&C8F0,&D0F0,&D8F0,&E0F0,&E8F0,&F0F0,&F8F0 ; 24-31

         DW &C140,&C940,&D140,&D940,&E140,&E940,&F140,&F940 ; 32-39

         DW &C190,&C990,&D190,&D990,&E190,&E990,&F190,&F990 ; 40-47

         DW &C1E0,&C9E0,&D1E0,&D9E0,&E1E0,&E9E0,&F1E0,&F9E0 ; 48-55

         DW &C230,&CA30,&D230,&DA30,&E230,&EA30,&F230,&FA30 ; 56-63

         DW &C280,&CA80,&D280,&DA80,&E280,&EA80,&F280,&FA80 ; 64-71

         DW &C2D0,&CAD0,&D2D0,&DAD0,&E2D0,&EAD0,&F2D0,&FAD0 ; 72-79

         DW &C320,&CB20,&D320,&DB20,&E320,&EB20,&F320,&FB20 ; 80-87

         DW &C370,&CB70,&D370,&DB70,&E370,&EB70,&F370,&FB70 ; 88-95

         DW &C3C0,&CBC0,&D3C0,&DBC0,&E3C0,&EBC0,&F3C0,&FBC0 ; 96-103

         DW &C410,&CC10,&D410,&DC10,&E410,&EC10,&F410,&FC10 ; 104-111

         DW &C460,&CC60,&D460,&DC60,&E460,&EC60,&F460,&FC60 ; 112-119

         DW &C4B0,&CCB0,&D4B0,&DCB0,&E4B0,&ECB0,&F4B0,&FCB0 ; 120-127

         DW &C500,&CD00,&D500,&DD00,&E500,&ED00,&F500,&FD00 ; 128-135

         DW &C550,&CD50,&D550,&DD50,&E550,&ED50,&F550,&FD50 ; 136-143

         DW &C5A0,&CDA0,&D5A0,&DDA0,&E5A0,&EDA0,&F5A0,&FDA0 ; 144-151

         DW &C5F0,&CDF0,&D5F0,&DDF0,&E5F0,&EDF0,&F5F0,&FDF0 ; 152-159

         DW &C640,&CE40,&D640,&DE40,&E640,&EE40,&F640,&FE40 ; 160-167

         DW &C690,&CE90,&D690,&DE90,&E690,&EE90,&F690,&FE90 ; 168-175

         DW &C6E0,&CEE0,&D6E0,&DEE0,&E6E0,&EEE0,&F6E0,&FEE0 ; 176-183

         DW &C730,&CF30,&D730,&DF30,&E730,&EF30,&F730,&FF30 ; 184-191

         DW &C780,&CF80,&D780,&DF80,&E780,&EF80,&F780,&FF80 ; 192-199

 

Vous l’aurez compris, ce tableau contient toutes les adresses des lignes de l’écran de 0 à 199. Il devient donc très simple de calculer une adresse de ligne en fonction de la coordonnée Y du bord haut/gauche de la voiture :

 

PLFOND = LIGNES + 2 x Y

Adresse ligne Y = (PLFOND)

 

Exemple :

 

Y = 1 => PLFOND = LIGNES + 2

Adresse ligne Y = (LIGNES + 2) = &C800

 

Ainsi, PLFOND contient l’adresse LIGNES + 2 x Y, et le contenu de cette adresse est l’adresse écran de la ligne Y. Mieux encore, en incrémentant PLFOND de 2, on obtient l’adresse de la ligne écran juste en dessous :

 

Adresse ligne Y + 1 = (PLFOND + 2)

 

Ainsi, calculer toutes les adresses des lignes du sprite se résume à une histoire d’incrément de 2.

 

Maintenant que l’on a l’adresse des lignes, il faut quand même connaître l’octet correspondant à la coordonnée X. Ceci permettra d’avoir l’adresse écran complète du bord haut/gauche du sprite à l ‘écran. En plus, il faut ajouter ce numéro à chaque fois que l’on récupère l’adresse de la ligne suivante.

 

SEGMENT = X / 4

 

On isole ensuite le n° de pixel en ne conservant que les 2 bits de poids faible de X :

 

PIXEL = X AND 3

 

 

10-10-4 Recherche du sprite en mémoire

 

Les sprites de la voiture font tous TSPRITE octets et sont rangés en mémoire à partir de l’étiquette VOITURE. Il n’y a d’ailleurs qu’une seule étiquette pour toute la liste de sprites. Ils sont ensuite rangés de la manière suivante :

 

  1. Sprite 1 : angle 0 pixel 0
  2. Sprite 2 : angle 0 pixel 1
  3. Sprite 3 : angle 0 pixel 2
  4. Sprite 4 : angle 0 pixel 3
  5. Sprite 5 : angle 1 pixel 0
  6. Sprite 6 : angle 1 pixel 1
  7. Sprite 32 : angle 7 pixel 3

 

Donc le calcul pour trouver l’adresse mémoire du sprite correspondant à l’angle et au pixel courant est :

 

Adresse sprite = VOITURE + TSPRITE x (4 x angle + n° pixel)

 

 

10-10-5 Détection de collision

 

Quand la voiture touche le bord de la piste, elle explose ! mais comment fait-on pour savoir que la voiture a touché ? et bien c’est simple, si au moins un des pixels visibles de la voiture se superpose à un pixel blanc ou rouge, c’est que le bord de piste est atteint. Bon en pratique c’est moins simple que ça, car il faut se rappeler du codage des pixels dans un octet de mémoire vidéo en mode 1. Soient les 4 pixels de couleur 2 (%10) suivant :

 

 

B1

B0

Pixel C0

1

0

Pixel C1

1

0

Pixel C2

1

0

Pixel C3

1

0

 

 

OCTET EN MODE 1

 

7

6

5

4

3

2

1

0

Codage

C0-B0

C1-B0

C2-B0

C3-B0

C0-B1

C1-B1

C2-B1

C3-B1

Résultat

0

0

0

0

1

1

1

1

 

Les poids forts des couleurs se trouvent à droite, et les poids faibles à gauche. Il y a un certain avantage cependant, car les couleurs 2 (blanc) et 3 (rouge) possèdent un bit de poids fort à 1, alors que les couleurs 0 (orange) et 1 (vert) ont un bit de poids fort à 0. Prenons le cas des deux octets suivant :

 

Voiture

2

2

1

0

= %

0

0

1

0

1

1

0

0

Fond

0

0

2

3

= %

0

0

0

1

0

0

1

1

 

Là le pixel vert de la voiture touche la bordure blanche, il y a donc collision. Pour le voir, on réalise plusieurs opérations logiques :

 

  1. A = octet de fond = %0001 0011
  2. On isole le poids faible : A <= A AND &0F = %0000 0011
  3. Poids faible = poids fort (masque de couleur 3) : A = %0011 0011
  4. B = A
  5. A = octet de la voiture = %0010 1100
  6. A AND B = %0010 0000 différent de 0 donc collision !

 

 

10-10-6 Dessin du sprite avec transparence

 

Si un pixel de la voiture est orange, on recopie le pixel de fond, sinon on dessine le pixel de la voiture. Encore une fois, on travaille sur des octets. Exemple :

 

Voiture

2

2

1

0

= %

0

0

1

0

1

1

0

0

Fond

0

1

1

1

= %

0

1

1

1

0

0

0

0

Résultat

2

2

1

1

= %

0

0

1

1

1

1

0

0

 

Les 2 premiers pixels de fond ne sont pas pris en compte. Le 3ème reste vert car celui de la voiture l’est aussi. Le 4ème est enfin pris en compte car celui de la voiture est orange. Pour ce faire, on réalise aussi plusieurs opérations logiques :

 

  1. A = octet de sprite = % 0010 1100
  2. C = A
  3. A = inversion poids faible <-> poids fort = % 1100 0010
  4. A <= A OR C (masque de couleur 3) = % 1110 1110
  5. B = CPL A = masque des couleurs de fond à supprimer = % 0001 0001
  6. A = octet de fond d’écran = % 0011 1100
  7. A <= A AND B (suppression des couleurs génantes) = % 0001 0000
  8. A <= A OR C (ajout des couleurs du sprite) = % 0011 1100

 

 

10-10-7 Etude du programme

 

Le morceau de programme chargé d’afficher le sprite est assez conséquent, mais comme nous l’avons vu, il y a beaucoup d’opérations à faire. Tout d’abord on regarde s’il faut restituer le fond de la voiture. Ce n’est le cas que si la voiture ne vient pas d’être placée sur la grille de départ. Dans ce cas, c’est le sous-programme RESTFOND qui se charge de cette restitution. Sinon le programme saute à DESVOIT :

 

         LD A,(DRAPFOND)        ; Restitution de fond d'écran?

         OR A

         JR Z,DESVOIT           ; Non => dessin de la voiture directement

 

         CALL RESTFOND          ; Restitution du fond d'écran de la voiture

 

Le programme calcule ensuite PLFOND, le pointeur sur l’adresse de la ligne haute du sprite. PLFOND = LIGNES + 2 x YVOITURE.

 

DESVOIT  LD A,(YVOITURE)

         LD L,A

         LD H,0         

         ADD HL,HL              ; HL = 2 x Y pour un pointage sur 16 bits

         LD DE,LIGNES

         ADD HL,DE              ; HL pointe l'adresse de la ligne courante

         LD (PLFOND),HL         ; mémorisation du pointeur de ligne du fond

 

Puis le n° de segment (ou octet) et le n° de pixel sont calculés dans la foulée. SEGMENT = XVOITURE / 4, PIXEL = XVOITURE AND 3.

 

         LD DE,(XVOITURE)       ; DE = X

         LD A,E

         AND %00000011          ; A = N° de pixel

         LD (PIXEL),A           ; Mémorisation de ce n°

         SRL D

         RR E

         SRL D

         RR E                   ; DE = X/4 = segment de la coordonnée X

         LD (SEGMENT),DE        ; Segment mémorisé

 

Le programme calcule ensuite l’adresse mémoire du sprite de la voiture correspondant à l’angle et au n° de pixel courant. Adresse = VOITURE + TSPRITE x (4 angle + n° de pixel). La multiplication se fait par boucles d’addition, sachant que (4 angle + n° de pixel) est au maximum égal à 31.

 

         LD HL,VOITURE          ; HL pointe les sprites de la voiture

         LD A,(ANGLE)

         SLA A

         SLA A                 

         LD B,A                

         LD A,(PIXEL)

         ADD B                  ; A = 4 x angle + n° de pixel

         JR Z,DESVOIT2          ; A = 0? Oui => DESVOIT2

         LD DE,TSPRITE          ; DE = taille d'un sprite en octet

DESVOIT1 ADD HL,DE              ; HL = VOITURE + TSPRITE x (4 angle + n° pixel)

         DEC A

         JR NZ,DESVOIT1

 

DE pointe alors l’adresse du sprite. B et C contiennent respectivement la hauteur en lignes et la largeur en octets du sprite. Ils serviront ensuite de compteurs pour le balayage complet du sprite. HL = PLAFOND pour le pointage sur les adresses des lignes écrans du sprite affiché. IX pointe la zone de mémorisation du fond d’écran du sprite FOND.

 

DESVOIT2 EX DE,HL               ; DE pointe sur le sprite courant

         LD A,1

         LD (DRAPFOND),A        ; Fond actif

         LD B,HSPRITE           ; B = hauteur du sprite pour comptage

         LD C,LOSPRITE          ; C = largeur en octets pour comptage

         LD HL,(PLFOND)         ; HL = pointeur de ligne du fond

         LD IX,FOND             ; IX = pointeur sur la mémoire de fond du sprite

 

On arrive ensuite à la boucle principale du programme, celle qui scanne le sprite octets par octets. HL et DE sont sauvegardés puis permettent le calcul du bord gauche de toutes les lignes du sprite. En gros, HL = (PLFOND) + SEGMENT = adresse de ligne courante + n° de segment.

 

DESVOIT3 PUSH HL                ; mémorisation du pointeur d'adresse de la ligne courante

         PUSH DE                ; mémorisation du pointeur sur le sprite

         LD E,(HL)

         INC HL

         LD D,(HL)              ; DE = adresse de la ligne courante

         EX DE,HL               ; puis HL

         LD DE,(SEGMENT)        ; DE = n° de segment de X

         ADD HL,DE              ; HL = adresse écran de la ligne de sprite courante

         POP DE                 ; Restitution du pointeur sur le sprite

 

BC est sauvegardé pour pouvoir faire quelques calculs. On récupère l’octet de fond d’écran courant et on le mémorise.

 

DESVOIT4 PUSH BC                ; sauvegarde des compteurs de scanning du sprite

 

         LD A,(HL)              ; A = octet de fond d'écran courant

         LD (IX),A              ; mémorisation de l'octet de fond

         INC IX

 

Le programme fait alors une détection de collision comme expliquée plus haut. Si au moins un pixel de la voiture a touché le bord, OBSTACLE est mis à 1.

 

         AND &0F                ; Poids faible pour analyse du blanc et rouge (coul. 2 & 3)

         LD B,A                 ; B = poids faible

         RLCA

         RLCA

         RLCA

         RLCA                   ; A = Inversion poids faible <-> poids fort

         OR B                   ; poids fort = poids faible (masque de couleurs 3)

         LD B,A                 ; recopie dans B

         LD A,(DE)              ; A = octet courant du sprite

         AND B                  ; à l'emplacement du masque = couleur 0 (transparente)?

         JR Z,DESVOIT5          ; Oui => DESVOIT5

         LD A,1

         LD (OBSTACLE),A        ; non => indicateur d'obstacle rencontré

 

Puis dessine l’octet à l’écran avec gestion de la transparence :

 

DESVOIT5 LD A,(DE)              ; A = octet courant du sprite

         LD C,A                 ; puis C

         RLCA

         RLCA

         RLCA

         RLCA                   ; Inversion poids faible <-> poids fort

         OR C                   ; A = masque des couleurs de sprite à conserver

         CPL                    ; inversion pour masque des couleurs de fond à supprimer

         LD B,A                 ; B = masque des couleurs à supprimer

         LD A,(HL)              ; A = octet de fond d'écran courant

         AND B                  ; suppression des couleurs génantes

         OR C                   ; ajout des couleurs du sprite

         LD (HL),A              ; affichage du résultat

 

Les compteurs B et C sont restitués. DE et HL sont incrémentés pour passer à l’octet suivant. Tant que la ligne de sprite courante n’est pas entièrement scannée, on reboucle en DESVOIT4

 

         POP BC                 ; restitution des compteurs de scanning du sprite

 

         INC DE                 ; incrément du pointeur sur le sprite

         INC HL                 ; incrément du segment d'écran courant

         DEC C                  ; ligne de sprite complètement scannée?

         JR NZ,DESVOIT4         ; non => DESVOIT4

 

Sinon on réinitialise C, on restitue HL et on l’incrémente de 2 pour pointer sur l’adresse de la ligne écran juste en dessous.

 

         LD C,LOSPRITE          ; C = largeur en octets du sprite

         POP HL                 ; Restitution du pointeur d'adresse de ligne courante

         INC HL

         INC HL                 ; HL pointe l'adresse de la ligne suivante

         DJNZ DESVOIT3          ; Rebouclage tant qu'il reste au moins une ligne

 

 

<< RETOUR  ---  SUITE : Gestion des crashes de la voiture >>