Revenir au plan du site

Faire marcher Conrad

Dans l'article [ précédent ] nous avons créé des routines pour presque tous les mouvements, mais la position de Conrad est toujours la même. Il reste deux mouvements à intégrer qui concernent la marche.

Il va falloir, en plus de changer le contenu des sprites, changer leurs positions. Dans le cas de la marche, seule la position horizontale va changer, il n'est pas utile de réécrire les coordonnées verticales. On peut écrire selon ses besoins jusqu'à 3 fonctions. Une pour le déplacement vertical, une pour le déplacement horizontal, une pour un déplacement libre.

Dans NOTRE cas une fois encore, on va écrire des fonctions dédiées à Conrad qui est un assemblage de plusieurs sprites selon un tableau 2x3. Ces fonctions ne seront pas utilisables pour un assemblage autre.
; HL=position X du côté gauche
.SetX
ld de,32 ; 16 pixels mode 1 == 32 pixels mode 2 en précision d'affichage
ld (#6000),hl : ld (#6010),hl : ld (#6020),hl : add hl,de
ld (#6008),hl : ld (#6018),hl : ld (#6028),hl
ret

L'idée peut être tentante de faire une fonction plus souple qui lise les coordonnées et ajoute le déplacement. On donnera un sprite de début, un nombre de sprites et une valeur de déplacement. Sauf que cela ne fonctionnera que et uniquement si les sprites n'ont pas de coordonnées négatives. Le cas échéant, la valeur lue dans l'Asic n'est pas 16 bits et il n'est pas possible de prédire l'état des bits non définis. À éviter.

Le mieux est de conserver la référence en mémoire vive et de l'écrire à chaque changement de coordonnées.

Interrompre un mouvement

Jusqu'à présent, tous les mouvements de Conrad sont très rigides. Je veux dire par là que chaque mouvement commencé doit être terminé avant d'en entamer un autre. J'ai fait ça parce que c'est le choix technique le plus simple. On peut retrouver cette façon de faire dans le jeu Prince of Persia. L'énorme avantage de cette méthode, en particulier pour la marche, est qu'on sait exactement quand un mouvement commence, où il va terminer et à quelles coordonnées. Ainsi, on peut calibrer un pas de marche sur 8 octets et concevoir le level design en conséquence. Cela permet au joueur de s'approcher du bord sans tomber, ou que le personnage se retrouve avec une jambe dans le vide donnant l'air de reposer sur une plateforme invisible. Horrible.

Comme notre personnage n'évolue pas sur un plan infini, il faut aussi pouvoir interrompre un mouvement lors de collisions. Pour cette démonstration, nous allons faire trèèèès simple et interdire de sortir de l'écran.

Sur la planche de Conrad, on devine un déplacement d'environ 50 pixels en 6 étapes d'animation, ce qui correspondrait approximativement à 8 pixels de décalage à chaque changement de sprites. Ceci est possible car les graphismes sont centrés comme il faut dans la planche. Facilitez-vous la vie, préparez vos graphs!

Sur la planche, on peut noter aussi que le premier sprite part de la position de repos, il faudrait commencer à décaler les sprites après la première étape d'animation. Pour réaliser cela, il nous faut deux routines. La première va initialiser le mouvement et indiquer la deuxième routine comme étant celle du mouvement.
Quant à la routine de mouvement, on en sortira soit en cas de collision avec le bord, soit si le joueur arrête d'appuyer sur la touche de direction.
.marcherGauche
ld hl,(.positionxmode2) : ld a,h : or l : jp z,.attendreGaucheInit ; au bord gauche, rien
ld hl,conradMarcheGauche : ld (.animation),hl ; indiquer l'animation à utiliser
ld hl,.marcherGaucheExecution : ld (.routine),hl ; indiquer la routine de marche récurrente
jp .execute
.marcherGaucheExecution
bit 2,a : jp nz,.attendreGaucheInit ; si on relâche la touche, on s'arrête
ld hl,(.positionxmode2) : ld a,h : or l : jp z,.attendreGaucheInit ; on s'arrête au bord
ld de,-8 : add hl,de : ld (.positionxmode2),hl ; valider les nouvelles coordonnées
call .SetX
jp .execute

À droite, la comparaison des touches et de la coordonnée X change.
.marcherDroite
ld hl,640-64 : ld de,(.positionxmode2) : xor a : sbc hl,de : jp c,.attendreDroiteInit ; au bord gauche, rien
ld hl,conradMarcheDroite : ld (.animation),hl ; indiquer l'animation à utiliser
ld hl,.marcherDroiteExecution : ld (.routine),hl ; indiquer la routine de marche récurrente
jp .execute
.marcherDroiteExecution
bit 3,a : jp nz,.attendreDroiteInit ; si on relâche la touche, on s'arrête
ld hl,640-64 : ld de,(.positionxmode2) : xor a : sbc hl,de : jp c,.attendreDroiteInit ; on s'arrête au bord
ld hl,8 : add hl,de : ld (.positionxmode2),hl ; valider les nouvelles coordonnées
call .SetX
jp .execute

Les animations en boucle étaient déjà définies.
conradMarcheGauche animate_loop hspConrad_MarcheGauche,6
conradMarcheDroite animate_loop hspConrad_MarcheDroite,6

Enfin, pour éviter une boucle infinie sur le bord d'écran (si on reste appuyé sur la direction, on va entamer un ping-pong incessant entre la routine d'attente, la routine de marche qui ne peut plus avancer et le retour à l'attente...). La solution simple est de réinitialiser le tampon clavier (qu'on avait de toutes façons altéré) pour désactiver les touches. Ainsi on reste sur l'attente et on affiche l'animation de l'attente.
.attendreDroiteInit ld a,#FF ; reinit des touches
.attendreDroite
ld hl,conradDroite : ld (.animation),hl
ld hl,.attendreDroiteManage : ld (.routine),hl
.attendreDroiteManage
bit 1,a : jp z,.assisDroite
bit 2,a : jp z,.retournerGauche
bit 3,a : jp z,.marcherDroite
bit 4,a : jp z,.degainerDeboutDroite
jp .execute

.attendreGaucheInit ld a,#FF ; réinit des touches
.attendreGauche
ld hl,conradGauche : ld (.animation),hl
ld hl,.attendreGaucheManage : ld (.routine),hl
.attendreGaucheManage
bit 1,a : jp z,.assisGauche
bit 2,a : jp z,.marcherGauche
bit 3,a : jp z,.retournerDroite
bit 4,a : jp z,.degainerDeboutGauche
jp .execute



Je vous ai fait un gros ZIP à télécharger [ ICI ] il contient les sources et binaires nécessaires...