Je suis en train de tenter de programmer un petit jeu vu de dessus, et je rencontre un problème de clignotement de mon unique sprite.
Je m'explique : 1) J'affiche un écran, en mode 1, qui servira de décors. 2) Je commence une boucle ici : 3) Je capture la zone de décors où je vais afficher mon sprite. 4) J'affiche mon sprite, en mode transparent. (routine de base, pas très rapide, normale, voir plus bas...) 5) En théorie, tant qu'il me restera des sprites à afficher, je bouclerai sur l'étape 2. 6) CALL &BD19 (Vsync) 7) Je restitue les morceaux de décors que j'ai capturés dans l'ordre inverse de celui où je les ai capturés. 8) Je met à zero mon compteur de sprites, et je recommence tout à l'étape 2.
Le problème que je rencontre, c'est que même avec un seul sprite affiché, lors de son déplacement horizontal, à peu près au milieu de l'écran, il se coupe en deux. Je comprend qu'il s'agit d'un mauvais usage du Vsync de ma part.
Si je supprime le Vsync, ça clignotte. Logique, puisque la routine qui restitue les décors (sans transparence) est presque aussi lente que celle qui affiche les sprites. Cela risque d'être pire quand je vais rajouter quelques sprites...
Du coup, je me demande ce que je dois faire. Comment faisait Claude Le Moullec, par exemple, pour faire ses jeux en BASIC/ASM sans que cela clignote dans tous les sens ?
Code :
; -------------------------------------------------------------------------------- ; Draw a 'Mode 1' transparent sprite ; Entry: B = width, C = height, DE = Sprite address, HL = screen address ; -------------------------------------------------------------------------------- CM_DrawSpriteTransparentMode1: ; -------------------------------------------------------------------------------- PUSH BC PUSH HL CM_DrawLineTransparentMode1: ; === PIXEL 1 === LD A, (DE) LD C, A AND #11 CP 0 JR Z, CM_Goto11End LD C, A LD A, (HL) AND #EE OR C LD (HL), A CM_Goto11End: ; === PIXEL 2 === LD A, (DE) LD C, A AND #22 CP 0 JR Z, CM_Goto22End LD C, A LD A, (HL) AND #DD OR C LD (HL), A CM_Goto22End: ; === PIXEL 3 === LD A, (DE) LD C, A AND #44 CP 0 JR Z, CM_Goto44End LD C, A LD A, (HL) AND #BB OR C LD (HL), A CM_Goto44End: ; === PIXEL 4 === LD A, (DE) LD C, A AND #88 CP 0 JR Z, CM_Goto88End LD C, A LD A, (HL) AND #77 OR C LD (HL), A CM_Goto88End: INC DE INC HL DJNZ CM_DrawLineTransparentMode1 POP HL POP BC DEC C RET Z LD A, H ADD #08 LD H, A AND #80 CP 0 JP NZ, CM_DrawSpriteTransparentMode1 PUSH DE LD DE, #C050 ADD HL, DE POP DE JP CM_DrawSpriteTransparentMode1
Inscription : 12 Juin 2008, 20:29 Message(s) : 1710
généralement ce que tu décris est provoqué par le fait qu'on change la mémoire de l'écran au moment où elle est affichée.
donc, il faut passer par un double buffer : tu travailles sur un écran 1 (exemple en &c000) pendant que l'écran 2 (exemple en &8000) est affiché et inversement après le call &bd19 (qui ne sert qu'à attendre le début d'affichage de l'écran pour faire simple) !
soit que ton code ne soit pas exécuter au moment de l'affichage
tu peux vérifier avec un émulateur type winape en pas à pas avec l'affichage de la position dans la frame, je crois que c'est une case à cocher sur le panneau de configuration/registres de mémoire !?
Je n'ai jamais compris la technique des masques. J'utilise d'ailleurs "Retro Game Asset Studio", qui me génère des sprites codés en assembleur, avec à la fin leurs masques, mais je n'ai pas trouvé d'exemple en .asm pour les tester. Si quelqu'un dispose d'un lien vers une routine pour afficher les sprites provenant de ce logiciel avec leurs masques... ça serait sympa de le mettre ici, merci.
@Megachur : Je suis d'accord, le double buffer serait le mieux, et j'y pense de plus en plus. Mais je ne comprend pas un truc. Comment commuter la mémoire vidéo en &4000 (ou &8000) sans perdre mes sprites, qui sont eux-même en &4000 ? J'imagine qu'il y a une astuce avec la mémoire étendue du 6128 ? Un lien serait le bienvenu là aussi, merci.
EDIT: Je viens de voir que je pouvais assembler mon .BIN principal en &389, et le lancer avec une ligne de programme BASIC. Avant, je croyais être limité à &1800. Donc, pour le problème de mémoire pour les sprites en &4000, c'est réglé, j'ai tout remanié. De &4000 à &7fff, c'est libre ! Je vais donc pouvoir bosser en double buffer.
Par contre, je me demande vraiment comment concilier la table de masques générée par "Retro Game Asset Studio" avec ton code source, marcel. Cela doit être facile, mais mon ignorance me joue des tours.
Pour précision, le logiciel en question fournit une table de 256 octets pour les masques. J'ai déjà entendu parler de cette fameuse table, mais je n'ai jamais trouvé un lien vers un code source pour l'exploiter.
Je vais chercher sur le site ici-même, hors forum, on ne sais jamais...
Ah alors c'est une table d'indirection si tu n'as qu'une table de 256 octets Il y a plein de techniques différentes pour les masques Mais dis moi, si tu n'est pas à l'aise avec l'assembleur (et les masques) pourquoi ne pas utiliser cpctelera par exemple?
Ce n'est pas l'assembleur qui me gène, c'est plutôt que je ne dispose pas de la documentation nécessaire sur la machine.
A l'époque, déjà, je devais me débrouiller avec quelques bouquins de micro-application (peek & pokes, les routines de l'amstrad cpc, et un de leurs bouquins sur l'assembleur), sans jamais trouver où acheter "la bible du cpc", par exemple.
J'ai vu pas mal de trucs défiler dans les revues du genre Amtrad Cent Pour Cent, mais je ne me suis pas penché sur tous les trucs de bidouilleurs de haut niveau de l'époque. Je me suis contenté d'expérimenter les bases.
Quant à maintenant, ce n'est pas que je veux renouer avec l'assembleur, mais plutôt que je suis sur un projet assez immense. Le principe est simple : On assemble visuellement des modules, chaque module contient un morceau de code, on fait un rendu du source global, et on lance le compilateur correspondant. J'ai commencé par faire des modules pour "AppGameKit 2" et pour "PureBasic", et cela rend bien. Puis, pour vérifier l'universalité de mon programme, je teste la génération d'un source ASM Amstrad CPC, lui-même ajouté par ligne de commande dans une disquette virtuelle, elle-même lancée dans un émulateur... et cela fonctionne !
Voici un screenshot de "CodeMug" (qui devrait être gratuit et open source) :
Faire du masquage n'a rien de spécialement spécifique à la machine (enfin pas plus que ton premier code)
En mode 0 t'as les bits du pixel gauche dans les bits impaires et ceux du pixel droit dans les bits pairs
octet=GDGDGDGD (je te passe l'ordre des bits, aucun intérêt pour du masquage)
Donc en admettant que la couleur 0 soit transparente, tu as 4 cas pour ton sprite
Et si ta table de 256 octets contient ce que je pense, ça donne ça
GDGDGDGD : 2 pixels utilisés -> le masque sera 00000000 pour enlever tous les pixels de l'écran G0G0G0G0: que le pixel gauche -> masque à 1010101 pour ne garder que le pixel droit de l'écran D0D0D0D: que le pixel droit -> 10101010 pour ne garder que le pixel gauche de l'écran 00000000: aucun pixel -> 11111111 pour garder tous les pixels de l'écran (vu que y en aura pas dans le sprite)
pour chaque octet de ton sprite tu n'as besoin que de quelques lignes d'assembleur, aucune comparaison ou saut...
de=adresse des données du sprite b=adresse de la table de conversion / 256 (il faut bien penser à aligner la table sur une adresse multiple de 256) hl=adresse de destination écran
ld a,(de) ; récupère l'octet du sprite dans a ld c,a ld a,(bc) ; récupère le masque dans la table en fonction de l'octet du sprite and (hl) ; enlève les pixels de l'écran en fonction des données du sprite or c ; fusionné avec le sprite ld (hl),a ; écrit à l'écran l'octet inc de inc hl ; incrémentation des adresses
reste à compter les pixels, revenir à la ligne, etc.
Ce qu'il y a de bien avec cette routine, c'est que ça prend le même temps pour du mode 0, mode 1 ou mode 2
Dernière édition par marcel le 01 Déc 2017, 11:02, édité 1 fois.
Donc en admettant que la couleur 0 soit transparente, tu as 4 cas pour ton sprite. Et si ta table de 256 octets contient ce que je pense, ça donne ça
Merci de ton aide précieuse. Si je comprend bien, on a besoin d'un octet de masque pour un octet du sprite ? Car j'ai l'impression que la table fournie par RGAS ne correspond pas, dans ce cas.
J'ai dessiné 16 sprites, de chacun 4x16 octets (16x16 pixels en Mode 1). Et j'ai malgré tout une seule table de 256 octets.
La voici, avec une petite phrase en Anglais en entête :
Code :
.maskLookupTable ; lookup table for masks, indexed by sprite byte. AND with screen data, then OR with pixel data. defb &FF,&EE,&DD,&CC,&BB,&AA,&99,&88,&77,&66,&55,&44,&33,&22,&11,&00,&EE,&EE,&CC,&CC,&AA,&AA,&88,&88,&66,&66,&44,&44,&22,&22,&00,&00 defb &DD,&CC,&DD,&CC,&99,&88,&99,&88,&55,&44,&55,&44,&11,&00,&11,&00,&CC,&CC,&CC,&CC,&88,&88,&88,&88,&44,&44,&44,&44,&00,&00,&00,&00 defb &BB,&AA,&99,&88,&BB,&AA,&99,&88,&33,&22,&11,&00,&33,&22,&11,&00,&AA,&AA,&88,&88,&AA,&AA,&88,&88,&22,&22,&00,&00,&22,&22,&00,&00 defb &99,&88,&99,&88,&99,&88,&99,&88,&11,&00,&11,&00,&11,&00,&11,&00,&88,&88,&88,&88,&88,&88,&88,&88,&00,&00,&00,&00,&00,&00,&00,&00 defb &77,&66,&55,&44,&33,&22,&11,&00,&77,&66,&55,&44,&33,&22,&11,&00,&66,&66,&44,&44,&22,&22,&00,&00,&66,&66,&44,&44,&22,&22,&00,&00 defb &55,&44,&55,&44,&11,&00,&11,&00,&55,&44,&55,&44,&11,&00,&11,&00,&44,&44,&44,&44,&00,&00,&00,&00,&44,&44,&44,&44,&00,&00,&00,&00 defb &33,&22,&11,&00,&33,&22,&11,&00,&33,&22,&11,&00,&33,&22,&11,&00,&22,&22,&00,&00,&22,&22,&00,&00,&22,&22,&00,&00,&22,&22,&00,&00 defb &11,&00,&11,&00,&11,&00,&11,&00,&11,&00,&11,&00,&11,&00,&11,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00
Il est à noter que "Retro Game Asset Studio" a été intégré à la suite "CPCTelera", mais que je ne trouve pas la documentation de RGAS.
Si je ne trouve rien pour n'utiliser que ces 256 octets, je pourrais programmer quelque chose pour créer un masque par octet, mais tel que j'ai fait les choses (avec le double buffer), je ne dispose que de 8k de libre pour les sprites. (&8000 à &9fff) Mon sprite prendrait déjà 2k { [ ( 4 x 16 ) x 16 ] x 2} sur 8k, à lui tout seul...
En fait la table est commune à tous les sprites On économise de la place C'est pour ça que la routine a besoin de trois pointeurs Un vers les données du sprite Un vers la table de conversion Un vers l'écran Tu peux toujours utiliser xh,xl du registre ix comme compteur pour largeur et hauteur
PS: le descriptif correspond bien à mon code. D'abord charger l'index vers la table grâce à la donnée du sprite. Ensuite le and et le or (que j'avais oublié j'ai corrigé et édité la routine au dessus
Utilisateur(s) parcourant ce forum : Aucun utilisateur inscrit et 6 invité(s)
Vous ne pouvez pas publier de nouveaux sujets dans ce forum Vous ne pouvez pas répondre aux sujets dans ce forum Vous ne pouvez pas éditer vos messages dans ce forum Vous ne pouvez pas supprimer vos messages dans ce forum Vous ne pouvez pas insérer de pièces jointes dans ce forum