Revenir au plan du site
Programmation d'une intro de Noël pour Titan
Titan m'a envoyé quelques images pour la réalisation d'une intro de Noël, on va réaliser cette intro en quelques minutes :)
Conversion des graphismes
On utilise l'outil ConvGeneric (description des commandes sur
[cette page]) pour convertir les graphismes. D'abord la conversion de l'écran qui contient
la palette entière, ensuite on réutilise cette palette pour forcer la conversion avec les encres déjà associées aux couleurs. Si vous ne forcez pas la palette, les graphismes auront chacun une palette avec
des couleurs dans un ordre 'imprévisible'.
convgeneric.exe -scr screen160.png -g
; on récupère la palette Plus "#000,#080,#008,#800,#808,#0F0,#00F,#80F,#0F8,#8F0,#88F,#F88,#FF0,#FF8,#F8F,#FFF"
; que l'on met dans un fichier screen160.pal
convgeneric.exe -impal screen160.pal doigts1.png
convgeneric.exe -impal screen160.pal doigts2.png
convgeneric.exe -impal screen160.pal yeuxFermes.png
convgeneric.exe -impal screen160.pal yeuxOuverts.png
; je fais exprès de mettre le maximum à 61 pour voir qu'il s'arrête bien à 60 sprites dans le log
convgeneric.exe -impal screen160.pal daFonte.png -size 8x17 -c 61
|
On n'oubliera pas de récupérer la palette Gate Array pour notre code!
defb #54,#5C,#44,#56,#46,#4C,#55,#57,#45,#4E,#5F,#59,#4A,#43,#5B,#4B
|
Écriture d'un prototype fonctionnel en Snapshot
Comme tous nos cours et travaux pratiques, l'écriture d'un Snapshot permet de se concentrer sur l'essence même du programme. Une fois que toutes nos étapes seront validées, nous pourrons ajouter les quelques initialisations et compressions éventuelles pour en faire un exécutable depuis une disquette et même un programme K7 pour nos copains sur 464.
Le plus simple d'abord, afficher l'écran et envoyer la palette. La liste des couleurs à envoyer se terminant par un zéro, on peut ajouter une couleur en plus (#54) pour que la routine envoie la couleur du border avant
de s'arrêter.
BUILDSNA : BANKSET 0
ORG #38 : EI : RET
ORG #100 : RUN #100
ld sp,#100 : ei
ld bc,#7F00+%10001100+%00 : out (c),c ; MODE 0
ld hl,palette : ld bc,#7F00
setPalette out (c),c : inc c : inc b : outi : ld a,(hl) : or a : jr nz,setPalette
jr $
palette defb #54,#5C,#44,#56,#46,#4C,#55,#57,#45,#4E,#5F,#59,#4A,#43,#5B,#4B
border defb #54,0
org #C000 : incbin 'screen160.bin'
|
Nous avons besoin d'une routine de sprite pour afficher les changements dans les yeux et les mains. On va aller au plus simple, nous n'avons PAS besoin d'une routine
qui calcule l'adresse de départ puisque nos deux éléments seront toujours affichés au même endroit.
Le hasard faisant bien les choses, les sprites des yeux et des mains font la même largeur, nous allons dérouler la copie avec plusieurs LDI plutôt qu'un LDIR
AfficheSprite
; HL=données du sprite
; DE=adresse écran en haut à gauche du sprite
; XL=hauteur du sprite
.afficheLigne push de : ldi 12 : pop de
ld a,d : add 8 : ld d,a ; ajouter #800 à DE
jr nc,.nextLine ; pas de débordement, on est toujours dans le même bloc de lignes
ld a,80 : add e : ld e,a ; on ajoute 80 (largeur d'une ligne en octets) pour passer au bloc suivant
ld a,#C0 : adc d : ld d,a ; et on enlève #4000 (additionner #C000 c'est comme enlever #4000)
.nextLine
dec xl
jr nz,.afficheLigne
ret
|
Reste le moins passionnant et parfois le plus 'difficile', à savoir trouver un tempo agréable visuellement.
- pour les yeux, on veut qu'ils clignent rapidement avec un délai 'aléatoire' entre chaque clignements.
- pour les mains, on souhaite une animation plus régulière, éventuellement avec quelques variations.
Pour réaliser ces opérations, on pourrait utiliser une structure commune à chaque étape :
- une étape a besoin d'une durée
- une étape affiche un sprite (besoin de son adresse, de l'adresse écran et de sa hauteur)
- une étape terminée doit basculer vers une autre étape (un pointeur vers une étape et où écrire ce pointeur)
- on doit initialiser l'autre étape avec une durée (besoin d'un minimum et d'un modulo)
Ensuite il va falloir gérer l'étape à partir de cette structure. C'est parti!
structure etape
duree defb
sprdata defw
adrecran defw
hauteur defb
etapeSuivante defw
pokeEtape defw
dureeMin defb
dureeMod defb
endstruct
GestionEtape
; IY=etape
ld a,(iy+etape.duree) : dec a : ld (iy+etape.duree),a : ret nz ; durée pas terminé, aurevoir
ld hl,(iy+etape.sprdata)
ld de,(iy+etape.adrecran)
ld a,(iy+etape.hauteur) : ld xl,a
call AfficheSprite
ld de,(iy+etape.etapeSuivante)
ld hl,(iy+etape.pokeEtape) : ld (hl),e : inc hl : ld (hl),d
ld c,(iy+etape.dureeMod) : ld a,r : and c : add (iy+etape.dureeMin) ; A=nouvelle durée
ld (de),a ; nouvelle duree de la nouvelle structure
ret
|
Reste à initialiser nos 4 étapes avec des valeurs adéquates et ça devrait fonctionner ^_^
BUILDSNA : BANKSET 0
ORG #38 : EI : RET
ORG #100 : RUN #100
ld sp,#100 : ei
ld bc,#7F00+%10001100+%00 : out (c),c ; MODE 0
ld hl,palette : ld bc,#7F00
setPalette out (c),c : inc c : inc b : outi : ld a,(hl) : or a : jr nz,setPalette
LaBoucle
call waitVBLstart
ld iy,yeuxOuverts : pokeYeux=$-2
call GestionEtape
ld iy,doigts1 : pokeDoigts=$-2
call GestionEtape
jr LaBoucle
;---
waitVBLstart
ld b,#F5
.loop in a,(c) : rra : jr c,.loop ; attendre l'absence de VBL
waitVBL
ld b,#F5
.loop in a,(c) : rra : jr nc,.loop ; attendre la VBL
ret
;---
struct etape
duree defb
sprdata defw
adrecran defw
hauteur defb
etapeSuivante defw
pokeEtape defw
dureeMin defb
dureeMod defb
endstruct
;---
yeuxOuverts
defb 1
defw yeuxFermesdata
defw #C000+80*7+#800*6+40
defb 21
defw yeuxFermes
defw pokeYeux
defb 4,3
yeuxFermes
defb 1
defw yeuxOuvertsdata
defw #C000+80*7+#800*6+40
defb 21
defw yeuxOuverts
defw pokeYeux
defb 30,31
doigts1
defb 1
defw doigts2data
defw #C000+80*13+32+#800*2
defb 30
defw doigts2
defw pokeDoigts
defb 15,15
doigts2
defb 1
defw doigts1data
defw #C000+80*13+32+#800*2
defb 30
defw doigts1
defw pokeDoigts
defb 15,15
;---
GestionEtape
; IY=etape
ld a,(iy+etape.duree) : dec a : ld (iy+etape.duree),a : ret nz ; durée pas terminée, aurevoir
ld hl,(iy+etape.sprdata)
ld de,(iy+etape.adrecran)
ld a,(iy+etape.hauteur) : ld xl,a
call AfficheSprite
ld de,(iy+etape.etapeSuivante)
ld hl,(iy+etape.pokeEtape) : ld (hl),e : inc hl : ld (hl),d
ld c,(iy+etape.dureeMod) : ld a,r : and c : add (iy+etape.dureeMin) ; A=nouvelle durée
ld (de),a ; nouvelle duree de la nouvelle structure
ret
;---
AfficheSprite
; HL=données du sprite
; DE=adresse écran en haut à gauche du sprite
; XL=hauteur du sprite
.afficheLigne push de : ldi 12 : pop de
ld a,d : add 8 : ld d,a ; ajouter #800 à DE
jr nc,.nextLine ; pas de débordement, on est toujours dans le même bloc de lignes
ld a,80 : add e : ld e,a ; on ajoute 80 (largeur d'une ligne en octets) pour passer au bloc suivant
ld a,#C0 : adc d : ld d,a ; et on enlève #4000 (additionner #C000 c'est comme enlever #4000)
.nextLine
dec xl
jr nz,.afficheLigne
ret
;---
palette defb #54,#5C,#44,#56,#46,#4C,#55,#57,#45,#4E,#5F,#59,#4A,#43,#5B,#4B
border defb #54,0
doigts1data incbin 'doigts1.bin'
doigts2data incbin 'doigts2.bin'
yeuxOuvertsdata incbin 'yeuxOuverts.bin'
yeuxFermesdata incbin 'yeuxFermes.bin'
org #C000 : incbin 'screen160.bin'
|

Nous avons l'image, les mains et les yeux animés, la suite au
[prochain article] :)