Il codice su tutorial_protothread2.c mostra come realizzare una animazione con diversi attori autonomi, ognuno gestito da un thread separato.
Non commenteremo ogni singola riga ma ci limiteremo a segnalare alcun punti fondamentali, da approfondire. In particolare questo tutorial sfrutta i tiles e il motore di rendering realizzato per il gioco "The Elevator",al sorgente del quale si rimanda per approfondimenti e chiarimenti
MR_PT_CTX(walk, unsigned char floor; unsigned char frame; unsigned char pos; unsigned char to; mr_color color;);
Questa dichiarazione permette di riservare ben 5 variabili locali per ogni thread che si occuperà di spostare il personaggio. Queste variabili sono utili per rappresentare alcune informazioni essenziali, quali:
floor
) su cui dovremo disegnare il personaggio;frame
) del personaggio disegnato per l'ultima volta sullo schermo;pos
) dove il personaggio è stato disegnato per l'ultima volta;to
) che il personaggio deve raggiungere;color
) con cui disegnare il personaggio.walk_protothread walkThread[FLOOR_COUNT];
Con questa istruzione allochiamo un thread per ogni piano dove camminerà il personaggio. Da notare che, a seguito di questa nuova definizione (walk_protothread
) l'occupazione per ogni protothread passa da 2 byte a 7 byte per protothread. Quindi lo spazio di memoria occupato, complessivamente, dal multithreading di questo tutorial è di 35 byte.
MR_PT_THREAD_EXT(walk, walk_protothread) { MR_PTI_BEGIN();
Da qui inizia il codice del protothread.
// ARRIVED? while (MR_PTI_CTX()->pos < MR_PTI_CTX()->to) {
Qui si controlla la posizione raggiunta dal personaggio, rispetto a quella attesa. Notare come l'accesso alle variabili “locali” avvengano attraverso l'estensione del contesto.
// INCREASE PASSENGER FRAME ++MR_PTI_CTX()->frame;
Qui aggiorniamo il frame di animazione.
// EVEN FRAME? if ((MR_PTI_CTX()->frame & 1) == 0) { // INCREASE PASSENGER POSITION ++MR_PTI_CTX()->pos; }
Spostiamo il personaggio in avanti di una posizione se e soltanto se il frame di animazione è pari. Questo significa che, ogni due fotogrammi, ci si sposta di un passo a destra.
// FRAME MAX REACHED? if (MR_PTI_CTX()->frame > WALK_FRAME_COUNT) { // RESET FRAME MR_PTI_CTX()->frame = 0; }
Qui garantiamo che l'animazione sia ciclica, dal primo all'ultimo frame.
// (yeld) MR_PTI_YIELD();
Qui lasciamo il tempo allo scheduler di far andar avanti altri thread.
// DRAW PASSENGER draw_passenger_walking(MR_PTI_CTX()->floor, MR_PTI_CTX()->pos, MR_PTI_CTX()->frame, MR_PTI_CTX()->color);
Disegniamo, infine, il personaggio.
}
MR_PTI_END();
Qui termina il protothread.
Nella funzione principale del tutorial, oltre alla parte prettamente grafica, sono inizializzate tutte le variabili di ogni protothread. In particolare, per ogni personaggio:
for (i = 0; i < FLOOR_COUNT; ++i) { walkThread[i].floor = i;
Viene impostato il piano corrispondente alla posizione ordinale.
walkThread[i].pos = rand() & 0xf;
Viene impostata una posizione di partenza casuale, da 0 a 15.
walkThread[i].frame = 0;
Viene impostato il fotogramma di partenza.
walkThread[i].to = MR_SCREEN_WIDTH - 2;
Viene impostata la posizione di arrivo, che corrisponde al bordo dello schermo.
switch (i) { case 0: walkThread[i].color = MR_COLOR_RED; break; case 1: walkThread[i].color = MR_COLOR_GREEN; break; case 2: walkThread[i].color = MR_COLOR_BLUE; break; case 3: walkThread[i].color = MR_COLOR_BLACK; break; case 4: walkThread[i].color = MR_COLOR_PURPLE; break; } }
Viene impostato il colore da utilizzare.
A questo punto, entra in scena il loop cooperativo:
while (1) { mr_start_frame();
Questa funzione permette di impostare una posizione di sincronizzazione temporale con il disegno.
for (i = 0; i < FLOOR_COUNT; ++i) { walk(&walkThread[i]); }
Questo loop corrisponde allo scheduling dei protothreads.
mr_end_frame(4);
Questa funzione permette di impostare la posizione finale di sincronizzazione, garantendo che l'intero disegno sia eseguito entro 4 jiffies (4/60 di secondo circa).
E' possibile compilare il tutorial con questa linea di comando:
make tutorial=PROTOTHREAD2 target=... all
Le piattaforme supportate sono:
vic2024
);c64
);c128
);plus4
);atari
).