Cet article est le troisième d'une série de cinq consacrés à la programmation d'un one pixel sine scroll sur Amiga, un effet très utilisé par les coders de démos et autres cracktros durant un temps. Par exemple, dans cette cracktro du groupe Supplex :
Dans le premier article, nous avons vu comment installer en environnement de développement sur un Amiga émulé avec WinUAE et coder la Copper list de base pour afficher quelque chose à l'écran. Dans le deuxième article, nous avons vu comment préparer une police de caractères 16x16 pour en afficher facilement les colonnes de pixels des caractères, précalculer les valeurs du sinus requises pour déformer le texte en modifiant l'ordonnée des colonnes, et mettre en place un triple buffering pour alterner proprement les images à l'écran.
Dans ce troisième article, nous allons rentrer dans le vif du sujet en voyant comment dessiner et animer le sine scroll, d'abord au CPU, puis au Blitter.
Cliquez ici pour télécharger l'archive contenant le code et les données du programme présenté ici - c'est la même que dans les autres articles.
NB : Cet article se lit mieux en écoutant l'excellent module composé par Nuke / Anarchy pour la partie magazine de Stolen Data #7, mais c'est affaire de goût personnel...
Click here to read this article in english.
Faire défiler et animer le sine scroll
La boucle principale peut maintenant être intégralement décrite. Elle accomplit successivement les tâches suivantes à chaque itération :
- attendre que le faisceau d'électron a terminé de dessiner la trame ;
- permuter circulairement les trois bitplanes pour afficher la dernière image ;
- attendre le Blitter et démarrer l'effacement du bitplane C contenant la pénultième image ;
- dessiner le texte dans le bitplane B qui contenait l'antépénultième image ;
- animer l'indice de la première colonne du premier caractère du texte à dessiner ;
- animer le sinus de cette première colonne ;
- tester si le bouton gauche de la souris est pressé.
Les trois premières tâches ont déjà été décrites. Nous allons donc décrire les suivantes.
Le sine scroll est dessiné par une boucle qui dessine SCROLL_DX colonnes de caractères consécutifs du texte à partir de la colonne SCROLL_X dans le bitplane. Les indices de la première colonne à dessiner et du premier caractère dont elle provient sont conservés dans les variables scrollColumn et scrollChar, respectivement. L'offset du sinus de la première colonne du sine scroll est conservé dans une variable angle.
Réglons immédiatement la question de l'animation du sine scroll dans la boucle principale.
Faire défiler le texte le long d'un sinus ne présenterait pas beaucoup d'intérêt si ce dernier n'était pas lui-même animé : il donnerait simplement l'impression de parcourir des montagnes russes. Pour cela, nous décrémentons l'offset du sinus de la première colonne du sine scroll à chaque trame, non sans oublier de gérer l'éventuel débordement :
move.w angle,d0 sub.w #(SINE_SPEED_FRAME<<1),d0 bge _angleFrameNoLoop add.w #(360<<1),d0 _angleFrameNoLoop: move.w d0,angle
De plus, le texte doit défiler de gauche à droite. Pour cela, nous incrémentons de SCROLL_SPEED l'indice de la première colonne dans le texte. Ici, nous devons gérer deux éventuels débordements : le débordement d'un caractère pour passer de la dernière colonne d'un caractère à la première du caractère suivant, et le débordement du texte pour passer du dernier caractère du texte au premier :
move.w scrollColumn,d0 addq.w #SCROLL_SPEED,d0 cmp.b #15,d0 ;La colonne suivante se trouve-t-elle après la dernière colonne du caractère courant ? ble _scrollNextColumn ;Si non, en rester là sub.b #15,d0 ;Si oui, utiliser une colonne dans le caractère suivante à la place... move.w scrollChar,d1 addq.w #1,d1 ;...et passer au caractère suivant lea text,a0 move.b (a0,d1.w),d2 bne _scrollNextChar ;...et vérifier si ce caractère ne se trouve pas après la fin du texte clr.w d1 ;...et si oui, reboucler sur le premier caractère du texte _scrollNextChar: move.w d1,scrollChar _scrollNextColumn: move.w d0,scrollColumn
Nous pouvons maintenant passer au dessin du sine scroll. Ce dernier est assuré par la boucle évoquée plus tôt, qui est logée dans la boucle principale. Avant d'entamer cette boucle, nous devons procéder à un certain nombre d'initialisations dans l'objectif de maximiser l'utilisation des registres pour minimiser la nécessité d'aller chercher des données en mémoire.
Tout d'abord, nous déterminons l'offset (D6) du mot dans le bitplane dans lequel se trouve le bit qui correspond à la première colonne où dessiner, et nous identifions ce bit (D7) :
;Déterminer l'offset du mot du bitplane de la première colonne où dessiner moveq #SCROLL_X,d6 lsr.w #3,d6 ; Offset de l'octet de la colonne dans le bitplane bclr #0,d6 ; Offset du mot (revient à lsr.w #4 puis lsl.w #1) ;Déterminer le bit de ce mot correspondant à cette colonne moveq #SCROLL_X,d4 and.w #$000F,d4 moveq #15,d7 sub.b d4,d7 ;Bit dans le mot
Ensuite, nous déterminons l'adresse (A0) du caractère courant ainsi que celle (A1) du mot dans la police 16x16 de sa colonne courante (D4) à dessiner dans la colonne courante du bitplane évoquée à l'instant :
move.w scrollChar,d0 lea text,a0 lea (a0,d0.w),a0 move.w scrollPixel,d4 clr.w d1 move.b (a0)+,d1 subi.b #$20,d1 lsl.w #5,d1 ;32 octets par caractère dans la police 16x16 move.w d4,d2 ;Colonne du caractère à dessiner lsl.w #1,d2 ;2 octets par ligne dans la police 16x16 add.w d2,d1 move.l font16,a1 lea (a1,d1.w),a1 ;Adresse de la colonne à dessiner
Dans le code précédent, l'offset de la première colonne d'un caractère se déduit du code ASCII de ce caractère auquel il faut soustraire $20 - aspect pratique de la police 8x8 qui a servi de base à la police 16x16, les caractères sont ainsi triés.
Enfin, nous procédons à des initialisations diverses de registres utilisés de manière récurrente dans la boucle, dont l'offset du sinus de la colonne courante (D0) et le nombre de colonnes restant à dessiner (D1) :
move.w angle,d0 move.w #SCROLL_DX-1,d1 move.l bitplaneB,a2