< All Topics
Print

10-Déplacement de l’envahisseur

On le sait tous, un extra-terrestre c’est bête à manger du foin, donc on va lui créer une intelligence artificielle (IA) limitée…

en fait, on va faire déplacer l’ennemi de façon autonome mais simple et de plus, l’exemple du livre ne va pas plus loin, donc….on va dire que c’est moi qui suis limité 😉

On va utiliser une des variables que l’on a déclaré précédemment et qui s’appelle inddir (comme ‘indicateur de direction’), je vous rappelle ce que l’on avait fait pour ces variables :

indcol: EQU 41000       ; défini l'adresse de la variable indcol (registre des collisions)
indbal: EQU 41001       ; défini l'adresse de la variable indbal
inddir: EQU 41002       ; défini l'adresse de la variable inddir

On les a d’abord déclaré comme associées à une adresse spécifique de la mémoire RAM dans le pied de page de notre code , pour inddir c’était une adresse très lointaine dans la mémoire qui nous permet d’espérer qu’elle ne se fera pas écraser par du code, ici l’adresse 41002.

De ce fait on pourra stocker n’importe quelle valeur dedans et la modifier au besoin.

Cet indicateur, on va le manipuler pour le faire passer d’une valeur 0 à 1 (et inversement).

Dans l’un des articles précédent on a déclaré inddir à l’adresse 41002 mais on a stocké une valeur dedans qui était 1, un petit rappel de la manière dont on a procédé dans le code :

;------------------------------------------------------------------------------
; Initialise les 3 variables 
; indcol: indicateur de collision adresse 41000
; indbal: indicateur de balle tirée adresse 41001
; inddir: indicateur de direction de l'ennemi adresse 41002
;------------------------------------------------------------------------------
ld   a,1
ld [indcol],a       ; indcol = 1
ld [indbal],a       ; indbal = 1
ld [inddir],a       ; inddir = 1

La stratégie va être que dans la boucle principale du programme, on va appeler une fois par boucle la routine de déplacement de l’ennemi.

Avant de le déplacer, on va aller consulter la valeur de cet indicateur :

  • s’il est à 1 on appellera une routine pour le déplacer de droite à gauche,
  • s’il est à 0 on appellera une routine qui le déplace de gauche à droite.

Mais ce n’est pas tout, avant d’incrémenter le déplacement dans les sous-routine on vérifiera que la valeur de la coordonnée (ici, X) n’a pas atteint une extrémité de l’écran.

Si c’est le cas, on va aller changer la valeur de l’indicateur à l’adresse 41002 pour qu’il se déplace dans l’autre sens à la prochaine boucle et ce, jusqu’à l’extrémité opposée. un petit schéma :

Pourquoi x=240 à droite alors que la résolution va jusqu’à 256 ?

En fait les coordonnées des sprites correspondent au premier pixel en haut à gauche de chaque sprite.

Nos sprites étant agrandis à une taille x2, ils font 16 pixels sur 16 pixels, donc quand notre sprite est à la coordonnée x=240, entre 240 et 256 ce sont les 16 pixels de l’envahisseur qui s’affichent.

Si on décidait d’aller au-delà le sprite sortirait de l’écran car il a besoin de 16 pixels à droite pour apparaître.


Je vais d’abord vous décrire l’algorithme choisi pour le déplacement (qui commence par la routine DEPENV) et ensuite on rentrera dans le code :

On a donc 4 routines à écrire :

  • DEPENV (comme ‘déplacement envahisseur’)
  • ENDRO1 (envahisseur droite)
  • ENGAU (envahisseur à gauche)
  • ENGAU1 (sous routine envahisseur à gauche)

Dans la boucle principale du programme on appellera la routine DEPENV qui dispatchera le travail entre les sous-routines en fonction de l’état de l’indicateur inddir et de l’état de la valeur de la coordonnée X du sprite envahisseur.

Passons au code que l’on rajoute en dessous de la routine DELAI (mais on pourrait le mettre ailleurs vu qu’on utilise des labels pour les appeler) :

;------------------------------------------------------------------------------
; Déplacement de l'envahisseur / Gestion de la direction de l'envahisseur
; indir=1 : de droite à gauche / indir=0 : de gauche à droite
;------------------------------------------------------------------------------
DEPENV:
ld     a,[inddir]   ; on charge en a la valeur de l'indicateur de direction
dec    a            ; on décremente a pour vérifier si indir=1
jp     z,ENGAU      ; si a=0 c'est que inddir valait 1,on va au sous-prog ENGAU (à gauche)
ld     hl,6917      ; sinon on récupère adresse de la position x du sprite dans la TAS
call   74           ; la fonction récupère la valeur de l'adresse dans A donc x=A
sub    240          ; on elève 240 à a
jp     nz,ENDRO1    ; si le résultat n'égale pas 0 alors on va en ENDRO1 (à droite)
ld     a,1          ; sinon on est au bord droit de l'écran et on change l'indicateur
ld     [inddir],a   ; on charge l'indicateur avec la nouvelle direction (1)
ret

La 1ere chose que fait la routine DEPENV, c’est de vérifier la valeur de l’indicateur inddir, s’il vaut 1 c’est que l’envahisseur doit se déplacer vers la gauche (0 vers la droite), il suffit de faire un calcul sur la valeur de l’inddir.

Ici on lui soustrait 1 :

  • si le résultat est égale à 0 c’est que inddir=1 quand on l’a appelé, dans ce cas on appelle la sous-routine ENGAU qui gère ce type de déplacement.
  • sinon (inddir=0 car 0-1 n’égale pas 0) on récupère le X du sprite envahisseur à l’adresse correspondante.

la TAS abrite ces informations, elle débute à 6912 et il faut 4 adresses pour chaque sprites, donc pour l’envahisseur c’est 6917 qui abrite la valeur du X….
…bon allez, je vous fais un petit rappel de ce qu’on a rentré dans la TAS pour ces SPRITES :

Donc on récupère la valeur de la coordonnée X du sprite, comme on compare avec zéro ou pas zéro, pour vérifier si X=240, il suffit de faire un calcul dessus, si tel n’est pas le cas alors la sous-routine ENDRO1 fera le boulot de déplacement vers la droite.

Si par contre on est déjà à l’extrème droite de l’écran, on ne fait que changer la valeur de inddir pour qu’à la prochaine boucle l’envahisseur se déplace vers la gauche.

Je vous donne les autres sous-routines, elles sont maintenant simples à comprendre :

------------------------------------------------------------------------------
; Déplacement de l'envahisseur vers la droite
;------------------------------------------------------------------------------
ENDRO1:
ld     hl,6917      ; on récupère adresse de la position x du sprite dans la TAS
call   74           ; la fonction récupère la valeur de l'adresse dans A donc x=A
inc    a            ; on incrémente a pour déplacer de 1 à droite (x+1)
call   77           ; on charge la position x de la TAS avec le x+1
ret                 ; on retourne au programme principal

;------------------------------------------------------------------------------
; Changement de direction (0) si l'envahisseur est arrivé au bord gauche x=0
;------------------------------------------------------------------------------
ENGAU:
ld    hl,6917       ; on récupère adresse de la position x du sprite dans la TAS
call  74            ; la fonction récupère la valeur de l'adresse dans A donc x=A
SUB   1             ; on enlève 1 à a (x-1)
jp    nz,ENGAU1     ; si maintenant n'est pas égale à 0 on va en ENGAU1
ld    a,0           ; sinon on est au bord gauche de l'écran et on change l'indicateur
ld    [inddir],a    ; on charge l'indicateur avec la nouvelle direction (0)
ret

;------------------------------------------------------------------------------
; Déplacement de l'envahisseur vers la gauche
;------------------------------------------------------------------------------
ENGAU1:
ld     hl,6917      ; on récupère adresse de la position x du sprite dans la TAS
call   74           ; la fonction récupère la valeur de l'adresse dans A donc x=A
dec    a            ; on décrémente a pour déplacer de 1 à gauche (x-1)
call   77           ; on charge la position x de la TAS avec le x+1
ret                 ; on retourne au programme principal

Ce code est à ajouter en dessous de la routine DEPENV.

Elles sont simple à comprendre, surtout avec le schéma de l’algorithme que je vous ai fait plus haut. La seule subtilité c’est les routines qui ont pour rôle de calculer le prochain déplacement effectif de l’envahisseur (ENGAU1 et ENDRO1) finissent par un CALL 77 qui est l’instruction qui modifie vraiment la coordonnée X du SPRITE dans la TAS, sans ce CALL 77 les autres calculs que l’on a fait étaient sans impact sur la TAS. Ils étaient un peut comme quand l’on fait des calculs sur le coin d’un cahier de brouillon.

Il ne nous reste plus qu’à rajouter une instruction dans la boucle principale qui appellera le DEPENV et c’est CALL DEPENV (c’est la ligne 8 du code ci-dessous) :

;------------------------------------------------------------------------------
; Boucle du jeu : Le programme démarre à DEBUT puis va à BOUCLE
;------------------------------------------------------------------------------

DEBUT:              ; c'est ici que débute le programme !!!
call  INIT          ; on appelle le sous-prog INIT
BOUCLE:             ; c'est la boucle principale du programme !!!
call   DEPENV       ; on appelle le déplacement de l'envahisseur
call   DELAI        ; on appelle le sous-prog DELAI afin de ralentir l'exécution
ld     a,8          ; on teste la touche gauche = ligne 8 de la matrice du clavier
call   321          ; (bios) SNSMAT retourne dans la valeur de la ligne 8
bit    4,a          ; bit teste la colonne 4 (0=pressé 1=non pressé)
call   z,FUSGAU     ; si résultat=0 on va au sous-prog FUSGAU (déplacer à droite)
call   DELAI        ; on ralenti l'exécution
ld     a,8          ; la touche droite est aussi sur la ligne 8 de la matrice
call   321          ; on appelle le bios pour qu'il donne la valeur de la ligne 8
bit    7,a          ; la colonne 7 est testée (0=pressé 1=non pressé)
call   z,FUSDRO     ; si résultat=0 on va au sous-prog FUSDRO (déplacer àgauche)
call   DELAI        ; on ralenti l'exécution
jp     BOUCLE       ; on redémarre la boucle principale

Et pour finir le code entier du programme mis à jour qu’il ne vous reste plus qu’à compiler et lancer dans l’émulateur blueMSX :

; ****************************************************************
; MSX ROM Cartridge Header and Function library
; ****************************************************************
FNAME "test3.ROM"
cpu z80

ORG 4000h

INCLUDE "MSXRom-Include.asm"

db "AB"
DW DEBUT            ; la ROM lance le sous-programme DEBUT au démarrage
DW 0
DW 0
DW 0
DS 6

;------------------------------------------------------------------------------
; Iinitialisation, appelé par le début du programme : DEBUT
;------------------------------------------------------------------------------
INIT:
Ld   a,1            ; 1 pour screen 1
ld   [64687],a      ; (bios)SCRMOD adresse hexa $FCAF :mode courant de l'ecran
call 95             ; (bios)CHGMOD : change le mode l'écran
;------------------------------------------------------------------------------
; Charge la TGS avec la forme des 3 sprites
;------------------------------------------------------------------------------
ld   hl,donnee      ; charge les données TGS via le label: donnee
ld   de,14336       ; adresse du début de la TGS
ld   bc,24          ; longueur du bloc = 3 sprites de 8 octets = 24
call 92             ; LIDRVM transfert de bloc de la RAM vers la VRAM
;------------------------------------------------------------------------------
; Charge la TAS avec les attributs des 3 sprites  (registre 5)
; Registre 5 => valeur x 128 = Début de l'adresse de la TAS
;------------------------------------------------------------------------------
ld   c,5            ; registre 5 gère l'adresse de début de la TAS
ld   b,54           ; valeur 54 = positionne début de la TAS à l'adresse 6912
call 71             ; WRTVDP Ecrire dans le VDP (VRAM)
ld   hl,donne1      ; charge les données TAS au label: donne1
ld   de,6912        ; adresse du début de la TAS
ld   bc,12          ; longueur du bloc = 4 données pour chaque sprite (3 sprites)
call 92             ; LIDRVM transfert de bloc de la RAM vers la VRAM
;------------------------------------------------------------------------------
; Initialise les 3 variables 
; indcol: indicateur de collision adresse 41000
; indbal: indicateur de balle tirée adresse 41001
; inddir: indicateur de direction de l'ennemi adresse 41002
;------------------------------------------------------------------------------
ld   a,1
ld [indcol],a       ; indcol = 1
ld [indbal],a       ; indbal = 1
ld [inddir],a       ; inddir = 1
;------------------------------------------------------------------------------
; Taille des sprites agrandis (registre 1)
;------------------------------------------------------------------------------
ld   c,1            ; registre 1 gère la taille des sprites
ld   b,225          ; 225 c'est la valeur pour agrandir un sprite 8 x 8
call 71             ; WRTVDP Ecrire dans un registre du VDP (VRAM)
;------------------------------------------------------------------------------
; Positionne l'adresse de début de la TNP (registre 2)
; Registre 2 => valeur x 1024 = Début de l'adresse de la TNP
;------------------------------------------------------------------------------
ld   c,2            ; registre 2
ld   b,6            ; valeur 6 = positionne début de la TNP à l'adresse 6144
call 71             ; WRTVDP Ecrire dans un registre du VDP (VRAM)
ret                 ; fin de la boucle INIT

;------------------------------------------------------------------------------
; Déplacement de la fusée à gauche
;------------------------------------------------------------------------------
FUSGAU:
ld     hl,6913      ; récupère adresse de la position x du sprite dans la TAS
call   74           ; la fonction charge la valeur de l'adresse dans A donc x=A
dec    a            ; on teste A en le décrementant d'une unité x-1
ret    z            ; si le résultat de l'opération x=0 on ne fait rien
                    ; et on quitte la sous-routine
                    ; car cela veut dire que la fusée est à la limite de l'écran à gauche
call   77           ; sinon on charge la position x de la TAS avec la nouvelle valeur x-1
ret                 ; on retourne au programme principal

;------------------------------------------------------------------------------
; Déplacement de la fusée droite
;------------------------------------------------------------------------------
FUSDRO:
ld     hl,6913      ; récupère adresse de la position x du sprite dans la TAS
call   74           ; la fonction charge la valeur de l'adresse dans A donc x=A
SUB    240          ; on teste A en lui enlevant 240
ret    z            ; si le résultat de l'opération=0 on ne fait rien 
                    ; et on quitte la sous-routine
                    ; car cela veut dire que la fusée est à la limite de l'écran à droite
add    a,241        ; rajoute les 240 pour retrouver le x initial et incrémente de 1(donc 241)
call   77           ; sinon on charge la position x de la TAS avec la nouvelle valeur x+1
ret                 ; on retourne au programme principal

;------------------------------------------------------------------------------
; DELAI
;------------------------------------------------------------------------------
DELAI:
ld    a,255         ; c'est la valeur de départ, plus elles est haute, plus c'est lent
                    ; a ne peut contenir qu'une valeur entre 0 et 255
DEL:
dec   a             ; c'est la boucle qui va décrémenter de 1 jusqu'à a=0
jp    nz,DEL        ; si a n'est pas égale à 0 on repart au début de la boucle
ret                 ; si a=0 on retourne au programme principal


;------------------------------------------------------------------------------
; Déplacement de l'envahisseur / Gestion de la direction de l'envahisseur
; indir=1 : de droite à gauche / indir=0 : de gauche à droite
;------------------------------------------------------------------------------
DEPENV:
ld     a,[inddir]   ; on charge en a la valeur de l'indicateur de direction
dec    a            ; on décremente a pour vérifier si indir=1
jp     z,ENGAU      ; si a=0 c'est que inddir valait 1,on va au sous-prog ENGAU (à gauche)
ld     hl,6917      ; sinon on récupère adresse de la position x du sprite dans la TAS
call   74           ; la fonction récupère la valeur de l'adresse dans A donc x=A
sub    240          ; on elève 240 à a
jp     nz,ENDRO1    ; si le résultat n'égale pas 0 alors on va en ENDRO1 (à droite)
ld     a,1          ; sinon on est au bord droit de l'écran et on change l'indicateur
ld     [inddir],a   ; on charge l'indicateur avec la nouvelle direction (1)
ret

;------------------------------------------------------------------------------
; Déplacement de l'envahisseur vers la droite
;------------------------------------------------------------------------------
ENDRO1:
ld     hl,6917      ; on récupère adresse de la position x du sprite dans la TAS
call   74           ; la fonction récupère la valeur de l'adresse dans A donc x=A
inc    a            ; on incrémente a pour déplacer de 1 à droite (x+1)
call   77           ; on charge la position x de la TAS avec le x+1
ret                 ; on retourne au programme principal

;------------------------------------------------------------------------------
; Changement de direction (0) si l'envahisseur est arrivé au bord gauche x=0
;------------------------------------------------------------------------------
ENGAU:
ld    hl,6917       ; on récupère adresse de la position x du sprite dans la TAS
call  74            ; la fonction récupère la valeur de l'adresse dans A donc x=A
SUB   1             ; on enlève 1 à a (x-1)
jp    nz,ENGAU1     ; si maintenant n'est pas égale à 0 on va en ENGAU1
ld    a,0           ; sinon on est au bord gauche de l'écran et on change l'indicateur
ld    [inddir],a    ; on charge l'indicateur avec la nouvelle direction (0)
ret

;------------------------------------------------------------------------------
; Déplacement de l'envahisseur vers la gauche
;------------------------------------------------------------------------------
ENGAU1:
ld     hl,6917      ; on récupère adresse de la position x du sprite dans la TAS
call   74           ; la fonction récupère la valeur de l'adresse dans A donc x=A
dec    a            ; on décrémente a pour déplacer de 1 à gauche (x-1)
call   77           ; on charge la position x de la TAS avec le x+1
ret                 ; on retourne au programme principal


;------------------------------------------------------------------------------
; Boucle du jeu : Le programme démarre à DEBUT puis va à BOUCLE
;------------------------------------------------------------------------------

DEBUT:              ; c'est ici que débute le programme !!!
call  INIT          ; on appelle le sous-prog INIT
BOUCLE:             ; c'est la boucle principale du programme !!!
call   DEPENV       ; on appelle le déplacement de l'envahisseur
call   DELAI        ; on appelle le sous-prog DELAI afin de ralentir l'exécution
ld     a,8          ; on teste la touche gauche = ligne 8 de la matrice du clavier
call   321          ; (bios) SNSMAT retourne dans la valeur de la ligne 8
bit    4,a          ; bit teste la colonne 4 (0=pressé 1=non pressé)
call   z,FUSGAU     ; si résultat=0 on va au sous-prog FUSGAU (déplacer à droite)
call   DELAI        ; on ralenti l'exécution
ld     a,8          ; la touche droite est aussi sur la ligne 8 de la matrice
call   321          ; on appelle le bios pour qu'il donne la valeur de la ligne 8
bit    7,a          ; la colonne 7 est testée (0=pressé 1=non pressé)
call   z,FUSDRO     ; si résultat=0 on va au sous-prog FUSDRO (déplacer àgauche)
call   DELAI        ; on ralenti l'exécution
jp     BOUCLE       ; on redémarre la boucle principale

;------------------------------------------------------------------------------
; Les données
;------------------------------------------------------------------------------
donnee:
db     24,24,126,126  ; sprite 1 pour la TGS
db     255,255,255,0  ; sprite 1   "     "
db     60,126,153,255 ; sprite 2   "     "
db     102,60,66,36   ; sprite 2   "     "
db     24,24,24,24    ; sprite 3   "     "
db     24,24,24,24    ; sprite 3   "     "
donne1:
db     170,100,0,15   ; Fusée joueur sprite 0 (position y, x,n°sprite,couleur)
db     0,0,1,12       ; Envahisseur  sprite 1 (position y, x,n°sprite,couleur)  
db     200,0,2,1      ; Laser fusée  sprite 2 (position y, x,n°sprite,couleur)



;**************************************************************************************************
; Standard Libraries
;**************************************************************************************************

INCLUDE "MSXRom-Lib.asm" ; intègre la librairie MSX

END: EQU $
indcol: EQU 41000       ; défini l'adresse de la variable indcol (registre des collisions)
indbal: EQU 41001       ; défini l'adresse de la variable indbal
inddir: EQU 41002       ; défini l'adresse de la variable inddir

;**************************************************************************************************
; RAM Definitions
;**************************************************************************************************

;-------------------
; Définit que le programme démarrera au début de la RAM disponible
;-------------------
ORG RAMSTART
Table of Contents