Champ d'étoiles vertical [FR] / [ENG]
4 participants
GFA :: Programmation :: Graphismes
Page 1 sur 1
Champ d'étoiles vertical [FR] / [ENG]
Tuto : [Vous devez être inscrit et connecté pour voir ce lien] / [Vous devez être inscrit et connecté pour voir ce lien]
Hello, ya encore du monde ici ?
Pfiou, ça fait des années que je n'étais pas venu !
Bon, bref, sur le groupe Facebook de Lonny , un gars voulait faire un scrolling d'étoiles, j'ai fait un petit truc, du coup pourquoi ne pas le partager ici.... ( les comms sont en anglais, c'est plutôt facile mais bon, si ya des questions n'hésitez pas )
Hello, ya encore du monde ici ?
Pfiou, ça fait des années que je n'étais pas venu !
Bon, bref, sur le groupe Facebook de Lonny , un gars voulait faire un scrolling d'étoiles, j'ai fait un petit truc, du coup pourquoi ne pas le partager ici.... ( les comms sont en anglais, c'est plutôt facile mais bon, si ya des questions n'hésitez pas )
- Code:
RESERVE 10000
'
@inits
REPEAT
VSYNC
CARD{&HFFFF8240}=0 ! black backgroud
' clear oldstars
starz.pointer&=0
FOR i&=0 TO maxstar&-1
xadr&=CARD{ADD(old.starz%,starz.pointer&)}
yadr&=CARD{ADD(old.starz%,starz.pointer&+4)}
adr&=ADD(xadr&,yadr&)
CARD{ADD(log%,adr&)}=0 ! clear previous starz
ADD starz.pointer&,8
NEXT i&
'
' calc new coords and display star
starz.pointer&=0
FOR i&=0 TO maxstar&-1
yadr&=CARD{ADD(mid.starz%,starz.pointer&+4)}
yspeed&=CARD{ADD(mid.starz%,starz.pointer&+6)}
newy%=ADD(yadr&,yspeed&)
IF newy%>31999 ! outside screen
x&=RAND(319) ! new random star
xadr&=SHL&(SHR&(x&,4),3) ! address
xofst&=x& AND 15 ! offset
ADD xofst&,xofst& ! word
CARD{starz%+starz.pointer&}=xadr&
CARD{starz%+starz.pointer&+2}=xofst&
CARD{starz%+starz.pointer&+4}=0 ! new y adr=0
CARD{starz%+starz.pointer&+6}=(RAND(8)+1)*160 ! new y speed
'
CARD{ADD(log%,xadr&)}=CARD{ADD(pxlz%,xofst&)} OR CARD{ADD(log%,xadr&)} ! display new star
ADD starz.pointer&,8
ELSE
xadr&=CARD{ADD(mid.starz%,starz.pointer&)}
xofst&=CARD{ADD(mid.starz%,starz.pointer&+2)}
'
CARD{ADD(starz%,starz.pointer&)}=xadr&
CARD{ADD(starz%,starz.pointer&+2)}=xofst&
CARD{ADD(starz%,starz.pointer&+4)}=newy%
CARD{ADD(starz%,starz.pointer&+6)}=yspeed&
'
adr&=ADD(xadr&,newy%)
CARD{ADD(log%,adr&)}=CARD{ADD(pxlz%,xofst&)} OR CARD{ADD(log%,adr&)} ! display new star
ADD starz.pointer&,8
ENDIF
NEXT i&
tuch&=BYTE{&HFFFFFC02}
IF tuch&=&H38 ! Alt pressed
CARD{&HFFFF8240}=&HF ! show CPU left
ENDIF
SWAP mid.starz%,old.starz%
SWAP starz%,mid.starz%
SWAP phy%,log%
BYTE{&HFFFF8201}=SHR(phy%,16)
BYTE{&HFFFF8203}=SHR(phy%,8)
'
UNTIL tuch&=&H39 ! Spacebar pressed
'
@fin
'
PROCEDURE inits
super%=GEMDOS(32,L:0) ! supervisor mode
'
HIDEM
OUT 4,8 ! no mouse
rez|=XBIOS(4)
xb2%=XBIOS(2)
'
scrbuf%=MALLOC(64000+256)
phy%=AND(ADD(scrbuf%,255),&HFFFFFF00)
log%=ADD(phy%,32000)
RC_COPY phy%,0,0,320,400 TO phy%,0,0,0 ! clears both phy% and log% buffers
pxlz%=MALLOC(32+32) ! 16 words for pre decaled stars + 16 words palette
palette%=pxlz%+32
BMOVE &HFFFF8240,palette%,32 ! saves oldpal
BYTE{&HFFFF8260}=0 ! low REZ
~XBIOS(5,L:log%,L:phy%,-1) ! set screens
VSYNC
SETCOLOR 1,&H777
maxstar&=100
starzbuf%=MALLOC(maxstar&*8*3) ! (1 word X / 1 word offset / 1 word Y / 1 word speed)*3 buffers
mid.pointer&=maxstar&*8
old.pointer&=maxstar&*8*2
FOR i|=0 TO maxstar&-1
' s&(i|,1)=RND(1)*319
x&=RAND(319)
xadr&=SHL&(SHR&(x&,4),3) ! address
xofst&=x& AND 15 ! offset
ADD xofst&,xofst& ! word
CARD{starzbuf%+mid.pointer&}=xadr&
CARD{starzbuf%+mid.pointer&+2}=xofst&
CARD{starzbuf%+mid.pointer&+4}=RAND(199)*160 ! y adr
CARD{starzbuf%+mid.pointer&+6}=(RAND(8)+1)*160 ! y speed
'
CARD{starzbuf%+old.pointer&}=0
CARD{starzbuf%+old.pointer&+2}=0
CARD{starzbuf%+old.pointer&+4}=0
CARD{starzbuf%+old.pointer&+6}=0
ADD mid.pointer&,8
ADD old.pointer&,8
NEXT i|
' BMOVE ecran2%,bg%,32000
starz%=starzbuf%
mid.starz%=ADD(starzbuf%,maxstar&*8)
old.starz%=ADD(starzbuf%,maxstar&*8*2)
mystar%=&X1000000000000000
CARD{pxlz%}=mystar% ! x=0
FOR i&=2 TO 30 STEP 2
mystar%=SHR(mystar%,1) ! 1 pixel shift to the right
CARD{ADD(pxlz%,i&)}=mystar%
NEXT i&
RETURN
'
PROCEDURE fin
SHOWM
OUT 4,12 ! mouse alive
~MFREE(scrbuf%)
~MFREE(starzbuf%)
BMOVE palette%,&HFFFF8240,32 ! restore old palette
~XBIOS(5,L:xb2%,L:xb2%,rez|)
~GEMDOS(32,L:super%) ! user mode
EDIT
RETURN
Dernière édition par tomchi le Sam 21 Nov - 9:35, édité 1 fois
tomchi- Messages : 52
Date d'inscription : 15/09/2018
Age : 49
Re: Champ d'étoiles vertical [FR] / [ENG]
Merci Tomchi ,
Le forum a besoin de se réveiller, si tu as encore d'autres bouts de code n'hésites pas
Le forum a besoin de se réveiller, si tu as encore d'autres bouts de code n'hésites pas
ManuM- Messages : 171
Date d'inscription : 01/01/2018
Localisation : Loir et Cher
Re: Champ d'étoiles vertical [FR] / [ENG]
Salut les gars !
C'est moi le gars sur le groupe Facebook de Lonny qui voulait mettre en place un scrolling d'étoiles.
Merci pour ta solution Tomchi, elle est élégante
C'est moi le gars sur le groupe Facebook de Lonny qui voulait mettre en place un scrolling d'étoiles.
Merci pour ta solution Tomchi, elle est élégante
Shaoth- Messages : 64
Date d'inscription : 28/09/2020
Age : 54
Localisation : Courbevoie
Re: Champ d'étoiles vertical [FR] / [ENG]
Cool !
On est en train de faire quelques tests avec Thomas (randomizer) et sur des bouts de code, finalement CARD n'est pas vriament ( du tout) plus rapide que d'utiliser un talbleau donc, même plus élégant le code pourrait afficher pas mal d'étoiles
On est en train de faire quelques tests avec Thomas (randomizer) et sur des bouts de code, finalement CARD n'est pas vriament ( du tout) plus rapide que d'utiliser un talbleau donc, même plus élégant le code pourrait afficher pas mal d'étoiles
tomchi- Messages : 52
Date d'inscription : 15/09/2018
Age : 49
Re: Champ d'étoiles vertical [FR] / [ENG]
Je suis très curieux de voir ça.
J'avais tenté d'utiliser un tableau pour stocker les coordonnées des étoiles mais ce n'était pas du tout convainquant...
J'avais tenté d'utiliser un tableau pour stocker les coordonnées des étoiles mais ce n'était pas du tout convainquant...
Shaoth- Messages : 64
Date d'inscription : 28/09/2020
Age : 54
Localisation : Courbevoie
Re: Champ d'étoiles vertical [FR] / [ENG]
le forum est en vie ! merci pour ce starfield
ManuM- Messages : 171
Date d'inscription : 01/01/2018
Localisation : Loir et Cher
Jb aime ce message
Re: Champ d'étoiles vertical [FR] / [ENG]
un scrolling d'étoiles c'est basique...
je fais ça les doigts dans le nez dans n'importe quel langage moderne.
moderne au sens d'utiliser aujourd'hui pour les jeux.
Mais ramené 30 ans en arrière c'est moins simple.
il faut bien mieux connaitre la machine et ne plus compter sur la puissance brute des ordinateurs actuels, ce que j'ai encore trop tendance à faire, mais j'évolue ! (ou je régresse, ça dépend de comment on le prend )
je fais ça les doigts dans le nez dans n'importe quel langage moderne.
moderne au sens d'utiliser aujourd'hui pour les jeux.
Mais ramené 30 ans en arrière c'est moins simple.
il faut bien mieux connaitre la machine et ne plus compter sur la puissance brute des ordinateurs actuels, ce que j'ai encore trop tendance à faire, mais j'évolue ! (ou je régresse, ça dépend de comment on le prend )
Shaoth- Messages : 64
Date d'inscription : 28/09/2020
Age : 54
Localisation : Courbevoie
Re: Champ d'étoiles vertical [FR] / [ENG]
Jamais touché un "langage moderne" ici , du coup on doit être complémentaire, faudra m'aider
Bon, ptit teaser juste pour moi : il manque vraiment pas grand chose pour arriver à 150 étoiles, aller aller aller !!
Bon, ptit teaser juste pour moi : il manque vraiment pas grand chose pour arriver à 150 étoiles, aller aller aller !!
tomchi- Messages : 52
Date d'inscription : 15/09/2018
Age : 49
Jb aime ce message
Re: Champ d'étoiles vertical [FR] / [ENG]
Merci Tomchi de prendre la relève.
Je suis un peu ko depuis un an du à de graves ennuis de santé, déjà 4 opérations et il y en a encore une prévue ce 8 octobre.
J'espère pouvoir m'y remettre plus sereinement après.
Je suis un peu ko depuis un an du à de graves ennuis de santé, déjà 4 opérations et il y en a encore une prévue ce 8 octobre.
J'espère pouvoir m'y remettre plus sereinement après.
Re: Champ d'étoiles vertical [FR] / [ENG]
Hello Georges !!
Ça fait plaisir de te lire, merci
Bon courage et prompt rétablissement !
( ton absence de post depuis pas mal de temps m'a inquiété quand je suis revenu faire un tour sur le forum, mais je n'ai pas osé demander de tes nouvelles, desolé )
Ça fait plaisir de te lire, merci
Bon courage et prompt rétablissement !
( ton absence de post depuis pas mal de temps m'a inquiété quand je suis revenu faire un tour sur le forum, mais je n'ai pas osé demander de tes nouvelles, desolé )
tomchi- Messages : 52
Date d'inscription : 15/09/2018
Age : 49
Re: Champ d'étoiles vertical [FR] / [ENG]
Le peu de connaissances que j'ai en programmation concerne le GFA Basic et vient de la
lecture des articles lus dans le magasine ST Mag. Ce qui suit est facile et basique, toute
contribution (utile) est la bienvenue.
Ok, c'est parti pour un petit Starfield !
Le principe va être d'effacer les étoiles affichées précédemment puis de calculer leurs
nouvelles ordonnées en ajoutant la vitesse à y pour enfin les afficher.
En pseudo code ça donne:
On voit donc qu'il faut stocker les coordonnées de chaque étoile pour l'effacer à son
ancienne position avant de l'afficher à la nouvelle position.
On se rend compte que du coup il faudra définir la position initiale de chaque étoile
avant que la boucle principale ne tourne.
La manière la plus évidente/simple en GFA de gérer ça sera d'utiliser un tableau : nous
definiront la constante maxstar& qui stockera le nombre d'étoiles que nous déplaceront.
D'après le pseudo code il nous faut un tableau à deux dimensions pour stocker les
coordonnées et la vitesse de chaque étoile.
star&(blah&,0)=x&
star&(blah&,1)=y&
star&(blah&,2)=speed&
Où blah& est le numéro de l'étoile dont nous nous occupons.
Nous y reviendront mais utiliser ce tableau à deux dimensions bien qu'étant suffisant pour
afficher quelques points, se révèle peu performant. ( En tout cas il y a une autre façon
facile de faire beaucoup plus efficace.)
Pour l'affichage / effaçage des points nous n'utiliserons *pas* PLOT, simplement parce que
nous voulons un résultat propre, rapide et fluide.
Il nous faut avoir au moins une idée rudimentaire de l'organisation des graphismes sur le
ST.
On va travailler en basse résolution cad 320*200 pixels avec 16 couleurs.
Les graphismes sont sockés par bloc de 16 pixels sur 8 octets : 320 pixels sont donc
stockés dans 160 octets , * 200 lignes = 32000 octets pour la taille d'un écran donc.
Pour obtenir les 16 couleurs, les pixels sont codés en 4 plans, on a donc chaque mot qui
stocke les 16 pixels pour chaque plan.
on comprend que la couleur 0 n'utilise aucun plan, la couleur 1 utilise le plan 1, la
couleur 2 le plan 2, la couleur 3 les plans 1 et 2, etc ...
Disons qu'on veut afficher le 3ème pixel en partant de la gauche avec la couleur 13 il
nous faudra écrire :
&X0010000000000000 à address%
&X0000000000000000 à address%+2 ( 2nd mot )
&X0010000000000000 à address%+4 ( 3ème mot )
&X0010000000000000 à address%+6 ( 4ème mot )
Pour afficher les étoiles avec la couleur 1 nous n'aurons qu'à écrire un seul mot dans la
mémoire écran. Comme il est fort probable que plusieurs étoiles se trouvent sur le même
bloc de 16 pixels nous utiliseront l'opérande OR pour "plotter" chaque étoile afin de ne
pas en effacer une qu'on aurait déjà affichée sur le même mot.
Pour gagner du temps machine on va précalculer les 16 positions d'un pixel dans un autre
tableau.
Pour écrire ces mots à l'écran on utilisera la commande CARD:
Effacer sera encore plus simple, il suffira d'écrire 0 .
On gagnera du temps machine si au lieu de travailler avec des coordonnées on utilise
directement les adresses.
Convertir x en adresse : x.address&=SHL(SHR(x&,4),3)
=> Encore un tableau pour stocker ces adresses:
Convertir y en addresse : y.address&=y&*160
Vitesse en addresse : y.speed&=speed&*160
On aura donc:
Si on écrit à l'écran pendant que le ST rafraichit ce dernier on aura des bugs
d'affichage, pour eviter cela nous allons recourir à l'éternelle technique du double
buffer:
Pendant que le ST affiche l'écran (ecran physique), nous ecrirons dans un second buffer
identique en taille=32000 octets (écran logique), ensuite nouos mettrons à jour l'adresse
vidéo après avoir SWAPpé (=échangé) les adresses écran logique et physique et utiliserons
VSYNC pour attendre la fin du balayage et passage à la prochaine VBL.
Notre routine, avant d'afficher les nouvelles étoiles doit les effacer, mais il faut
maintenant prendre en compte le double buffering .
En effet, il nous faudra connaitre la position de chaque étoile que nous voudrons effacer
non pas une mais deux VBL plus tôt :
1ère VBL: affiche buffer 1, on écrit dans le buffer 2 l'étoile est à y.
2ème VBL: affiche buffer 2, on écrit dans le buffer 1 l'étoile est à y+yspeed
3ème VBL: affiche buffer 1, on écrit dans le buffer 2, l'étoile est à y+yspeed+yspeed
Comme la routine peut avoir créé une nouvelle étoile dans le cas où la précédente serait
sortie de l'écran nous ne pouvons pas nous contenter de soustraire 2*yspeed, au lieu de
cela nous utiliserons (encore) 3 tableaux et utiliseront SWAP pour les intervertir, comme
pour les adresses écran.
Assez de blabla basico théorique, passons au code complet.
Quelques mots à propose de PROCEDURE inits:
Le programme avant de tourner vous demandera combien d'étoiles vous souhaitez qu'il
affiche, le maximum en 1 VBL (programme compilé) est d'environ 75.
On utilisera le mode superviseur afin d'écrire directement dans certains registres mémoire
du ST.
Le buffer alloué avec MALLOC sera effacé (rempli de 0), evitant que quelque donnée
résidant en mémoire ne se situe dans ledit bloc et vienne parasiter nos données.
Le pus facile/rapide en GFA est d'utiliser RC_COPY.
Certains pourront remarquer qu'il n'y a pas d'appel à la fonction XBIOS(5) (qui sert à
mettre en place les adresses écran logique et physique) dans la boucle principale mais
écriture dans les registres mémoire correspondants.
Il n'est pas du tout indispensable de procéder de cette façon mais comme mes programmes
sont principalement destinés au STE, j'ai pris l'habitude de coder de cette manière.
Appuyer sur Alternate pendant que le programme tourne changera la couleur de fond en bleu,
la partie de l'écran en bleu représentant le temps machine encore disponible avant la fin
de la VBL.
J'ai écrit cet article après des échanges constructifs avec Thomas, l'auteur de plusieurs
jeux en GFA (anarcho ride / frogs / randomazer ...).
Ma première routine utilisait CARD pour lire/écrire dans les buffers au lieu d'utiliser
les tableaux.
On a pas mal discuté sur la rapidité en temps machine de ces tableaux et Thomas a proposé
une routine plus rapide que celle que j'avais écrite en utilisant des tableaux à une
dimension. Après tests il s'avère que les tableaux à deux dimensions prenaient énormément
plus de temps que d'utiliser plusieurs tableaux simples. Bien que cela lui convienne
parfaitement, j'avaonçais que ça faisait trop de tableaux/variables à gérer pour moi. Il
faut un tableau pour x adr, x ofst, y, yspeed et tout cela multiplié par 3 (
current/mid/old).
L'intégriste du CARD que je suis se mit en tête d'optimiser la routine originale tirant
parti de chaque technique. Le code qui suit affichera 150 étoiles chaque VBL.
Le but étant d'éviter les calculs (additions) au maximum.
Au lieu d'utiliser plusieurs tableaux, les données seront stockées dans un unique buffer:
Un seul buffer donc, et des pointeurs pour chaque "buffer dans le buffer".
La quantité de mémoire nécessaire est alors maxstar&*8*3: on a besoin de stocker current /
mid / old positions des étoiles (=>*3) et un bloc de data pour une étoile fait 8 octets
(4*2).
Les pointeurs:
dernier point, au lieu d'utiliser une boucle FOR/NEXT j'ai opté pour REPEAT/UNTIL,
définissant une variable qui pointe dans le buffer dans lequel je veux taper et lui
ajoutant 8 à chaque étoile jusqu'à ce qu'elle pointe à la fin du buffer.
lecture des articles lus dans le magasine ST Mag. Ce qui suit est facile et basique, toute
contribution (utile) est la bienvenue.
Ok, c'est parti pour un petit Starfield !
Le but : Un programme facilement lisible et modifiable qui doit tourner à 50 fps compilé.
Des étoiles en déplacement vertical ayant des vitesses différentes.
Chaque étoile sortant de l'écran sera remplacée par une nouvelle générée
aléatoirement.
Le principe va être d'effacer les étoiles affichées précédemment puis de calculer leurs
nouvelles ordonnées en ajoutant la vitesse à y pour enfin les afficher.
En pseudo code ça donne:
- Code:
REPEAT
VSYNC ! rafraichissement ecran
FOR star&=1 TO maxstar&
erase(star&) ! a coder
NEXT star&
'
FOR star&=1 TO maxstar&
ADD y.star&,yspeed& ! a
display(star&) ! coder
NEXT star&
UNTIL space_pressed
On voit donc qu'il faut stocker les coordonnées de chaque étoile pour l'effacer à son
ancienne position avant de l'afficher à la nouvelle position.
On se rend compte que du coup il faudra définir la position initiale de chaque étoile
avant que la boucle principale ne tourne.
La manière la plus évidente/simple en GFA de gérer ça sera d'utiliser un tableau : nous
definiront la constante maxstar& qui stockera le nombre d'étoiles que nous déplaceront.
D'après le pseudo code il nous faut un tableau à deux dimensions pour stocker les
coordonnées et la vitesse de chaque étoile.
- Code:
maxstar&=50
DIM star&(maxstar&,2)
star&(blah&,0)=x&
star&(blah&,1)=y&
star&(blah&,2)=speed&
Où blah& est le numéro de l'étoile dont nous nous occupons.
Remarquez le "&" : il indique au GFA que nous voulons travailler avec des mots/word (-
32768;...;32767). Premièrement parce que nos valeurs (x& par exemple) pourront être plus
grandes que ce qu'un octet/byte ne peut contenir ("|"), deuxièmement parce que le ST
travaille plus vite ( à fortiori en GFA ) avec des mots qu'avec des longs mots ("%") ou
pire des nombres à virgule.
Nous y reviendront mais utiliser ce tableau à deux dimensions bien qu'étant suffisant pour
afficher quelques points, se révèle peu performant. ( En tout cas il y a une autre façon
facile de faire beaucoup plus efficace.)
Pour l'affichage / effaçage des points nous n'utiliserons *pas* PLOT, simplement parce que
nous voulons un résultat propre, rapide et fluide.
Il nous faut avoir au moins une idée rudimentaire de l'organisation des graphismes sur le
ST.
On va travailler en basse résolution cad 320*200 pixels avec 16 couleurs.
Les graphismes sont sockés par bloc de 16 pixels sur 8 octets : 320 pixels sont donc
stockés dans 160 octets , * 200 lignes = 32000 octets pour la taille d'un écran donc.
Pour obtenir les 16 couleurs, les pixels sont codés en 4 plans, on a donc chaque mot qui
stocke les 16 pixels pour chaque plan.
- Code:
--------------------------------------------------------
! n° ! bin ! plan 1 ! plan 2 ! plan 3 ! plan 4 !
! 00 ! 0000 ! non ! non | non ! non !
! 01 ! 0001 ! oui ! non ! non ! non !
! 02 ! 0010 ! non ! oui ! non ! non !
! 03 ! 0011 ! oui ! oui ! non ! non !
! 04 ! 0100 ! non ! non ! oui ! non !
! 05 ! 0101 ! oui ! non ! oui ! non !
! 06 ! 0110 ! non ! oui ! oui ! non !
! 07 ! 0111 ! oui ! oui ! oui ! non !
! 08 ! 1000 ! non ! non ! non ! oui !
! 09 ! 1001 ! oui ! non ! non ! oui !
! 10 ! 1010 ! non ! oui ! non ! oui !
! 11 ! 1011 ! oui ! oui ! non ! oui !
! 12 ! 1100 ! non ! non ! oui ! oui !
! 13 ! 1101 ! oui ! non ! oui ! oui !
! 14 ! 1110 ! non ! oui ! oui ! oui !
! 15 ! 1111 ! oui ! oui ! oui ! oui !
--------------------------------------------------------
on comprend que la couleur 0 n'utilise aucun plan, la couleur 1 utilise le plan 1, la
couleur 2 le plan 2, la couleur 3 les plans 1 et 2, etc ...
Disons qu'on veut afficher le 3ème pixel en partant de la gauche avec la couleur 13 il
nous faudra écrire :
&X0010000000000000 à address%
&X0000000000000000 à address%+2 ( 2nd mot )
&X0010000000000000 à address%+4 ( 3ème mot )
&X0010000000000000 à address%+6 ( 4ème mot )
"&X" indique au GFA que nous lui fournissons des valeurs binaires, pour des valeurs
héxadécimales on indiquera "&H" )
Pour afficher les étoiles avec la couleur 1 nous n'aurons qu'à écrire un seul mot dans la
mémoire écran. Comme il est fort probable que plusieurs étoiles se trouvent sur le même
bloc de 16 pixels nous utiliseront l'opérande OR pour "plotter" chaque étoile afin de ne
pas en effacer une qu'on aurait déjà affichée sur le même mot.
Pour gagner du temps machine on va précalculer les 16 positions d'un pixel dans un autre
tableau.
- Code:
DIM pxlz&(15)
pxlz&(0)=-32768 !&X1000000000000000=&H8000=-32768
mystar&=&X0100000000000000
pxlz&(1)=mystar&
FOR i&=2 TO 15 !
mystar&=SHR(mystar&,1) ! decalage d'un pixel sur la droite
pxlz&(i&)=mystar&
NEXT i&
Pour écrire ces mots à l'écran on utilisera la commande CARD:
- Code:
CARD{screen_address%}=pxlz&(blah&) OR CARD{screen_address%}
Effacer sera encore plus simple, il suffira d'écrire 0 .
On gagnera du temps machine si au lieu de travailler avec des coordonnées on utilise
directement les adresses.
Convertir x en adresse : x.address&=SHL(SHR(x&,4),3)
=> Encore un tableau pour stocker ces adresses:
- Code:
DIM xadr.ar&(320)
for i&=0 to 319
xadr.ar&(i&)=SHL(SHR(i&,4),3)
next i&
Convertir y en addresse : y.address&=y&*160
Vitesse en addresse : y.speed&=speed&*160
On aura donc:
- Code:
CARD{screen_address%+x.address&+y.address&}=pxlz&(xofst&) OR CARD{screen_address%
+x.address&+y.address&}
Si on écrit à l'écran pendant que le ST rafraichit ce dernier on aura des bugs
d'affichage, pour eviter cela nous allons recourir à l'éternelle technique du double
buffer:
Pendant que le ST affiche l'écran (ecran physique), nous ecrirons dans un second buffer
identique en taille=32000 octets (écran logique), ensuite nouos mettrons à jour l'adresse
vidéo après avoir SWAPpé (=échangé) les adresses écran logique et physique et utiliserons
VSYNC pour attendre la fin du balayage et passage à la prochaine VBL.
- Code:
scrbuf%=MALLOC(64000+256) ! Alloue la memoire pour 2 ecrans
phy%=AND(ADD(scrbuf%,255),&HFFFFFF00) ! adresse ecran physique
log%=ADD(phy%,32000) ! adresse ecran logique
Sur STf l'adresse écran doit se situer sur une adresse multiple de 256 ce qui explique le
+256 sur la taille mémoire allouée et le fait que l'on mette l'octet de phy% à zéro (
AND&HFFFFFF00).
Notre routine, avant d'afficher les nouvelles étoiles doit les effacer, mais il faut
maintenant prendre en compte le double buffering .
En effet, il nous faudra connaitre la position de chaque étoile que nous voudrons effacer
non pas une mais deux VBL plus tôt :
1ère VBL: affiche buffer 1, on écrit dans le buffer 2 l'étoile est à y.
2ème VBL: affiche buffer 2, on écrit dans le buffer 1 l'étoile est à y+yspeed
3ème VBL: affiche buffer 1, on écrit dans le buffer 2, l'étoile est à y+yspeed+yspeed
Comme la routine peut avoir créé une nouvelle étoile dans le cas où la précédente serait
sortie de l'écran nous ne pouvons pas nous contenter de soustraire 2*yspeed, au lieu de
cela nous utiliserons (encore) 3 tableaux et utiliseront SWAP pour les intervertir, comme
pour les adresses écran.
- Code:
DIM current.star&(maxstar&,3)
DIM middle.star&(maxstar&,3)
DIM old.star&(maxstar&,3)
Assez de blabla basico théorique, passons au code complet.
Quelques mots à propose de PROCEDURE inits:
Le programme avant de tourner vous demandera combien d'étoiles vous souhaitez qu'il
affiche, le maximum en 1 VBL (programme compilé) est d'environ 75.
On utilisera le mode superviseur afin d'écrire directement dans certains registres mémoire
du ST.
Le buffer alloué avec MALLOC sera effacé (rempli de 0), evitant que quelque donnée
résidant en mémoire ne se situe dans ledit bloc et vienne parasiter nos données.
Le pus facile/rapide en GFA est d'utiliser RC_COPY.
Certains pourront remarquer qu'il n'y a pas d'appel à la fonction XBIOS(5) (qui sert à
mettre en place les adresses écran logique et physique) dans la boucle principale mais
écriture dans les registres mémoire correspondants.
Il n'est pas du tout indispensable de procéder de cette façon mais comme mes programmes
sont principalement destinés au STE, j'ai pris l'habitude de coder de cette manière.
Appuyer sur Alternate pendant que le programme tourne changera la couleur de fond en bleu,
la partie de l'écran en bleu représentant le temps machine encore disponible avant la fin
de la VBL.
- Code:
RESERVE 10000
'
@inits
REPEAT
VSYNC
CARD{&HFFFF8240}=0 ! fond noir
' clear oldstars
FOR i&=0 TO maxstar&
xadr&=old.star&(i&,0)
yadr&=old.star&(i&,2)
CARD{ADD(log%,ADD(xadr&,yadr&))}=0 ! efface les anciennes etoiles
NEXT i&
'
' calc new coords and display star
FOR i&=0 TO maxstar&
yadr&=middle.star&(i&,2)
yspeed&=middle.star&(i&,3)
newy%=ADD(yadr&,yspeed&)
IF newy%>31999 ! hors de l'ecran
x&=RAND(319) ! nouvelle etoile randomisee
xadr&=xadr.ar&(x&)
xofst&=x& AND 15 ! offset
current.star&(i&,0)=xadr&
current.star&(i&,1)=xofst&
current.star&(i&,2)=0 ! y nouvelle etoile = 0
current.star&(i&,3)=(RAND(8)+1)*160 ! y speed
'
dest%=ADD(log%,xadr&) ! y=0 donc address=x address
CARD{dest%}=CARD{dest%} OR pxlz&(xofst&)
ELSE
xadr&=middle.star&(i&,0)
xofst&=middle.star&(i&,1)
'
current.star&(i&,0)=xadr&
current.star&(i&,1)=xofst&
current.star&(i&,2)=newy%
current.star&(i&,3)=yspeed&
'
dest%=ADD(log%,ADD(xadr&,newy%))
CARD{dest%}=CARD{dest%} OR pxlz&(xofst&)
ENDIF
NEXT i&
tuch&=BYTE{&HFFFFFC02}
IF tuch&=&H38 ! appui sur Alt
CARD{&HFFFF8240}=&HF ! CPU restant
ENDIF
SWAP middle.star&(),old.star&() ! swap stars data arrays old=middle
SWAP current.star&(),middle.star&() ! middle=current
SWAP phy%,log%
BYTE{&HFFFF8201}=SHR(phy%,16)
BYTE{&HFFFF8203}=SHR(phy%,8)
'
UNTIL tuch&=&H39 ! Spacebar pressed
'
@fin
'
PROCEDURE inits
CLS
INPUT "Max Stars",maxstar& ! combien d'etoiles ? screen
DIM pxlz&(15) ! tableau des 16 positions d'un pixel
DIM xadr.ar&(320) ! tableau conversion X en adresse
DIM current.star&(maxstar&,3) ! tableau stockant X adresse/ X ofset/ Y
adresse / Y speed pour chaque etoile
DIM middle.star&(maxstar&,3) ! meme chose
DIM old.star&(maxstar&,3) ! idem <= on swappera ces 3 tableaux
pour avoir les anciennes positions des etoiles
scrbuf%=MALLOC(64000+256+32) ! 1 buffer pour les 2 ecrans +
sauvegarde de la palette
phy%=AND(ADD(scrbuf%,255),&HFFFFFF00) ! phy% sur un octet nul
log%=ADD(phy%,32000) ! log% 32000 octets plus loin
palette%=ADD(starzbuf%,64000) ! palette% apres log%
super%=GEMDOS(32,L:0) ! supervisor mode
'
HIDEM
BYTE{&HFFFFFC02}=&H12 ! no mouse
rez|=XBIOS(4) ! resolution sauvee
xb2%=XBIOS(2) ! adresse ecran sauvee
'
for i&=0 to 319
xadr.ar&(i&)=SHL(SHR(i&,4),3) ! conversion X en adresse
next i&
BMOVE &HFFFF8240,palette%,32 ! ancienne palette sauvee
BYTE{&HFFFF8260}=0 ! basse resolution
VSYNC
~XBIOS(5,L:log%,L:phy%,-1) ! met en place les ecrans
VSYNC
RC_COPY phy%,0,0,320,400 TO phy%,0,0,0 ! effaceles buffers phy% et log%
VSYNC
SETCOLOR 1,&H777 ! etoiles blanches, oh my god !
FOR i&=0 TO maxstar&
x&=RAND(319)
xadr&=SHL(SHR(x&,4),3) ! adresse
xofst&=x& AND 15 ! offset
middle.star&(i&,0)=xadr&
middle.star&(i&,1)=xofst&
middle.star&(i&,2)=RAND(199)*160 ! y adr
middle.star&(i&,3)=(RAND(8)+1)*160 ! y speed
'
old.star&(i&,0)=0
old.star&(i&,1)=0
old.star&(i&,2)=0
old.star&(i&,3)=0
NEXT i&
pxlz&(0)=-32768 ! -32768=&H8000=&X1000000000000000
mystar&=&X0100000000000000
pxlz&(1)=mystar&
FOR i&=2 TO 15
mystar&=SHR(mystar&,1) ! 1 pixel decale a droite
pxlz&(i&)=mystar&
NEXT i&
RETURN
'
PROCEDURE fin
SHOWM
BYTE{&HFFFFFC02}=&H8 ! rend la souris ! mouse alive
~MFREE(scrbuf%)
BMOVE palette%,&HFFFF8240,32 ! remet ancienne palette
~XBIOS(5,L:xb2%,L:xb2%,rez|)
~GEMDOS(32,L:super%) ! user mode
EDIT
RETURN
J'ai écrit cet article après des échanges constructifs avec Thomas, l'auteur de plusieurs
jeux en GFA (anarcho ride / frogs / randomazer ...).
Ma première routine utilisait CARD pour lire/écrire dans les buffers au lieu d'utiliser
les tableaux.
On a pas mal discuté sur la rapidité en temps machine de ces tableaux et Thomas a proposé
une routine plus rapide que celle que j'avais écrite en utilisant des tableaux à une
dimension. Après tests il s'avère que les tableaux à deux dimensions prenaient énormément
plus de temps que d'utiliser plusieurs tableaux simples. Bien que cela lui convienne
parfaitement, j'avaonçais que ça faisait trop de tableaux/variables à gérer pour moi. Il
faut un tableau pour x adr, x ofst, y, yspeed et tout cela multiplié par 3 (
current/mid/old).
L'intégriste du CARD que je suis se mit en tête d'optimiser la routine originale tirant
parti de chaque technique. Le code qui suit affichera 150 étoiles chaque VBL.
Le but étant d'éviter les calculs (additions) au maximum.
Au lieu d'utiliser plusieurs tableaux, les données seront stockées dans un unique buffer:
- Code:
INPUT "Max Stars",maxstar&
DIM pxlz&(15)
DIM xadr.ar&(320)
scrbuf%=MALLOC(64000+256+maxstar&*8*3+32)
phy%=AND(ADD(scrbuf%,255),&HFFFFFF00)
log%=ADD(phy%,32000)
starzbuf%=ADD(log%,32000) !MALLOC(maxstar&*8*3) (1 mot X / 1 mot offset / 1
mot Y / 1 mot speed)*3 buffers
palette%=ADD(starzbuf%,maxstar&*8*3) !MALLOC(32)
Un seul buffer donc, et des pointeurs pour chaque "buffer dans le buffer".
La quantité de mémoire nécessaire est alors maxstar&*8*3: on a besoin de stocker current /
mid / old positions des étoiles (=>*3) et un bloc de data pour une étoile fait 8 octets
(4*2).
Les pointeurs:
- Code:
starz%=starzbuf%
mid.starz%=ADD(starzbuf%,maxstar&*8)
old.starz%=ADD(starzbuf%,maxstar&*8*2)
dernier point, au lieu d'utiliser une boucle FOR/NEXT j'ai opté pour REPEAT/UNTIL,
définissant une variable qui pointe dans le buffer dans lequel je veux taper et lui
ajoutant 8 à chaque étoile jusqu'à ce qu'elle pointe à la fin du buffer.
- Code:
RESERVE 10000
'
@inits
REPEAT
VSYNC
CARD{&HFFFF8240}=0 ! black backgroud
' clear oldstars
old.starz.pointer%=old.starz%
old.starz.pointer.max%=ADD(old.starz%,maxstar.lg&)
REPEAT
xadr&=CARD{old.starz.pointer%}
yadr&=CARD{ADD(old.starz.pointer%,4)}
CARD{ADD(log%,ADD(xadr&,yadr&))}=0 ! clear previous starz
ADD old.starz.pointer%,8
UNTIL old.starz.pointer%=old.starz.pointer.max%
'
' calc new coords and display star
starz.pointer%=starz%
starz.pointer.max%=ADD(starz%,maxstar.lg&)
mid.starz.pointer%=mid.starz%
REPEAT
yadr&=CARD{ADD(mid.starz.pointer%,4)}
yspeed&=CARD{ADD(mid.starz.pointer%,6)}
newy%=ADD(yadr&,yspeed&)
IF newy%>31999 ! outside screen
x&=RAND(319) ! new random star
xadr&=xadr.ar&(x&)
xofst&=x& AND 15 ! offset
CARD{starz.pointer%}=xadr&
CARD{ADD(starz.pointer%,2)}=xofst&
CARD{ADD(starz.pointer%,4)}=0
CARD{ADD(starz.pointer%,6)}=MUL(ADD(RAND(8),1),160) !MUL(ADD(x& AND 7,1),160) !
(RAND(8)+1)*160
'
dest%=ADD(log%,xadr&)
CARD{dest%}=CARD{dest%} OR pxlz&(xofst&)
ADD starz.pointer%,8
ADD mid.starz.pointer%,8
ELSE
xadr&=CARD{mid.starz.pointer%}
xofst&=CARD{ADD(mid.starz.pointer%,2)}
'
CARD{starz.pointer%}=xadr&
CARD{ADD(starz.pointer%,2)}=xofst&
CARD{ADD(starz.pointer%,4)}=newy%
CARD{ADD(starz.pointer%,6)}=yspeed&
'
dest%=ADD(log%,ADD(xadr&,newy%))
CARD{dest%}=CARD{dest%} OR pxlz&(xofst&)
ADD starz.pointer%,8
ADD mid.starz.pointer%,8
ENDIF
UNTIL starz.pointer%=starz.pointer.max%
tuch&=BYTE{&HFFFFFC02}
IF tuch&=&H38 ! Alt pressed
CARD{&HFFFF8240}=&HF ! show CPU left
ENDIF
SWAP mid.starz%,old.starz%
SWAP starz%,mid.starz%
SWAP phy%,log%
BYTE{&HFFFF8201}=SHR(phy%,16)
BYTE{&HFFFF8203}=SHR(phy%,8)
'
UNTIL tuch&=&H39 ! Spacebar pressed
'
@fin
'
PROCEDURE inits
CLS
INPUT "Max Stars",maxstar&
DIM pxlz&(15)
DIM xadr.ar&(320)
scrbuf%=MALLOC(64000+256+maxstar&*8*3+32)
phy%=AND(ADD(scrbuf%,255),&HFFFFFF00)
log%=ADD(phy%,32000)
starzbuf%=ADD(log%,32000) !MALLOC(maxstar&*8*3) ! (1 word X /
1 word offset / 1 word Y / 1 word speed)*3 buffers
palette%=ADD(starzbuf%,maxstar&*8*3) !MALLOC(32)
super%=GEMDOS(32,L:0) ! supervisor mode
'
HIDEM
BYTE{&HFFFFFC02}=&H12 ! plus de souris ! no mouse
rez|=XBIOS(4)
xb2%=XBIOS(2)
'
for i&=0 to 319
xadr.ar&(i&)=SHL(SHR(i&,4),3)
next i&
BMOVE &HFFFF8240,palette%,32 ! saves oldpal
BYTE{&HFFFF8260}=0 ! low REZ
VSYNC
~XBIOS(5,L:log%,L:phy%,-1) ! set screens
VSYNC
RC_COPY phy%,0,0,320,400 TO phy%,0,0,0 ! clears both phy% and log% buffers
VSYNC
SETCOLOR 1,&H777
mid.pointer&=maxstar&*8
old.pointer&=maxstar&*8*2
FOR i|=0 TO maxstar&-1
x&=RAND(319)
xadr&=SHL(SHR(x&,4),3) ! address
xofst&=x& AND 15 ! offset
CARD{starzbuf%+mid.pointer&}=xadr&
CARD{starzbuf%+mid.pointer&+2}=xofst&
CARD{starzbuf%+mid.pointer&+4}=RAND(199)*160 ! y adr
CARD{starzbuf%+mid.pointer&+6}=(RAND(8)+1)*160 ! y speed
'
CARD{starzbuf%+old.pointer&}=0
CARD{starzbuf%+old.pointer&+2}=0
CARD{starzbuf%+old.pointer&+4}=0
CARD{starzbuf%+old.pointer&+6}=0
ADD mid.pointer&,8
ADD old.pointer&,8
NEXT i|
maxstar.lg&=SHL(maxstar&-1,3) ! X8
starz%=starzbuf%
mid.starz%=ADD(starzbuf%,maxstar&*8)
old.starz%=ADD(starzbuf%,maxstar&*8*2)
pxlz&(0)=-32768 !&X1000000000000000
mystar&=&X0100000000000000
pxlz&(1)=mystar&
FOR i&=2 TO 15 !2 TO 30 STEP 2
mystar&=SHR(mystar&,1) ! 1 pixel shift to the right
pxlz&(i&)=mystar&
NEXT i&
RETURN
'
PROCEDURE fin
SHOWM
BYTE{&HFFFFFC02}=&H8 ! rend la souris ! mouse alive
~MFREE(scrbuf%)
~MFREE(starzbuf%)
BMOVE palette%,&HFFFF8240,32 ! restore old palette
~XBIOS(5,L:xb2%,L:xb2%,rez|)
~GEMDOS(32,L:super%) ! user mode
EDIT
RETURN
tomchi- Messages : 52
Date d'inscription : 15/09/2018
Age : 49
Shaoth aime ce message
Re: Champ d'étoiles vertical [FR] / [ENG]
The very few programming I know is all about GFA Basic and comes from reading tutorials published in the french ST Mag magazine. Following is all easy and basic stuff, any contribution to improve this is welcome.
So, let's do some simple and easy vertical starfield then .
Basically, what we'll do is erase previous star then increase star y with its speed and PLOT it.
Pseudo code would look like :
Now that means that we have to store coordinates of each star to erase where they were, then add speed and then plot them.
Also that shows us that we have to set them once before we start the main loop.
Most obvious way to handle them is to use an array : say we define maxstar& as a constant to set the numbers of stars we want to move.
Then we set a 2 dimensions array to store coords + speed of each star:
star&(blah&,0)=x&
star&(blah&,1)=y&
star&(blah&,2)=speed&
Where blah& is the current star we're computing.
We'll come on that later but this 2 dimensions array will prove to be far from being fast ( at least there's some other easy stuff being way faster ) tho enough for a few stars.
Now how will we erase/display them ? We're *not* going to use PLOT, because we want the starfield to be fast and fluid.
One has to know ( at least a bit ) how graphics are stored in memory in a ST.
We'll work in ST Low resolution, that means 320*200 pixels / 16 colors.
Graphics are stored 16 pixels at a time into 8 bytes : 320 pixels are then stored in 160 bytes * 200 lines = 32000 bytes (size of screen).
Also, to use the 16 colors, pixels are coded into 4 bitplanes, that way each word in memory stores the pixels for each plane.
So if you want to put third pixel from the left with color 13 you have to write :
&X0010000000000000 into address%
&X0000000000000000 into address%+2 ( second word )
&X0010000000000000 into address%+4 ( third word )
&X0010000000000000 into address%+6 ( fourth word )
If we want to use color 1 to plot the stars, we'll only write a single word to screen memory. As it may happen that many stars have to be plotted within the same 16 pixels bloc, we'll be ORing our pixel with the word we want to write on screen so that we don't erase previously plotted star,
The 16 positions of a pixel within a word will be stored into another array.
Erasing the star will be easy too, just write 0 for each previous star position.
To avoid having to convert x and y for each star each VBL, we won't just store coordinates in the array, instead we'll work with addresses.
Convert x to an address : x.address&=SHL(SHR(x&,4),3) that will put it on an 8 bytes boundary (=16 pixels boundary)
=> Let's use another array to store these addresses:
Convert y to an address : y.address&=y&*160
Set speed as a line address : y.speed&=speed&*160
That way we get to:
Plotting stars to screen memory will result into flickering as the ST will refresh screen as we write to it, to avoid that we'll use double buffering:
While ST is displaying screen (physical screen), we'll write to another buffer that has the same size=32000 bytes (logical screen) , then we'll set video base address after SWAPping physical and logical screen address, then VSYNC will wait till the whole screen display is done.
Remember that our routine has to erase previous stars before plotting new ones, with double screen buffer that brings us to another simple trick to do.
Indeed, this means we have to know where the star we want to erase was, not one but two VBLs before.
Because getting back to the same buffer will take 2 VBLs.
1st VBL: display buffer 1, write to buffer 2 star is at y
2nd VBL: display buffer 2, write to buffer 1 star is at y+yspeed
3rd VBL: display buffer 1, write to buffer 2 star is at y+yspeed+yspeed
Of course, we could have set a new star in case it got out of screen, so we can't just SUB 2*yspeed, instead we'll use 3 arrays and SWAP them as we SWAP screen buffers.
Right, we now have enough theory to start coding the routine.
A few words about PROCEDURE inits
You'll be asked how many stars you want to display, max in 1 VBL is approx 75.
We'll get into supervisor mode to write to some memory registers, saving resolution / screens addresses / palette etc to restore them once the user quits the prg.
The buffer MALLOCed for screens is cleared (filled with zeros ), you never know if some data was already in this location before your prg is launched.
Easiest way is to use RC_COPY.
Also, one will notice there's no call to XBIOS(5) (which sets the screen addresses) in the main loop, instead it also writes to memory registers, not that this has to be done that way, ususally using STE features , I just got used to code like that.
Pressing Alternate while running will change background color to blue, blue part of screen being CPU time left before end of VBL.
I decided to write this article after interesting chats with Thomas, author of great GFA games (anarcho ride / frogs / randomizer ... ).
My first attempt for this starfield didn't use array, it CARDed values to/from a buffer. We talked about arrays and speed, and Thomas came to some fastest code than mine using one dimensional arrays. Tests did prove that two dimensions arrays as showed in the code above were chewing much more CPU time than using multiple one dimension arrays, while he felt comfortable with that point, I argued that was too many arrays for me. One has to have an array for x adr, xofst, y, yspeed three times (current / mid / old ).
Being a CARD "integrist", I challenged myself to optimize the old routine, following code will display 150 stars each VBL and uses both CARD and arrays.
Goal was to avoid computations as much as possible.
Instead of using many arrays, stars data will be stored in a buffer :
Only one big buffer is MALLOCed, then we set pointers to tell where each buffer starts.
Memory needed for the stars data is maxstar&*8*3 : we still need to store current / mid / old positions (=>*3) and one set of data is 8 bytes (4*2).
Let's set pointers for current / mid / old stars data:
Last thing, instead of using FOR/NEXT I went for REPEAT/UNTIL, set a variable that points to the buffer I wanna poke to and add 8 to it each loop until it points to the end of the buffer.
So, let's do some simple and easy vertical starfield then .
What we want : Easily readable, modifiable code, also it *has* to run nicely at 50 fps (once compiled).
Multiple speed stars moving verticaly.
Each star getting outside screen will be replaced with a randomly plotted new one.
Basically, what we'll do is erase previous star then increase star y with its speed and PLOT it.
Pseudo code would look like :
- Code:
REPEAT
VSYNC ! Screen refresh
FOR star&=1 TO maxstar&
erase(star&) ! to be coded
NEXT star&
'
FOR star&=1 TO maxstar&
ADD y.star&,yspeed& ! to be
display(star&) ! coded
NEXT star&
UNTIL space_pressed
Now that means that we have to store coordinates of each star to erase where they were, then add speed and then plot them.
Also that shows us that we have to set them once before we start the main loop.
Most obvious way to handle them is to use an array : say we define maxstar& as a constant to set the numbers of stars we want to move.
Then we set a 2 dimensions array to store coords + speed of each star:
- Code:
maxstar&=50
DIM star&(maxstar&,2)
star&(blah&,0)=x&
star&(blah&,1)=y&
star&(blah&,2)=speed&
Where blah& is the current star we're computing.
Notice the "&" : this tells GFA Basic we want to deal with words (-32768;..;32767). Why ? First because our values will be higher than what a byte ("|") can carry, and also because the ST will work faster with words than with longwords ("%") or floats.
We'll come on that later but this 2 dimensions array will prove to be far from being fast ( at least there's some other easy stuff being way faster ) tho enough for a few stars.
Now how will we erase/display them ? We're *not* going to use PLOT, because we want the starfield to be fast and fluid.
One has to know ( at least a bit ) how graphics are stored in memory in a ST.
We'll work in ST Low resolution, that means 320*200 pixels / 16 colors.
Graphics are stored 16 pixels at a time into 8 bytes : 320 pixels are then stored in 160 bytes * 200 lines = 32000 bytes (size of screen).
Also, to use the 16 colors, pixels are coded into 4 bitplanes, that way each word in memory stores the pixels for each plane.
Now you see color 0 uses no plane, color 1 uses plane 1, color 2 uses plane 2, color 3 uses plane 1 and plane 2 , etc .
--------------------------------------------------------
! numb ! bin ! plane1 ! plane2 ! plane3 ! plane4 !
! 00 ! 0000 ! no ! no | no ! no !
! 01 ! 0001 ! yes ! no ! no ! no !
! 02 ! 0010 ! no ! yes ! no ! no !
! 03 ! 0011 ! yes ! yes ! no ! no !
! 04 ! 0100 ! no ! no ! yes ! no !
! 05 ! 0101 ! yes ! no ! yes ! no !
! 06 ! 0110 ! no ! yes ! yes ! no !
! 07 ! 0111 ! yes ! yes ! yes ! no !
! 08 ! 1000 ! no ! no ! no ! yes !
! 09 ! 1001 ! yes ! no ! no ! yes !
! 10 ! 1010 ! no ! yes ! no ! yes !
! 11 ! 1011 ! yes ! yes ! no ! yes !
! 12 ! 1100 ! no ! no ! yes ! yes !
! 13 ! 1101 ! yes ! no ! yes ! yes !
! 14 ! 1110 ! no ! yes ! yes ! yes !
! 15 ! 1111 ! yes ! yes ! yes ! yes !
--------------------------------------------------------
So if you want to put third pixel from the left with color 13 you have to write :
&X0010000000000000 into address%
&X0000000000000000 into address%+2 ( second word )
&X0010000000000000 into address%+4 ( third word )
&X0010000000000000 into address%+6 ( fourth word )
( "&X" tells GFA you're setting binary values, for hexadecimal values use "&H" )
If we want to use color 1 to plot the stars, we'll only write a single word to screen memory. As it may happen that many stars have to be plotted within the same 16 pixels bloc, we'll be ORing our pixel with the word we want to write on screen so that we don't erase previously plotted star,
The 16 positions of a pixel within a word will be stored into another array.
- Code:
DIM pxlz&(15)
pxlz&(0)=-32768 !&X1000000000000000=&H8000=-32768
mystar&=&X0100000000000000
pxlz&(1)=mystar&
FOR i&=2 TO 15 !
mystar&=SHR(mystar&,1) ! 1 pixel shift to the right
pxlz&(i&)=mystar&
NEXT i&
- Code:
CARD{screen_address%}=pxlz&(blah&) OR CARD{screen_address%}
Erasing the star will be easy too, just write 0 for each previous star position.
To avoid having to convert x and y for each star each VBL, we won't just store coordinates in the array, instead we'll work with addresses.
Convert x to an address : x.address&=SHL(SHR(x&,4),3) that will put it on an 8 bytes boundary (=16 pixels boundary)
=> Let's use another array to store these addresses:
- Code:
DIM xadr.ar&(320)
for i&=0 to 319
xadr.ar&(i&)=SHL(SHR(i&,4),3)
next i&
Convert y to an address : y.address&=y&*160
Set speed as a line address : y.speed&=speed&*160
That way we get to:
- Code:
CARD{screen_address%+x.address&+y.address&}=pxlz&(xofst&) OR CARD{screen_address%+x.address&+y.address&}
Plotting stars to screen memory will result into flickering as the ST will refresh screen as we write to it, to avoid that we'll use double buffering:
While ST is displaying screen (physical screen), we'll write to another buffer that has the same size=32000 bytes (logical screen) , then we'll set video base address after SWAPping physical and logical screen address, then VSYNC will wait till the whole screen display is done.
- Code:
scrbuf%=MALLOC(64000+256) ! Allocate memory for 2 screens
phy%=AND(ADD(scrbuf%,255),&HFFFFFF00) ! Sets physical screen address
log%=ADD(phy%,32000) ! sets logical screen address
On ST screen address has to be on a 256 bytes boundary hence the +256 memory allocation and phy% having low byte zeroed (AND &HFFFFFF00).
Remember that our routine has to erase previous stars before plotting new ones, with double screen buffer that brings us to another simple trick to do.
Indeed, this means we have to know where the star we want to erase was, not one but two VBLs before.
Because getting back to the same buffer will take 2 VBLs.
1st VBL: display buffer 1, write to buffer 2 star is at y
2nd VBL: display buffer 2, write to buffer 1 star is at y+yspeed
3rd VBL: display buffer 1, write to buffer 2 star is at y+yspeed+yspeed
Of course, we could have set a new star in case it got out of screen, so we can't just SUB 2*yspeed, instead we'll use 3 arrays and SWAP them as we SWAP screen buffers.
- Code:
DIM current.star&(maxstar&,3)
DIM middle.star&(maxstar&,3)
DIM old.star&(maxstar&,3)
Right, we now have enough theory to start coding the routine.
A few words about PROCEDURE inits
You'll be asked how many stars you want to display, max in 1 VBL is approx 75.
We'll get into supervisor mode to write to some memory registers, saving resolution / screens addresses / palette etc to restore them once the user quits the prg.
The buffer MALLOCed for screens is cleared (filled with zeros ), you never know if some data was already in this location before your prg is launched.
Easiest way is to use RC_COPY.
Also, one will notice there's no call to XBIOS(5) (which sets the screen addresses) in the main loop, instead it also writes to memory registers, not that this has to be done that way, ususally using STE features , I just got used to code like that.
Pressing Alternate while running will change background color to blue, blue part of screen being CPU time left before end of VBL.
- Code:
RESERVE 10000
'
@inits
REPEAT
VSYNC
CARD{&HFFFF8240}=0 ! black backgroud
' clear oldstars
FOR i&=0 TO maxstar&
xadr&=old.star&(i&,0)
yadr&=old.star&(i&,2)
CARD{ADD(log%,ADD(xadr&,yadr&))}=0 ! clear previous starz
NEXT i&
'
' calc new coords and display star
FOR i&=0 TO maxstar&
yadr&=middle.star&(i&,2)
yspeed&=middle.star&(i&,3)
newy%=ADD(yadr&,yspeed&)
IF newy%>31999 ! outside screen
x&=RAND(319) ! new random star
xadr&=xadr.ar&(x&)
xofst&=x& AND 15 ! offset
current.star&(i&,0)=xadr&
current.star&(i&,1)=xofst&
current.star&(i&,2)=0 ! y new star = 0
current.star&(i&,3)=(RAND(8)+1)*160 ! y speed
'
dest%=ADD(log%,xadr&) ! y=0 so address=x address
CARD{dest%}=CARD{dest%} OR pxlz&(xofst&)
ELSE
xadr&=middle.star&(i&,0)
xofst&=middle.star&(i&,1)
'
current.star&(i&,0)=xadr&
current.star&(i&,1)=xofst&
current.star&(i&,2)=newy%
current.star&(i&,3)=yspeed&
'
dest%=ADD(log%,ADD(xadr&,newy%))
CARD{dest%}=CARD{dest%} OR pxlz&(xofst&)
ENDIF
NEXT i&
tuch&=BYTE{&HFFFFFC02}
IF tuch&=&H38 ! Alt pressed
CARD{&HFFFF8240}=&HF ! show CPU left
ENDIF
SWAP middle.star&(),old.star&() ! swap stars data arrays old=middle
SWAP current.star&(),middle.star&() ! middle=current
SWAP phy%,log%
BYTE{&HFFFF8201}=SHR(phy%,16)
BYTE{&HFFFF8203}=SHR(phy%,8)
'
UNTIL tuch&=&H39 ! Spacebar pressed
'
@fin
'
PROCEDURE inits
CLS
INPUT "Max Stars",maxstar& ! asks for how many stars you want on screen
DIM pxlz&(15) ! array storing 16 shifted pixels
DIM xadr.ar&(320) ! array storing X addresses
DIM current.star&(maxstar&,3) ! array storing X address / X ofset / Y address / Y speed for each star
DIM middle.star&(maxstar&,3) ! same as above
DIM old.star&(maxstar&,3) ! idem <= we'll swap these 3 to get stars old address
scrbuf%=MALLOC(64000+256+32) ! 1 buffer for both 2 screens + palette save
phy%=AND(ADD(scrbuf%,255),&HFFFFFF00) ! phy% on a byte boundary
log%=ADD(phy%,32000) ! log% 32000 bytes further
palette%=ADD(starzbuf%,64000) ! palette% after log%
super%=GEMDOS(32,L:0) ! supervisor mode
'
HIDEM
BYTE{&HFFFFFC02}=&H12 ! no mouse
rez|=XBIOS(4) ! saves resolution
xb2%=XBIOS(2) ! saves screen address
'
for i&=0 to 319
xadr.ar&(i&)=SHL(SHR(i&,4),3) ! X coordinate to address
next i&
BMOVE &HFFFF8240,palette%,32 ! saves oldpal
BYTE{&HFFFF8260}=0 ! low REZ
VSYNC
~XBIOS(5,L:log%,L:phy%,-1) ! set screens
VSYNC
RC_COPY phy%,0,0,320,400 TO phy%,0,0,0 ! clears both phy% and log% buffers
VSYNC
SETCOLOR 1,&H777 ! stars are white, oh my god !
FOR i&=0 TO maxstar&
x&=RAND(319)
xadr&=SHL(SHR(x&,4),3) ! address
xofst&=x& AND 15 ! offset
middle.star&(i&,0)=xadr&
middle.star&(i&,1)=xofst&
middle.star&(i&,2)=RAND(199)*160 ! y adr
middle.star&(i&,3)=(RAND(8)+1)*160 ! y speed
'
old.star&(i&,0)=0
old.star&(i&,1)=0
old.star&(i&,2)=0
old.star&(i&,3)=0
NEXT i&
pxlz&(0)=-32768 ! -32768=&H8000=&X1000000000000000
mystar&=&X0100000000000000
pxlz&(1)=mystar&
FOR i&=2 TO 15
mystar&=SHR(mystar&,1) ! 1 pixel shift to the right
pxlz&(i&)=mystar&
NEXT i&
RETURN
'
PROCEDURE fin
SHOWM
BYTE{&HFFFFFC02}=&H8 ! rend la souris ! mouse alive
~MFREE(scrbuf%)
BMOVE palette%,&HFFFF8240,32 ! restore old palette
~XBIOS(5,L:xb2%,L:xb2%,rez|)
~GEMDOS(32,L:super%) ! user mode
EDIT
RETURN
I decided to write this article after interesting chats with Thomas, author of great GFA games (anarcho ride / frogs / randomizer ... ).
My first attempt for this starfield didn't use array, it CARDed values to/from a buffer. We talked about arrays and speed, and Thomas came to some fastest code than mine using one dimensional arrays. Tests did prove that two dimensions arrays as showed in the code above were chewing much more CPU time than using multiple one dimension arrays, while he felt comfortable with that point, I argued that was too many arrays for me. One has to have an array for x adr, xofst, y, yspeed three times (current / mid / old ).
Being a CARD "integrist", I challenged myself to optimize the old routine, following code will display 150 stars each VBL and uses both CARD and arrays.
Goal was to avoid computations as much as possible.
Instead of using many arrays, stars data will be stored in a buffer :
- Code:
INPUT "Max Stars",maxstar&
DIM pxlz&(15)
DIM xadr.ar&(320)
scrbuf%=MALLOC(64000+256+maxstar&*8*3+32)
phy%=AND(ADD(scrbuf%,255),&HFFFFFF00)
log%=ADD(phy%,32000)
starzbuf%=ADD(log%,32000) !MALLOC(maxstar&*8*3) (1 word X / 1 word offset / 1 word Y / 1 word speed)*3 buffers
palette%=ADD(starzbuf%,maxstar&*8*3) !MALLOC(32)
Only one big buffer is MALLOCed, then we set pointers to tell where each buffer starts.
Memory needed for the stars data is maxstar&*8*3 : we still need to store current / mid / old positions (=>*3) and one set of data is 8 bytes (4*2).
Let's set pointers for current / mid / old stars data:
- Code:
starz%=starzbuf%
mid.starz%=ADD(starzbuf%,maxstar&*8)
old.starz%=ADD(starzbuf%,maxstar&*8*2)
Last thing, instead of using FOR/NEXT I went for REPEAT/UNTIL, set a variable that points to the buffer I wanna poke to and add 8 to it each loop until it points to the end of the buffer.
- Code:
RESERVE 10000
'
@inits
REPEAT
VSYNC
CARD{&HFFFF8240}=0 ! black backgroud
' clear oldstars
old.starz.pointer%=old.starz%
old.starz.pointer.max%=ADD(old.starz%,maxstar.lg&)
REPEAT
xadr&=CARD{old.starz.pointer%}
yadr&=CARD{ADD(old.starz.pointer%,4)}
CARD{ADD(log%,ADD(xadr&,yadr&))}=0 ! clear previous starz
ADD old.starz.pointer%,8
UNTIL old.starz.pointer%=old.starz.pointer.max%
'
' calc new coords and display star
starz.pointer%=starz%
starz.pointer.max%=ADD(starz%,maxstar.lg&)
mid.starz.pointer%=mid.starz%
REPEAT
yadr&=CARD{ADD(mid.starz.pointer%,4)}
yspeed&=CARD{ADD(mid.starz.pointer%,6)}
newy%=ADD(yadr&,yspeed&)
IF newy%>31999 ! outside screen
x&=RAND(319) ! new random star
xadr&=xadr.ar&(x&)
xofst&=x& AND 15 ! offset
CARD{starz.pointer%}=xadr&
CARD{ADD(starz.pointer%,2)}=xofst&
CARD{ADD(starz.pointer%,4)}=0
CARD{ADD(starz.pointer%,6)}=MUL(ADD(RAND(8),1),160) !MUL(ADD(x& AND 7,1),160) !(RAND(8)+1)*160
'
dest%=ADD(log%,xadr&)
CARD{dest%}=CARD{dest%} OR pxlz&(xofst&)
ADD starz.pointer%,8
ADD mid.starz.pointer%,8
ELSE
xadr&=CARD{mid.starz.pointer%}
xofst&=CARD{ADD(mid.starz.pointer%,2)}
'
CARD{starz.pointer%}=xadr&
CARD{ADD(starz.pointer%,2)}=xofst&
CARD{ADD(starz.pointer%,4)}=newy%
CARD{ADD(starz.pointer%,6)}=yspeed&
'
dest%=ADD(log%,ADD(xadr&,newy%))
CARD{dest%}=CARD{dest%} OR pxlz&(xofst&)
ADD starz.pointer%,8
ADD mid.starz.pointer%,8
ENDIF
UNTIL starz.pointer%=starz.pointer.max%
tuch&=BYTE{&HFFFFFC02}
IF tuch&=&H38 ! Alt pressed
CARD{&HFFFF8240}=&HF ! show CPU left
ENDIF
SWAP mid.starz%,old.starz%
SWAP starz%,mid.starz%
SWAP phy%,log%
BYTE{&HFFFF8201}=SHR(phy%,16)
BYTE{&HFFFF8203}=SHR(phy%,8)
'
UNTIL tuch&=&H39 ! Spacebar pressed
'
@fin
'
PROCEDURE inits
CLS
INPUT "Max Stars",maxstar&
DIM pxlz&(15)
DIM xadr.ar&(320)
scrbuf%=MALLOC(64000+256+maxstar&*8*3+32)
phy%=AND(ADD(scrbuf%,255),&HFFFFFF00)
log%=ADD(phy%,32000)
starzbuf%=ADD(log%,32000) !MALLOC(maxstar&*8*3) ! (1 word X / 1 word offset / 1 word Y / 1 word speed)*3 buffers
palette%=ADD(starzbuf%,maxstar&*8*3) !MALLOC(32)
super%=GEMDOS(32,L:0) ! supervisor mode
'
HIDEM
BYTE{&HFFFFFC02}=&H12 ! plus de souris ! no mouse
rez|=XBIOS(4)
xb2%=XBIOS(2)
'
for i&=0 to 319
xadr.ar&(i&)=SHL(SHR(i&,4),3)
next i&
BMOVE &HFFFF8240,palette%,32 ! saves oldpal
BYTE{&HFFFF8260}=0 ! low REZ
VSYNC
~XBIOS(5,L:log%,L:phy%,-1) ! set screens
VSYNC
RC_COPY phy%,0,0,320,400 TO phy%,0,0,0 ! clears both phy% and log% buffers
VSYNC
SETCOLOR 1,&H777
mid.pointer&=maxstar&*8
old.pointer&=maxstar&*8*2
FOR i|=0 TO maxstar&-1
x&=RAND(319)
xadr&=SHL(SHR(x&,4),3) ! address
xofst&=x& AND 15 ! offset
CARD{starzbuf%+mid.pointer&}=xadr&
CARD{starzbuf%+mid.pointer&+2}=xofst&
CARD{starzbuf%+mid.pointer&+4}=RAND(199)*160 ! y adr
CARD{starzbuf%+mid.pointer&+6}=(RAND(8)+1)*160 ! y speed
'
CARD{starzbuf%+old.pointer&}=0
CARD{starzbuf%+old.pointer&+2}=0
CARD{starzbuf%+old.pointer&+4}=0
CARD{starzbuf%+old.pointer&+6}=0
ADD mid.pointer&,8
ADD old.pointer&,8
NEXT i|
maxstar.lg&=SHL(maxstar&-1,3) ! X8
starz%=starzbuf%
mid.starz%=ADD(starzbuf%,maxstar&*8)
old.starz%=ADD(starzbuf%,maxstar&*8*2)
pxlz&(0)=-32768 !&X1000000000000000
mystar&=&X0100000000000000
pxlz&(1)=mystar&
FOR i&=2 TO 15 !2 TO 30 STEP 2
mystar&=SHR(mystar&,1) ! 1 pixel shift to the right
pxlz&(i&)=mystar&
NEXT i&
RETURN
'
PROCEDURE fin
SHOWM
BYTE{&HFFFFFC02}=&H8 ! rend la souris ! mouse alive
~MFREE(scrbuf%)
~MFREE(starzbuf%)
BMOVE palette%,&HFFFF8240,32 ! restore old palette
~XBIOS(5,L:xb2%,L:xb2%,rez|)
~GEMDOS(32,L:super%) ! user mode
EDIT
RETURN
tomchi- Messages : 52
Date d'inscription : 15/09/2018
Age : 49
Shadow272 aime ce message
Re: Champ d'étoiles vertical [FR] / [ENG]
Cette explication de fou !!
Merci @tomchi
Je vais lire ça avec intérêt !
Merci @tomchi
Je vais lire ça avec intérêt !
Shaoth- Messages : 64
Date d'inscription : 28/09/2020
Age : 54
Localisation : Courbevoie
GFA :: Programmation :: Graphismes
Page 1 sur 1
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum
|
|