Sommario:

Network Rivalry: un gioco a bassa latenza per BBC Micro:bit: 10 passaggi (con immagini)
Network Rivalry: un gioco a bassa latenza per BBC Micro:bit: 10 passaggi (con immagini)

Video: Network Rivalry: un gioco a bassa latenza per BBC Micro:bit: 10 passaggi (con immagini)

Video: Network Rivalry: un gioco a bassa latenza per BBC Micro:bit: 10 passaggi (con immagini)
Video: ✨A Will Eternal EP 01 - 106 Full Version [MULTI SUB] 2024, Luglio
Anonim
Network Rivalry: un gioco a bassa latenza per BBC Micro: bit
Network Rivalry: un gioco a bassa latenza per BBC Micro: bit
Network Rivalry: un gioco a bassa latenza per BBC Micro: bit
Network Rivalry: un gioco a bassa latenza per BBC Micro: bit

In questo tutorial spiegherò come implementare un gioco multiplayer di base su BBC micro:bit con le seguenti caratteristiche:

  • Un'interfaccia semplice
  • Bassa latenza tra la pressione dei pulsanti e gli aggiornamenti dello schermo
  • Un numero flessibile di partecipanti
  • Facile controllo del gioco utilizzando un dispositivo remoto principale ("root")

Il gioco è essenzialmente una simulazione della politica. Tutti i giocatori iniziano non assegnati a nessuna squadra, ad eccezione di due giocatori. Uno di questi giocatori è assegnato alla squadra A e l'altro è assegnato alla squadra B.

L'obiettivo del gioco per ogni giocatore è essere nella squadra con la maggior parte dei giocatori nel momento in cui tutti vengono convertiti.

Il diagramma sopra illustra una macchina a stati finiti, ovvero una specifica degli stati in cui può trovarsi il dispositivo e le transizioni tra tali stati.

Uno stato può essere considerato come l'insieme corrente di dati che descrive la memoria del dispositivo da quando è stato acceso. Sulla base di tali dati, il dispositivo può eseguire determinate azioni o reagire in modo diverso all'input dell'utente.

Una transizione è una condizione logica che, se vera, fa sì che il dispositivo cambi il suo stato. Una transizione può essere da uno stato a qualsiasi altro stato. Uno stato può avere più transizioni.

Il diagramma sopra specifica i seguenti stati:

  • Non assegnato
  • Ascolta per A
  • Ascolta B
  • Squadra A
  • Squadra B

Un dispositivo che esegue il codice di gioco può trovarsi in uno di questi cinque stati, ma solo uno alla volta e solo questi cinque.

Assumerò in tutta la guida che stai utilizzando l'editor MakeCode di Microsoft, che puoi trovare su:

L'implementazione completa del gioco può essere trovata qui:

makecode.microbit.org/_CvRMtheLbRR3 ("microbit-demo-user" è il nome del progetto)

E l'implementazione del controller di rete master ("root") può essere trovata qui:

makecode.microbit.org/_1kKE6TRc9TgE ("microbit-demo-root" è il nome del progetto)

Farò riferimento a questi esempi durante il mio tutorial.

Passaggio 1: considerazioni sulla progettazione di grandi immagini

Prima di scrivere qualsiasi codice, dobbiamo pensare a come vogliamo che sia il nostro prodotto finale. in altre parole, quali sono i requisiti della domanda? Cosa dovrebbe fare il nostro codice al dispositivo una volta terminato? Ho suddiviso le funzionalità dell'applicazione principale in sei categorie, ognuna delle quali può essere considerata da una diversa prospettiva progettuale.

  1. Vogliamo controllare le azioni del dispositivo in base al suo stato attuale
  2. Vogliamo che il dispositivo reagisca all'input dell'utente
  3. Potremmo voler visualizzare animazioni e grafica utilizzando il display LED 5 x 5
  4. Vogliamo inizializzare i valori dei dati nella memoria del dispositivo all'avvio del dispositivo
  5. Vogliamo trasmettere dati in modalità wireless utilizzando la radio del dispositivo
  6. Vogliamo ascoltare e ricevere dati sulla radio del dispositivo ed elaborarli di conseguenza

Permettetemi di entrare un po' più in dettaglio su ciascuno di essi.

1. Vogliamo controllare le azioni del dispositivo in base al suo stato attuale

Come la maggior parte degli altri programmi, l'esecuzione delle istruzioni specificate dal codice avviene una riga alla volta. Vogliamo che il nostro dispositivo esegua determinate istruzioni in base al suo stato interno, come illustrato dal diagramma nella parte superiore di questo tutorial. Potremmo scrivere una serie di condizionali dopo ogni blocco di codice che controlla che il dispositivo dovrebbe fare, ma questo approccio può diventare molto disordinato molto rapidamente, quindi useremo invece un ciclo infinito che controlla semplicemente una variabile e in base a quella variabile, esegue una serie specifica di istruzioni o non fa nulla. Questa variabile sarà identificata dal suffisso "_state" sia nella nostra applicazione utente che nella nostra applicazione root.

2. Vogliamo che il dispositivo reagisca all'input dell'utente

Nonostante la normale esecuzione del codice avvenga in sequenza, vale a dire una riga alla volta, abbiamo bisogno che il nostro dispositivo reagisca alla pressione dei pulsanti mentre il ciclo di stato principale determina ciò che il dispositivo dovrebbe fare in un dato momento. A tale scopo, il dispositivo ha la capacità di inviare segnali al software di livello inferiore che interagisce con l'hardware, attivando quello che viene chiamato un evento. Possiamo scrivere codice che dice al dispositivo di fare qualcosa quando rileva un tipo specifico di evento.

3. Vogliamo visualizzare animazioni e grafica utilizzando il display LED 5 x 5

Il meccanismo per farlo sembra essere semplice, ma il blocco visualizza un'immagine aggiunge un ritardo nascosto di 400 ms. Poiché vogliamo che il nostro dispositivo continui a eseguire il suo loop di stato con la minore latenza possibile, dovremo modificare il codice javascript per ridurre al minimo il ritardo.

4. Vogliamo inizializzare i valori dei dati nella memoria del dispositivo all'avvio del dispositivo

Prima che il nostro dispositivo faccia qualsiasi cosa, l'applicazione deve caricare i suoi dati in memoria. Ciò include variabili costanti denominate per la leggibilità del codice, variabili che contengono immagini, che possono far parte di un'animazione e variabili contatore che devono iniziare da 0 per funzionare correttamente. Finiremo con un lungo elenco di nomi di variabili e i loro nuovi valori assegnati. Come scelta di stile personale, indicherò valori costanti, cioè valori che non avrò mai bisogno di cambiare, usando ALL_CAPS. Premetterò anche gli identificatori delle variabili principali con un nome di categoria che si riferisce a una sorta di oggetto o tipo in cui ricade l'identificatore. Questo è nel tentativo di rendere il codice più facile da seguire. Non userò mai un nome di variabile come "item" o "x" a causa dell'ambiguità che sorge quando si tenta di decifrare il codice.

5. Vogliamo trasmettere dati in modalità wireless utilizzando la radio del dispositivo

Questo è in realtà un compito abbastanza semplice quando si utilizza il linguaggio dei blocchi MakeCode. Impostiamo semplicemente tutti i dispositivi sullo stesso gruppo radio all'avvio e quindi quando vogliamo inviare un segnale, possiamo passare un singolo numero al blocco "Numero di invio radio" fornitoci. È importante che il mittente e il destinatario lavorino sullo stesso gruppo radio, perché in caso contrario, invieranno o riceveranno su frequenze diverse e la comunicazione non andrà a buon fine.

6. Vogliamo ascoltare e ricevere dati sulla radio del dispositivo ed elaborarli di conseguenza

Tenendo presente le stesse considerazioni dell'elemento precedente, ascolteremo le trasmissioni in entrata allo stesso modo ascolteremo l'input dell'utente: con un gestore di eventi. Scriveremo un blocco di codice che esaminerà eventuali segnali in ingresso e verificherà se è necessario intraprendere qualsiasi azione senza disturbare il ciclo di stato principale.

Inoltre, dovremmo considerare brevemente il design dell'applicazione root molto più semplice, un programma che consentirà a un dispositivo di controllare l'intera rete. Non dedicherò molto tempo a questo dato che è molto più semplice del progetto di cui sopra e gran parte di esso è semplicemente ripetizione. Ho diviso la funzionalità della radice deice in tre categorie.

  1. Vogliamo essere in grado di selezionare un segnale
  2. Vogliamo essere in grado di trasmettere un segnale

-

1. Vogliamo essere in grado di selezionare un segnale

Questo può essere fatto semplicemente facendo scorrere un pulsante attraverso i possibili segnali. Poiché ce ne sono solo tre, questo approccio sarà sufficiente. Allo stesso tempo, possiamo avere un loop che rivisualizza costantemente il segnale selezionato, consentendo all'utente di premere un pulsante e vedere il segnale selezionato apparire sul display a LED con una latenza minima.

2. Vogliamo essere in grado di trasmettere un segnale

Poiché ci sono due pulsanti, possiamo designarne uno per la selezione e l'altro per la conferma. Come l'applicazione utente, inviamo semplicemente il segnale sulla rete come un numero. Non sono necessarie altre informazioni.

Parlerò di più del protocollo di segnale semplice nella prossima sezione.

Fase 2: Il protocollo di segnale: un linguaggio semplice per la comunicazione di rete

I seguenti segnali possono essere pensati come l'insieme di tutte le possibili parole che i dispositivi possono utilizzare per dialogare tra loro. Poiché la rete è così semplice, non c'è molto da dire e quindi possiamo rappresentare questi tre segnali con semplici valori interi.

0. Ripristina

  • Identificatore nel codice: SIG-R
  • Valore intero: 0
  • Scopo: Dire a tutti i dispositivi nel raggio di azione di interrompere ciò che stanno facendo e agire come se fossero appena stati avviati. Se questo segnale raggiunge tutti i dispositivi della rete, l'intera rete verrà ripristinata e gli utenti potranno iniziare una nuova partita. Questo segnale può essere trasmesso solo da un dispositivo root.

1. Conversione A

  • Identificatore nel codice: SIG-A
  • Valore intero: 1
  • Scopo: Dire a qualsiasi dispositivo che si trova nello stato LISTEN_A, una volta ricevuto il segnale di conversione, di passare allo stato TEAM_A.

2. Conversione B

  1. Identificatore nel codice: SIG-B
  2. Valore intero: 2
  3. Scopo: Dire a qualsiasi dispositivo che si trova nello stato LISTEN_B, una volta ricevuto il segnale di conversione, di passare allo stato TEAM_B.

Passaggio 3: vogliamo controllare le azioni del dispositivo in base al suo stato attuale

Vogliamo controllare le azioni del dispositivo in base al suo stato attuale
Vogliamo controllare le azioni del dispositivo in base al suo stato attuale
Vogliamo controllare le azioni del dispositivo in base al suo stato attuale
Vogliamo controllare le azioni del dispositivo in base al suo stato attuale
Vogliamo controllare le azioni del dispositivo in base al suo stato attuale
Vogliamo controllare le azioni del dispositivo in base al suo stato attuale

Finalmente possiamo iniziare a scrivere il codice.

Innanzitutto, apri un nuovo progetto in Make Code

  • Crea una nuova funzione. Ho chiamato il mio ciclo perché questo è il ciclo principale dell'applicazione
  • Aggiungi un blocco di loop che si ripeterà all'infinito. Ho usato while(true) perché un vero letterale non sarà mai falso, quindi il flusso di controllo dell'applicazione non uscirà mai dal ciclo
  • Aggiungi abbastanza blocchi if-else per verificare se il dispositivo si trova in uno dei suoi cinque possibili stati
  • Crea una variabile per mantenere lo stato corrente del dispositivo
  • Crea variabili per rappresentare ciascuno dei cinque possibili stati

    Nota: è corretto che queste variabili non abbiano ancora alcun valore assegnato. Ci arriveremo. A questo punto, è più importante scrivere codice pulito e facile da leggere

  • Modifica ogni condizione nei blocchi if-else per confrontare lo stato corrente con uno dei possibili stati
  • Nella parte inferiore dei blocchi if-else, aggiungi una pausa per un certo numero di millisecondi e crea una variabile per contenere quel numero. Lo inizializzeremo più tardi. Assicurati che la variabile abbia un nome descrittivo, come tick o heartbeat. Poiché questo è il ciclo principale del dispositivo, questa pausa determinerà la velocità con cui il dispositivo esegue il ciclo principale, quindi è un valore molto importante ed è troppo importante per essere un numero magico senza nome.

Nota: non preoccuparti dei blocchi grigi nella terza immagine. A quelli li arriverò più tardi.

Passaggio 4: vogliamo reagire all'input dell'utente

Vogliamo reagire all'input dell'utente
Vogliamo reagire all'input dell'utente
Vogliamo reagire all'input dell'utente
Vogliamo reagire all'input dell'utente

Ora, vogliamo dire al dispositivo come gestire la pressione dei pulsanti. Il primo pensiero potrebbe essere quello di utilizzare semplicemente i blocchi "Quando viene premuto il pulsante" nella categoria di input, ma vorremmo un controllo più granulare di quello. Useremo il blocco "on event from (X) with value (Y)" dalla categoria di controllo nella sezione avanzata, perché siamo avanzati in questo tutorial.

  • Crea quattro blocchi "su evento da…".

    • Due di questi dovrebbero controllare l'origine dell'evento "MICROBIT_ID_BUTTON_A"
    • Due di questi dovrebbero controllare l'origine dell'evento "MICROBIT_ID_BUTTON_B"
    • Dei due eventi che hanno come target ciascun pulsante:

      • Si dovrebbe controllare l'evento di tipo "MICROBIT_BUTTON_EVT_UP"
      • Si dovrebbe controllare l'evento di tipo "MICROBIT_BUTTON_EVT_DOWN"
    • Nota: queste opzioni in tutte lettere maiuscole sono etichette utilizzate nel codice micro:bit di livello inferiore. Sono semplicemente segnaposto che vengono successivamente sostituiti da numeri interi quando il codice viene compilato in un eseguibile binario. È più facile per gli umani usare queste etichette piuttosto che cercare quale numero intero inserire, anche se entrambe funzionerebbero allo stesso modo.
  • Ho scelto, per una questione di stile, di fare in modo che ogni blocco "on event from…" chiami una funzione che descriva l'evento sollevato. Sebbene non strettamente necessario, a mio parere ciò migliora la leggibilità. Se si desidera farlo, è possibile inserire il codice di gestione degli eventi all'interno del blocco "on event from…" stesso.

    Nota: il blocco di codice che gestisce la risposta del dispositivo a un evento è chiamato intuitivamente "gestore di eventi"

  • Aggiungere, in ogni gestore di eventi, la stessa struttura if-else utilizzata per suddividere il flusso di controllo in base allo stato del dispositivo come struttura nel ciclo di stato principale.
  • Aggiungi blocchi di assegnazione che modificano quello stato del dispositivo come specificato dal nostro diagramma di stato

    • Sappiamo che quando il dispositivo è nello stato NON ASSEGNATO, il dispositivo dovrebbe reagire al pulsante A premuto da una transizione allo stato LISTEN_A e al pulsante B premuto da una transizione allo stato LISTEN_B
    • Sappiamo anche che quando il dispositivo è nello stato LISTEN_A o LISTEN_B, il dispositivo dovrebbe reagire al rilascio del pulsante A e al rilascio del pulsante B, rispettivamente, tornando allo stato NON ASSEGNATO.
    • Infine, sappiamo che quando il dispositivo è nello stato TEAM_A o TEAM_B, il dispositivo dovrebbe reagire al pulsante A premuto e al pulsante B premuto rispettivamente trasmettendo SIG_A e trasmettendo SIG_B.

      A questo punto non è necessario inserire i dettagli dei segnali di trasmissione. Ci arriveremo più tardi. Ciò che è importante è che istruiamo queste funzioni a utilizzare il codice che scriveremo dando un nome a quel blocco di azioni, come broadcastSignalSIG_A, che descrive cosa dovrebbe essere fatto a quel punto

Passaggio 5: si desidera inizializzare i valori dei dati nella memoria dei dispositivi all'avvio del dispositivo

Vogliamo inizializzare i valori dei dati nella memoria dei dispositivi all'avvio del dispositivo
Vogliamo inizializzare i valori dei dati nella memoria dei dispositivi all'avvio del dispositivo
Vogliamo inizializzare i valori dei dati nella memoria dei dispositivi all'avvio del dispositivo
Vogliamo inizializzare i valori dei dati nella memoria dei dispositivi all'avvio del dispositivo
Vogliamo inizializzare i valori dei dati nella memoria dei dispositivi all'avvio del dispositivo
Vogliamo inizializzare i valori dei dati nella memoria dei dispositivi all'avvio del dispositivo

A questo punto, abbiamo usato molte variabili (nomi per dati), ma non abbiamo effettivamente assegnato valori a quei nomi. Vogliamo che il dispositivo carichi i valori di tutte queste variabili in memoria all'avvio, quindi posizioniamo l'inizializzazione per queste variabili in un blocco "all'avvio".

Questi sono i valori che dobbiamo inizializzare:

  • Costanti del segnale, come da protocollo del segnale. I valori DEVONO essere:

    • SIG_R = 0
    • SIG_A = 1
    • SIG_B = 2
    • Nota: ho anteposto queste costanti con "EnumSignals" per indicare che queste variabili devono comportarsi come se facessero parte di un tipo enumerato chiamato Signals. Ecco come queste variabili possono essere implementate in altri linguaggi di programmazione. La definizione e la spiegazione dei tipi enumerati esula dallo scopo del mio tutorial. Si può Google se lo desiderano. Questi prefissi sono semplici scelte stilistiche e non sono affatto indispensabili per il corretto funzionamento del programma.
  • Costanti di stato, che possono essere arbitrarie purché abbiano un valore. Ho fatto una scelta di stile per usare semplicemente numeri interi ascendenti da 0, in questo modo:

    • NON ASSEGNATO = 0
    • ASCOLTA_A = 1
    • ASCOLTA_B = 2
    • SQUADRA_A = 3
    • SQUADRA_B = 4
    • Nota: ho preso la stessa decisione di stile anche per quanto riguarda i prefissi per queste variabili. Inoltre, menzionerò che tutto ciò che riguarda questi incarichi, i valori e l'ordine, è completamente arbitrario. Non importa nemmeno che questi valori siano coerenti da dispositivo a dispositivo, perché vengono utilizzati solo internamente e non per la comunicazione in rete. L'importante è che le variabili abbiano un valore e che possano essere confrontate tra loro per vedere se sono equivalenti o meno.
  • Per la leggibilità, una costante denominata BOOT_STATE e impostarla su UNASSIGNED. Ciò rende più esplicito il fatto di ripristinare lo stato di avvio, anziché uno stato più arbitrario, quando il dispositivo riceve un segnale di ripristino, che implementeremo in seguito.
  • Costanti di animazione, utilizzate nel passaggio successivo per creare animazioni che consentono interruzioni a latenza estremamente bassa tramite l'input dell'utente. Finora non li abbiamo usati, ma verranno sicuramente spiegati e utilizzati nella sezione seguente. Il significato di alcuni di questi dovrebbe essere intuitivo a causa dei loro nomi.

    • TICKS_PER_FRAME_LOADING_ANIMATION = 50
    • MS_PER_DEVICE_TICK = 10
    • MS_PER_FRAME_BROADCAST_ANIMATION = 500
    • MICROSECONDS_PER_MILLISECOND = 1000
    • NUMBER_OF_FRAMES_IN_LOADING_ANIMATION = 4
  • Un'altra variabile per l'animazione, questa volta un contatore decisamente non costante. Come la maggior parte dei contatori, lo inizializziamo a 0

    iTickLoadingAnimation = 0

  • Crea due serie di variabili per contenere i fotogrammi delle animazioni. Il primo, che chiamo "animazione di caricamento", dovrebbe avere quattro immagini (che potresti aver indovinato dall'ultima inizializzazione costante), e il secondo, che chiamo "animazione di trasmissione", che dovrebbe avere tre immagini. Consiglio di nominare le variabili in modo che corrispondano ai fotogrammi dell'animazione, ad es. ringAnimation0, ringAnimation1…

    Crea gli stessi valori di immagine che ho fatto io o crea immagini più originali e più interessanti

  • Ultimo ma non meno importante, dobbiamo impostare il gruppo radio del dispositivo a 0 utilizzando il blocco "gruppo set radio (X)"
  • Facoltativamente, scrivi il messaggio "Inizializzazione completata" sull'output seriale per dire all'utente che tutto è andato per il meglio.
  • Ora che abbiamo finito di configurare il dispositivo, possiamo chiamare la nostra funzione loop di stato.

Passaggio 6: vogliamo visualizzare animazioni e grafica utilizzando il display LED 5 X 5

Vogliamo visualizzare animazioni e grafica utilizzando il display a LED 5 X 5
Vogliamo visualizzare animazioni e grafica utilizzando il display a LED 5 X 5
Vogliamo visualizzare animazioni e grafica utilizzando il display a LED 5 X 5
Vogliamo visualizzare animazioni e grafica utilizzando il display a LED 5 X 5
Vogliamo visualizzare animazioni e grafica utilizzando il display a LED 5 X 5
Vogliamo visualizzare animazioni e grafica utilizzando il display a LED 5 X 5

E ora qualcosa di completamente diverso.

Vogliamo visualizzare alcune animazioni e alcuni personaggi, ma non vogliamo interrompere il ciclo di stato principale. Sfortunatamente, i blocchi che visualizzano immagini e stringhe di testo hanno un ritardo di 400 ms per impostazione predefinita. Non c'è modo di cambiarlo senza modificare la rappresentazione javascript del codice. Quindi, questo è quello che faremo.

  • Crea una funzione per ogni immagine. Ciò consentirà di utilizzare un singolo blocco per visualizzare l'immagine invece di modificare ogni volta javascript. In questo programma specifico, nessuna immagine viene utilizzata più di una volta, ma penso ancora che questo stile renda il codice più facile da leggere.
  • Aggiungi, in ogni nuova funzione, un blocco "mostra immagine (X) all'offset 0" con il nome della variabile immagine corrispondente che sostituisce (X)
  • Aggiungi, nel ciclo di stato principale. Blocchi "Mostra stringa (X)" a ciascun blocco oltre a quello che gestisce lo stato NON ASSEGNATO. Aggiungi un carattere per il dispositivo da visualizzare per indicare i suoi diversi stati. Ecco cosa ho fatto:

    • ASCOLTA_A: 'un'
    • ASCOLTA_B: 'b'
    • TEAM_A: 'A'
    • TEAM_B: 'B'

      Per lo stato UNASSIGNED, effettuare una chiamata a una funzione che aggiornerà l'animazione di caricamento. Inseriremo di seguito i dettagli di questa funzione

  • Passa alla modalità javascript.
  • Trova tutte le chiamate a X.showImage(0) e basic.showString(X)
  • Cambia ognuno in X.showImage(0, 0) o basic.showString(X, 0)

    • L'aggiunta di questo argomento aggiuntivo imposterà il ritardo dopo l'azione su 0. Per impostazione predefinita, questo è omesso e il dispositivo si fermerà per 400 ms dopo l'esecuzione di ciascuno di questi blocchi.
    • Ora abbiamo un meccanismo quasi privo di latenza per visualizzare le nostre immagini nei nostri blocchi di animazione, che ora possiamo costruire

Innanzitutto, costruiremo la funzione di animazione broadcast relativamente semplice. È più semplice perché non vogliamo che l'utente sia in grado di fare nulla fino al completamento della funzione, in modo da impedirgli di inviare spam alla funzione di trasmissione. Per ottenere ciò, possiamo semplicemente mantenere il flusso di controllo limitato al blocco fino al completamento della funzione, che è un comportamento standard.

  • Crea una funzione che visualizzerà l'animazione trasmessa.
  • All'interno di quel blocco, aggiungi tre chiamate di funzione, una per ogni fotogramma dell'animazione, nell'ordine in cui dovrebbero essere visualizzate
  • Aggiungi un blocco "wait (us) (X)" dopo ogni chiamata a una funzione di visualizzazione delle immagini.

    Nota: questo blocco, dalla sezione di controllo avanzato, andrà anche oltre "pausa (ms)" in quanto congelerà completamente il processore fino allo scadere del tempo specificato. Quando viene utilizzato il blocco di pausa, è possibile che il dispositivo esegua altre attività dietro le quinte. Questo è impossibile con il blocco di attesa

  • Sostituisci (X) con (MS_PER_FRAME_BROADCAST_ANIMATION x MICROSECONDS_PER_MILLISECOND)
  • L'animazione ora dovrebbe funzionare correttamente

In secondo luogo, costruiremo il meccanismo per visualizzare l'animazione di caricamento. L'idea alla base di questo è aggiornare il display a LED a un intervallo specifico, che definiamo nella variabile MS_PER_DEVICE_TICK. Questo valore, la lunghezza del tick del dispositivo, è il numero di millisecondi che il dispositivo mette in pausa dopo aver completato ogni iterazione del loop di stato. Poiché questo valore è abbastanza piccolo, possiamo aggiornare il display una volta durante ogni iterazione del ciclo di visualizzazione e sembrerà all'utente che l'animazione sta procedendo senza interruzioni e quando lo stato cambia, ci sarà pochissima latenza tra l'input dell'utente il display in fase di aggiornamento. Contando i tick, cosa che facciamo con la variabile iTickLoadingAnimation, possiamo visualizzare il fotogramma appropriato dell'animazione.

  • Crea una funzione che aggiorni l'animazione di caricamento
  • Aggiungi una condizione per verificare se il contatore tick ha raggiunto il suo valore massimo. Questa condizione saràvera se il valore del contatore di tickèmaggiore del numero di frame nell'animazione di caricamento moltiplicato per il numero di tick per visualizzare ciascun frame

    Se la condizione è vera, reimposta iTickLoadingAnimation su 0

  • Aggiungi un blocco di condizioni if-else. Questi determineranno quale fotogramma dell'animazione visualizzare.

    Per ogni fotogramma dell'animazione, se il contatore di tick è inferiore al numero di tick in ogni animazione moltiplicato per il numero di fotogramma dell'animazione (a partire da 1), allora visualizza quel fotogramma, altrimenti controlla se il fotogramma successivo è quello da essere visualizzato

  • Nella parte inferiore del blocco, incrementa iTickLoadingAnimation
  • L'animazione ora dovrebbe funzionare correttamente

Nota: tutti i blocchi grigi che appaiono nel mio esempio vengono generati quando si modifica la rappresentazione javascript di un blocco. Significa semplicemente che il blocco rappresenta codice javascript che non può essere rappresentato utilizzando l'insieme standard di blocchi e deve essere modificato in forma di testo.

Passaggio 7: vogliamo trasmettere dati in modalità wireless utilizzando la radio del dispositivo

Vogliamo trasmettere dati in modalità wireless utilizzando la radio del dispositivo
Vogliamo trasmettere dati in modalità wireless utilizzando la radio del dispositivo

Questo passaggio è molto più breve del precedente. In effetti, è probabilmente il passaggio più breve dell'intero tutorial.

Ricordiamo che quando abbiamo programmato la risposta del dispositivo all'input dell'utente, avevo due blocchi nello screenshot che non erano spiegati in quella sezione. Erano chiamate a funzioni che inviano segnali via radio. Più specificamente:

  • Sul pulsante A premuto:

    • Se il dispositivo è nello stato TEAM_A:

      Segnale di trasmissione SIG_A

  • Sul pulsante B premuto:

    • Se il dispositivo è nello stato TEAM_B

      Segnale di trasmissione SIG_B

Crea queste funzioni se non esistono già.

In ogni funzione:

  • Chiama la funzione di animazione in trasmissione. Questo bloccherà qualsiasi altra cosa fino al completamento, che sarà in MS_PER_FRAME_BROADCAST_ANIMATION * 3 = 1,5 secondi. La costante viene moltiplicata per tre perché ci sono tre fotogrammi nell'animazione. Questo è arbitrario e se ne possono aggiungere altri se l'aggiornamento estetico è abbastanza grande. Un secondo scopo di questa animazione è impedire a un utente di inviare spam alla funzione di trasmissione.
  • Aggiungi un blocco "numero di invio radio (X)", dove è la costante del segnale menzionata nel nome della funzione

Questo è tutto ciò che serve per trasmettere alla radio.

Passaggio 8: vogliamo ascoltare e ricevere dati sulla radio del dispositivo ed elaborarli di conseguenza

Vogliamo ascoltare e ricevere dati sulla radio del dispositivo ed elaborarli di conseguenza
Vogliamo ascoltare e ricevere dati sulla radio del dispositivo ed elaborarli di conseguenza
Vogliamo ascoltare e ricevere dati sulla radio del dispositivo ed elaborarli di conseguenza
Vogliamo ascoltare e ricevere dati sulla radio del dispositivo ed elaborarli di conseguenza

Questo è il passaggio finale per creare l'applicazione principale.

Diremo al dispositivo come elaborare i segnali radio in entrata. Innanzitutto, il nostro dispositivo chiamerà il segnale ricevuto. Quindi, in base al valore di quel segnale, deciderà quale azione intraprendere, se del caso.

Primo:

  1. Crea un blocco di codice che inizia con un blocco "ricevuto via radio (X)".
  2. Facoltativamente, assegna il valore ricevuto a un'altra variabile con un nome più descrittivo.
  3. Chiama una funzione che elaborerà il segnale

In secondo luogo, nella funzione di elaborazione del segnale:

  1. Crea un blocco di istruzioni if-else che diramano il flusso di controllo in base al valore del segnale.
  2. Se il segnale era SIG_R

    Imposta lo stato del dispositivo su BOOT_STATE (questo è il motivo per cui abbiamo creato questa costante in precedenza)

  3. Se il segnale era SIG_A e se lo stato corrente è LISTEN_A

    Imposta lo stato del dispositivo su TEAM_A

  4. Se il segnale era SIG_B e se lo stato corrente è LISTEN_B

    Imposta lo stato del dispositivo su TEAM_B

Questo è tutto. L'applicazione è terminata.

Passaggio 9: dispositivo root: vogliamo essere in grado di selezionare un segnale

Dispositivo root: vogliamo essere in grado di selezionare un segnale
Dispositivo root: vogliamo essere in grado di selezionare un segnale

Ora scriveremo una semplice applicazione per un dispositivo "root", vale a dire un dispositivo che controllerà la rete.

Questo dispositivo dovrà svolgere due funzioni:

  • Vogliamo consentire all'utente di selezionare uno dei nostri segnali
  • Vogliamo consentire all'utente di trasmettere il segnale

Poiché le specifiche di questa applicazione sono un sottoinsieme delle precedenti, fornirò una panoramica ma non entrerò nei dettagli come prima. L'immagine sopra contiene il codice completo per questa applicazione.

Per consentire all'utente di selezionare un segnale:

  1. Inizializzare 5 variabili in un blocco "on start":

    1. I tre segnali (0, 1, 2)
    2. Il numero di segnali (3)
    3. Una variabile per contenere il segnale attualmente selezionato (inizialmente impostato sul primo segnale, 0)
  2. Gestire una pressione del pulsante A:

    1. Incrementa il segnale selezionato
    2. Controllare se il segnale selezionato è maggiore o uguale al numero di segnali

      In tal caso, impostare il segnale selezionato su 0

  3. Dopo il blocco di avvio, eseguire un ciclo "per sempre" che visualizza il valore del segnale attualmente selezionato senza ritardi

Per consentire all'utente di trasmettere un segnale

  1. Imposta il gruppo radio a 0 nel blocco "on start"
  2. Gestire una pressione del pulsante B:

    Trasmetti il segnale selezionato utilizzando un blocco "numero invio radio (X)"

Questo è tutto. L'applicazione del nodo radice è estremamente semplice.

Passaggio 10: abbiamo finito

Abbiamo finito
Abbiamo finito

Sopra c'è un'immagine dei dispositivi che eseguono l'applicazione. I due a destra eseguono l'applicazione "utente" principale e quello a sinistra esegue l'applicazione "root".

Ho dimostrato questo gioco a CS Connections 2018, una conferenza estiva di una settimana per insegnanti di scuole medie e superiori sull'educazione informatica. Ho distribuito circa 40 dispositivi agli insegnanti e spiegato le regole. La maggior parte ha trovato il gioco divertente e molti lo hanno trovato confuso finché non hanno capito come giocare. La dimostrazione è stata breve, ma abbiamo trovato il gioco divertente tra un pubblico abbastanza vario.

Ulteriori informazioni su CS Connections 2018 sono disponibili qui.

Consigliato: