Revenir au plan du site

Animation d'un personnage avec les sprites hard

Comme évoqué dans l'article [ Animation delta des sprites hards ], les sprites hards possèdent leur propre mémoire qu'il faut écrire à chaque fois qu'on veut en changer le contenu. Ce ne sont pas des sprites hard "traditionnels" avec lesquels il n'y aurait que l'adresse des données à changer.

L'article donnait plusieurs méthodes pour écrire dans les sprites hards. Nous allons utiliser la méthode qui décompresse à la volée. Pourquoi?

Pour simplifier la gestion de notre personnage, qui voudrait aller à droite, à gauche, sauter, changer de direction instantanément, nous n'allons pas utiliser le delta, cela impliquerait de calculer beaucoup de combinaisons, de créer un code complexe.

Ici, nous allons nous concentrer sur quelques animations et en fonction du souhait de l'utilisateur, nous changerons l'animation courante. C'est pourquoi la copie complète dans les sprites est la plus adaptée, même si plus lente.

Voici la routine pour rappel.

Copie avec décompression à la volée

startingindex 0
align 256
tableDecale4
repeat 256,x
defb x>>4
rend

decrunchSprite
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4)
.loop
repeat 8
ld a,(hl) : inc l : ld (de),a : inc e
ld c,a : ld a,(bc) : ld (de),a : inc e
rend
jr nz,.loop
ret

Différents type d'animations

Voici une planche très allégée d'un très bon jeu disposant de nombreuses étapes d'animations. Si on se concentre sur la première ligne, elle contient 4 animations différentes :
- Le personnage est orienté vers la gauche et ne bouge pas.
- Le personnage est orienté vers la droite et ne bouge pas.
- Le personnage se tourne de gauche à droite et ne bouge plus.
- Le personnage se tourne de droite à gauche et ne bouge plus.



Si les 5 premiers meta-sprites (composés chacun de 6 sprites hards) sont numérotés de 0 à 4, on peut définir les 4 animations de la sorte :
- sprite 0, reboucler sur la dernière étape, y en a qu'une, c'est le sprite 0
- sprite 4, reboucler sur la dernière étape
- sprite 0, 1, 2, 3, 4, puis reboucler sur la dernière étape (sprite 4)
- sprite 4, 3, 2, 1, 0, puis reboucler sur la dernière étape (sprite 0)

Pour le moment, c'est assez simple, on peut utiliser le même type d'animation pour tout le monde, dérouler un ou plusieurs meta-sprite, puis reboucler sur le dernier.

Listes chainées

Vous connaissez les listes chainées?

C'est une technique qui consiste à relier des objets identiques en incluant dans chaque objet une notion de suivant (et parfois de précédent mais ce cas ne nous intéresse pas).

Ainsi, quand on a différentes étapes à exécuter, on définit chaque étape dans un objet et on indique quelle est l'étape suivante. Cela peut se faire avec un pointeur, un index...



Dans notre cas, nous avons besoin de peu de choses : L'adresse des données à copier et l'objet suivant.

On va s'écrire une macro pour pouvoir tout définir facilement. Pour rappel, nos sprites sont stockés sur 128 octets chacun.
startingindex 0
macro animate_single zelabel,nbanim
curspr=0
repeat {nbanim}-1
defw $+4 ; nos objets font 4 octets, on passe au suivant
defw {zelabel}+6*128*curspr
curspr+=1
rend
defw $ ; boucle infinie sur la dernière étape
defw {zelabel}+6*128*curspr
mend

Exemple d'utilisation de la macro.
animate_single hspConrad_GaucheDroite,1 ; Conrad regarde à gauche
animate_single hspConrad_GaucheDroite+4*6*128,1 ; Conrad regarde à droite
animate_single hspConrad_GaucheDroite,5 ; Conrad regarde de gauche à droite puis reste figé à droite

Ah! Il nous manque le cas où les données sont lues à l'envers! Vite, une petite macro ;)
startingindex 0
macro animate_single_reverse zelabel,nbanim
curspr=nbanim-1
repeat {nbanim}-1
defw $+4
defw {zelabel}+6*128*curspr
curspr-=1
rend
defw $ ; infinite loop on last step
defw {zelabel}+6*128*curspr
mend

Exemple d'utilisation de la macro inversée.
animate_single_reverse hspConrad_GaucheDroite,5 ; Conrad regarde de droite à gauche puis reste figé à gauche

Moteur d'animation exploitant la liste chainée

On a créé nos listes avec à chaque fois [suivant+adresse données], reste à exploiter ces listes.

Principe de base, on a une variable qui pointe vers un objet. En lisant l'adresse suivante dans l'objet, on actualise cette variable. Nos listes chainées ne changent jamais, elles sont uniquement lues.

Notre routine aura deux paramètres, l'adresse de la variable pointant vers l'objet, et la destination (quel sprite hard écrire)
; A=sprite destination
; HL=animation
animate_execute_step
;.reloop
ld e,(hl) : inc hl : ld d,(hl) : dec hl ;: ex hl,de ; current step in 'animation'
ex hl,de : ldi : ldi ; overwrite current step in 'animation'
ld d,a : ld e,0 : ld a,(hl) : inc hl : ld h,(hl) : ld l,a ; HL = sprite data source / DE = sprite destination
ld b,hi(hsp_conversion)
ld xl,6 ; meta-sprite de 6 sprites hards
.unpack_hsp
repeat 8
ld a,(hl) : inc l : ld (de),a : inc e : ld c,a : ld a,(bc) : ld (de),a : inc e ; 1 byte => 2 pixels
rend
jr nz,.unpack_hsp
dec l : inc hl
inc d ; next sprite
dec xl
jr nz,.unpack_hsp
ret
; DE = next sprite

Bon, on se l'affiche? D'abord la conversion, classique avec deux options en particulier. Le stockage des pixels groupés par deux sur un octet et la conservation des sprites vides.
; 30 parce que 6x5=30
convgeneric.exe -hsp -g -meta 2x3 -c 30 -p 2 -k flashBackSheet.png

Le premier jet sera au plus simple, on gère les touches gauche et droite du joystick et on affiche soit la planche de droite, soit celle de gauche...

Il vous faudra les fichiers [flashBackGround.bin], [conradGD.bin] [conradGDanimate.asm] pour compiler le source.
buildsna
SNASET CPC_TYPE,4 ; 6128+
bankset 0
org #38
ei : ret
org #100
run #100

;*** RMR2 tags ***
ASICOFF equ 0 : ROM0000 equ 0 : ROM4000 equ %01000 : ROM8000 equ %10000 : ASICON equ %11000
ROM0 equ 0 : ROM1 equ 1 : ROM2 equ 2 : ROM3 equ 3 : ROM4 equ 4 : ROM5 equ 5 : ROM6 equ 6 : ROM7 equ 7
macro RMR2 tags : ld a,{tags}+%10100000 : ld b,#7F : out (c),a : mend

ld sp,#100 ; la pile avant le code
ld bc,#7F80+%1100 : out (c),c ; MODE 0
jp UnlockAsic

include 'conradGDanimate.asm' ; définir les routines d'animation et les macros

UnlockAsic
ld bc,#BCFF
out (c),c
out (c),0
ld hl,%1001000011101010
.loop
out (c),c
ld a,h:rlca:ld h,l:ld l,a
srl c:res 3,c
and #88
or c
ld c,a
cp #4D
jr nz,.loop
ld a,#CD
out (c),a : out (c),a
RMR2 ASICON
ld a,199 : ld (#6800),a ; interruption ligne 200
ld hl,palette
ld de,#6400
ld bc,64
ldir ; on envoie toutes les couleurs d'un coup, fond, border, sprites
ei

; activer 6 sprites en ratio mode 1
ld a,%1001
ld hl,#6004 : ld b,6 : ld de,8
.setHsp ld (hl),a : add hl,de : djnz .setHsp

; il faut positionner nos sprites à minima à l'initialisation
ld hl,200 : ld (#6000+8*0),hl : ld (#6000+8*2),hl : ld (#6000+8*4),hl
ld hl,232 : ld (#6000+8*1),hl : ld (#6000+8*3),hl : ld (#6000+8*5),hl
ld hl,72 : ld (#6002+8*0),hl : ld (#6002+8*1),hl
ld hl,88 : ld (#6002+8*2),hl : ld (#6002+8*3),hl
ld hl,104 : ld (#6002+8*4),hl : ld (#6002+8*5),hl

;********************************************************
mainLoop
;********************************************************
halt : halt : halt : halt
; lire le joystick
ld a,#49 : ld bc,#F40E : out (c),c : ld bc,#F6C0 : out (c),c : out (c),0
ld bc,#F792 : out (c),c : dec b : out (c),a : ld b,#F4 : in a,(c)
ld bc,#F782 : out (c),c : dec b : out (c),0

bit 2,a : jp z,Conrad.gauche
bit 3,a : jp z,Conrad.droite
jp Conrad.execute ; continuer l'animation en cours si aucune touche

;*********
Conrad
;*********
.gauche ld hl,conradGauche : ld (.animation),hl : jp .execute
.droite ld hl,conradDroite : ld (.animation),hl : jp .execute
.execute ld a,#40 : ld hl,.animation : call animate_execute_step : jp mainLoop
.animation defw conradDroite ; animation par défaut à droite

conradGauche animate_single hspConrad_GaucheDroite,1 ; Conrad regarde à gauche
conradDroite animate_single hspConrad_GaucheDroite+4*6*128,1 ; Conrad regarde à droite
conradGaucheDroite animate_single hspConrad_GaucheDroite,5 ; Conrad regarde de gauche à droite puis reste figé à droite
conradDroiteGauche animate_single_reverse hspConrad_GaucheDroite,5 ; Conrad regarde de droite à gauche puis reste figé à gauche

palette defw #000,#004,#322,#041,#444,#664,#00B,#775,#885,#886,#995,#996,#63D,#AA6,#BB6,#EEF
border defw #000
hsp defw #360,#461,#572,#194,#693,#44A,#2B5,#55B,#888,#66C,#7C6,#77E,#88F

org #8000 : hspConrad_GaucheDroite incbin 'conradGD.bin'
org #C000 : incbin 'flashBackGround.bin'



Rendez-vous dans [ l'article suivant ] pour voir comment on va faire évoluer la gestion de notre personnage et utiliser les deux autres animations.