Revenir au plan du site


Déplacer un sprite soft proprement


On va enfoncer une porte ouverte, pour ne pas que le balayage passe au moment où on affiche le sprite, il suffit d'afficher le sprite quand le balayage est ailleurs.

Il existe des méthodes pointues pour ne jamais avoir cet effet de cisaillement (Tearing). Certains comptent le temps machine que va prendre leur routine et calculent en permanence où ils sont à l'écran. D'autres utilisent des techniques à double écran (double Buffer), travaillant tranquillement dans un écran quand c'est l'autre qui est affiché. Mais la méthode la plus simple quand on est sûr d'avoir rapidement terminé, c'est d'attendre que le balayage soit en bas de l'écran pour afficher.

C'est le PPI qui va nous permettre de lire l'état de la VBL. La routine n'est pas compliqué, on lit le port B du PPI (#F500) et c'est le bit 0 qui nous renseigne sur l'état de la VBL. Un décalage va mettre ce bit dans la retenue.
waitVBL
ld b,#F5
.loop in a,(c) : rra : jr nc,.loop
ret

Ne reste plus qu'à ajouter cette routine et à l'appeler avant chaque affichage!

Téléchargez [ce sprite] si ce n'est pas déjà fait, puis assemblez ce code source (vous avez le droit de le lire, il est commenté de partout).
BUILDSNA : BANKSET 0
ORG #100
RUN #100

ld bc,#7F00+%10001100+%00 : out (c),c ; MODE 0
; on règle quelques couleurs pour notre sprite
ld bc,#7F00 : out (c),c : ld a,#54 : out (c),a
ld bc,#7F01 : out (c),c : ld a,#44 : out (c),a
ld bc,#7F02 : out (c),c : ld a,#5C : out (c),a
ld bc,#7F03 : out (c),c : ld a,#5E : out (c),a
ld bc,#7F04 : out (c),c : ld a,#4E : out (c),a
ld bc,#7F05 : out (c),c : ld a,#43 : out (c),a
ld bc,#7F06 : out (c),c : ld a,#4B : out (c),a

ld iy,listePositions
deplacerSprite
call waitVBL
ld c,(iy+0) : inc yl ; en n'incrémentant que le poids faible, on revient tout seul
; au départ au bout de 256 valeurs
ld b,0 ; X=(iy+0)
ld hl,80 ; Y=80
ld xl,21
ld xh,72
ld de,donnees_sprite
call AfficheSprite
jr deplacerSprite


align 256
listePositions
angle=0
repeat 256
defb sin(angle)*30+30
angle=angle+360/128 ; 2 périodes histoires d'aller plus vite
rend


waitVBL
ld b,#F5
.loop in a,(c) : rra : jr nc,.loop
ret

AfficheSprite
; BC=coordonnée X (0-319)
; HL=coordonnée Y (0-199)
; DE=adresse des données du sprite
; XL=largeur du sprite en octets
; XH=hauteur du sprite en nombre de lignes
push de ; on met DE dans la pile car le calcul d'adresse s'en sert
call CalculeAdressePixel
pop de ; et on récupère DE
; HL=destination écran
; DE=toujours l'adresse source des données
ex hl,de ; on permute source et destination pour être utilisées
; avec l'instruction LDIR

.afficheLignes
ld b,0
ld c,xl ; chargeur la largeur d'une ligne
push de ; on met l'adresse de début de la ligne de côté
ldir ; copier la ligne de HL vers DE sur BC octets
pop de ; on récupère l'adresse du début de ligne
; et on calcule le passage à la ligne suivante
ex hl,de ; on permute HL et DE pour pouvoir faire des additions
ld bc,#800 : add hl,bc : jr nc,.dansLeBloc
ld bc,80-#4000 : add hl,bc ; changement de bloc!
.dansLeBloc
ex hl,de ; on permute à nouveau pour retrouver notre DE une ligne plus bas
dec xh ; notre compteur de lignes
jr nz,.afficheLignes
ret
;-------------------
CalculeAdressePixel
; BC=coordonnée X (0-159)
; HL=coordonnée Y (0-199)
; adresse de la ligne dans HL en résultat
ld de,tableau
add hl,hl ; adresses 16 bits, il faut indexer de 2 en 2
add hl,de
ld a,(hl) : inc hl
ld h,(hl) : ld l,a
srl bc : add hl,bc
ret
;-------------------
adresse_ecran=#C000
largeur_ecran=80
tableau
repeat 25
repeat 8
defw adresse_ecran
adresse_ecran+=#800
rend
adresse_ecran+=largeur_ecran
adresse_ecran-=#4000
rend

donnees_sprite incbin 'quiPique.bin'

Le déplacement de notre sprite est beaucoup plus fluide et il n'y a plus d'effet de cisaillement. Encore une victoire de canard!

Bon, dans les faits, on ne va pas se mentir, la routine est très lente, le sprite est gros, il s'en est fallu de peu qu'on continue à se manger le cisaillement quand même ^_^.

On voit sur la capture ci-dessous que quand l'affichage est terminé (en bas du sprite), le balayage a déjà commencé à parcourir le haut du sprite!