Inhaltsverzeichnis:

Netzwerkrivalität: ein Spiel mit geringer Latenz für die BBC Micro:bit - Gunook
Netzwerkrivalität: ein Spiel mit geringer Latenz für die BBC Micro:bit - Gunook

Video: Netzwerkrivalität: ein Spiel mit geringer Latenz für die BBC Micro:bit - Gunook

Video: Netzwerkrivalität: ein Spiel mit geringer Latenz für die BBC Micro:bit - Gunook
Video: Ping verbessern & reduzieren - schnell und einfach | ArtisGraphics 2024, November
Anonim
Netzwerkrivalität: ein Spiel mit geringer Latenz für die BBC Micro:bit
Netzwerkrivalität: ein Spiel mit geringer Latenz für die BBC Micro:bit
Netzwerkrivalität: ein Spiel mit geringer Latenz für die BBC Micro:bit
Netzwerkrivalität: ein Spiel mit geringer Latenz für die BBC Micro:bit

In diesem Tutorial erkläre ich, wie man ein einfaches Multiplayer-Spiel auf dem BBC micro:bit mit den folgenden Features implementiert:

  • Eine einfache Schnittstelle
  • Geringe Latenz zwischen Tastendruck und Bildschirmaktualisierungen
  • Eine flexible Teilnehmerzahl
  • Einfache Steuerung des Spiels mit einer Master-Fernbedienung ("root")

Das Spiel ist im Wesentlichen eine Simulation von Politik. Alle Spieler starten ohne Zuordnung zu einem Team, mit Ausnahme von zwei Spielern. Einer dieser Spieler wird Team A zugewiesen und der andere wird Team B zugewiesen.

Das Ziel des Spiels für jeden Spieler ist es, zu dem Zeitpunkt, an dem alle konvertiert sind, im Team mit der Mehrheit der Spieler zu sein.

Das obige Diagramm zeigt einen endlichen Automaten, d. h. eine Spezifikation der Zustände, in denen sich das Gerät befinden kann, und der Übergänge zwischen diesen Zuständen.

Ein Zustand kann man sich als den aktuellen Datensatz vorstellen, der den Speicher des Geräts seit dem Einschalten beschreibt. Basierend auf diesen Daten kann das Gerät bestimmte Aktionen ausführen oder anders auf Benutzereingaben reagieren.

Ein Übergang ist eine logische Bedingung, die, wenn sie wahr ist, dazu führt, dass das Gerät seinen Zustand ändert. Ein Übergang kann von einem Zustand in einen anderen Zustand erfolgen. Ein Zustand kann mehrere Übergänge haben.

Das obige Diagramm spezifiziert die folgenden Zustände:

  • Nicht zugewiesen
  • Höre auf A
  • Höre auf B
  • Team A
  • Team B

Ein Gerät, auf dem der Spielcode ausgeführt wird, kann sich in einem dieser fünf Zustände befinden, jedoch nur in einem und nur in diesen fünf.

Ich gehe im gesamten Handbuch davon aus, dass Sie den MakeCode-Editor von Microsoft verwenden, den Sie unter https://makecode.microbit.org finden

Die vollständige Implementierung des Spiels finden Sie hier:

makecode.microbit.org/_CvRMtheLbRR3 ("microbit-demo-user" ist der Projektname)

Und die Implementierung des Master-Netzwerk-Controllers ("root") finden Sie hier:

makecode.microbit.org/_1kKE6TRc9TgE ("microbit-demo-root" ist der Projektname)

Ich werde mich in meinem Tutorial auf diese Beispiele beziehen.

Schritt 1: Überlegungen zum Big Picture-Design

Bevor wir Code schreiben, müssen wir uns überlegen, wie unser Endprodukt aussehen soll. Mit anderen Worten, was sind die Anforderungen an die Bewerbung? Was soll unser Code dem Gerät mitteilen, wenn es fertig ist? Die Funktionalität der Hauptanwendung habe ich in sechs Kategorien eingeteilt, die jeweils aus einer anderen gestalterischen Perspektive betrachtet werden können.

  1. Wir möchten die Aktionen des Geräts basierend auf seinem aktuellen Zustand steuern
  2. Wir möchten, dass das Gerät auf Benutzereingaben reagiert
  3. Vielleicht möchten wir Animationen und Grafiken mit dem 5 x 5 LED-Display anzeigen
  4. Wir möchten beim Hochfahren des Geräts Datenwerte im Gerätespeicher initialisieren
  5. Wir wollen Daten drahtlos über das Funkgerät des Geräts übertragen
  6. Wir wollen über das Funkgerät des Geräts Daten abhören, empfangen und entsprechend verarbeiten

Erlauben Sie mir, auf jeden einzelnen ein wenig mehr einzugehen.

1. Wir möchten die Aktionen des Geräts basierend auf seinem aktuellen Zustand steuern

Wie bei den meisten anderen Programmen erfolgt die Ausführung der vom Code angegebenen Anweisungen zeilenweise. Wir möchten, dass unser Gerät basierend auf seinem internen Zustand bestimmte Anweisungen ausführt, wie im Diagramm oben in diesem Tutorial veranschaulicht. Wir könnten nach jedem Codeblock, der das Gerät überprüft, eine Reihe von Bedingungen schreiben, aber dieser Ansatz kann sehr schnell sehr unübersichtlich werden, führt einen bestimmten Satz von Anweisungen aus oder tut überhaupt nichts. Diese Variable wird sowohl in unserer Benutzeranwendung als auch in unserer Root-Anwendung durch das Suffix "_state" identifiziert.

2. Wir möchten, dass das Gerät auf Benutzereingaben reagiert

Obwohl die normale Ausführung des Codes sequentiell erfolgt, dh Zeile für Zeile, muss unser Gerät auf Tastendrücke reagieren, während die Hauptzustandsschleife bestimmt, was das Gerät zu einem bestimmten Zeitpunkt tun soll. Zu diesem Zweck hat das Gerät die Fähigkeit, Signale an die untergeordnete Software zu senden, die mit der Hardware interagiert und ein sogenanntes Ereignis auslöst. Wir können Code schreiben, der dem Gerät sagt, dass es etwas tun soll, wenn es einen bestimmten Ereignistyp erkennt.

3. Wir möchten Animationen und Grafiken mit dem 5 x 5 LED-Display anzeigen

Der Mechanismus dazu scheint einfach zu sein, aber der Block, der ein Bild anzeigt, fügt eine versteckte Verzögerung von 400 ms hinzu. Da wir möchten, dass unser Gerät seine Zustandsschleife weiterhin mit so geringer Latenz wie möglich ausführt, müssen wir den Javascript-Code bearbeiten, um die Verzögerung zu minimieren.

4. Wir möchten beim Hochfahren des Geräts Datenwerte im Gerätespeicher initialisieren

Bevor unser Gerät etwas tut, muss die Anwendung ihre Daten in den Speicher laden. Dazu gehören konstante Variablen, die nach der Lesbarkeit des Codes benannt sind, Variablen, die Bilder enthalten, die Teil einer Animation sein können, und Zählervariablen, die bei 0 beginnen müssen, um richtig zu funktionieren. Am Ende erhalten wir eine lange Liste von Variablennamen und ihren neu zugewiesenen Werten. Als persönliche Stilwahl bezeichne ich konstante Werte, d. h. Werte, die ich nie ändern muss, mit ALL_CAPS. Ich werde den Bezeichnern von Hauptvariablen auch einen Kategorienamen voranstellen, der sich auf eine Art von Objekt oder Typ bezieht, unter den der Bezeichner fällt. Dies ist der Versuch, dem Code leichter zu folgen. Ich werde niemals einen Variablennamen wie "item" oder "x" verwenden, da beim Versuch, den Code zu entschlüsseln, Mehrdeutigkeiten auftreten.

5. Wir möchten Daten drahtlos über das Funkgerät des Geräts übertragen

Dies ist eigentlich eine ziemlich einfache Aufgabe, wenn Sie die MakeCode-Blocksprache verwenden. Wir stellen einfach alle Geräte beim Booten auf die gleiche Funkgruppe und wenn wir dann ein Signal senden möchten, können wir eine einzige Nummer an den uns bereitgestellten Block "Radio send number" übergeben. Es ist wichtig, dass Sender und Empfänger an derselben Funkgruppe arbeiten, da sie andernfalls auf unterschiedlichen Frequenzen senden oder empfangen und die Kommunikation fehlschlägt.

6. Wir wollen Daten über das Funkgerät des Geräts abhören und empfangen und entsprechend verarbeiten

Unter Berücksichtigung der gleichen Überlegungen wie im vorherigen Punkt werden wir auf eingehende Übertragungen genauso wie auf Benutzereingaben lauschen: mit einem Ereignishandler. Wir werden einen Codeblock schreiben, der alle eingehenden Signale untersucht und prüft, ob Maßnahmen ergriffen werden müssen, ohne die Hauptzustandsschleife zu stören.

Darüber hinaus sollten wir kurz das Design der viel einfacheren Root-Anwendung betrachten, eines Programms, mit dem ein Gerät das gesamte Netzwerk steuern kann. Ich werde nicht viel Zeit damit verbringen, da es viel einfacher ist als das obige Design und vieles davon einfach Wiederholung ist. Ich habe die Funktionalität des Wurzeldeices in drei Kategorien unterteilt.

  1. Wir wollen ein Signal auswählen können
  2. Wir wollen ein Signal senden können

-

1. Wir wollen ein Signal auswählen können

Dies kann durch einfaches Durchlaufen einer Schaltfläche durch die möglichen Signale erfolgen. Da es nur drei gibt, reicht dieser Ansatz aus. Gleichzeitig können wir eine Schleife haben, die das ausgewählte Signal ständig neu anzeigt, sodass der Benutzer eine Taste drücken kann und das ausgewählte Signal mit sehr geringer Latenz auf dem LED-Display angezeigt wird.

2. Wir wollen ein Signal senden können

Da es zwei Schaltflächen gibt, können wir eine zur Auswahl und die andere zur Bestätigung festlegen. Wie die Benutzeranwendung senden wir das Signal einfach als Nummer über das Netzwerk. Es sind keine weiteren Angaben erforderlich.

Ich werde im nächsten Abschnitt mehr über das einfache Signalprotokoll sprechen.

Schritt 2: Das Signalprotokoll: eine einfache Sprache für die Netzwerkkommunikation

Die folgenden Signale kann man sich als die Menge aller möglichen Wörter vorstellen, die die Geräte verwenden können, um miteinander zu sprechen. Da das Netzwerk so einfach ist, gibt es nicht viel zu sagen, und so können wir diese drei Signale durch einfache ganzzahlige Werte darstellen.

0. Zurücksetzen

  • Kennung im Code: SIG-R
  • Ganzzahliger Wert: 0
  • Zweck: Sagen Sie allen Geräten in Reichweite, dass sie ihre Aktivitäten unterbrechen und so tun sollen, als ob sie gerade hochgefahren wären. Wenn dieses Signal jedes Gerät im Netzwerk erreicht, wird das gesamte Netzwerk zurückgesetzt und die Benutzer können ein neues Spiel starten. Dieses Signal kann nur von einem Root-Gerät gesendet werden.

1. Umrechnung A

  • Kennung im Code: SIG-A
  • Ganzzahliger Wert: 1
  • Zweck: Sagen Sie jedem Gerät, das sich im Zustand LISTEN_A befindet, sobald es das Umwandlungssignal empfängt, in den Zustand TEAM_A zu wechseln.

2. Umrechnung B

  1. Kennung im Code: SIG-B
  2. Ganzzahliger Wert: 2
  3. Zweck: Sagen Sie jedem Gerät, das sich im Zustand LISTEN_B befindet, sobald es das Umwandlungssignal empfängt, in den Zustand TEAM_B zu wechseln.

Schritt 3: Wir möchten die Aktionen des Geräts basierend auf seinem aktuellen Status steuern

Wir möchten die Aktionen des Geräts basierend auf seinem aktuellen Zustand steuern
Wir möchten die Aktionen des Geräts basierend auf seinem aktuellen Zustand steuern
Wir möchten die Aktionen des Geräts basierend auf seinem aktuellen Zustand steuern
Wir möchten die Aktionen des Geräts basierend auf seinem aktuellen Zustand steuern
Wir möchten die Aktionen des Geräts basierend auf seinem aktuellen Zustand steuern
Wir möchten die Aktionen des Geräts basierend auf seinem aktuellen Zustand steuern

Endlich können wir mit dem Schreiben von Code beginnen.

Öffnen Sie zunächst ein neues Projekt in Make Code

  • Erstellen Sie eine neue Funktion. Ich habe meine Schleife genannt, weil dies die Kernschleife der Anwendung ist
  • Fügen Sie einen Schleifenblock hinzu, der unbegrenzt wiederholt wird. Ich habe while(true) verwendet, weil ein literales true nie falsch sein wird, daher wird der Kontrollfluss der Anwendung die Schleife nie verlassen
  • Fügen Sie genügend if-else-Blöcke hinzu, um zu überprüfen, ob sich das Gerät in einem der fünf möglichen Zustände befindet
  • Erstellen Sie eine Variable, um den aktuellen Gerätestatus zu speichern
  • Erstellen Sie Variablen, um jeden der fünf möglichen Zustände darzustellen

    Hinweis: Es ist in Ordnung, dass diesen Variablen noch keine Werte zugewiesen sind. Wir werden dazu kommen. An dieser Stelle ist es wichtiger, dass wir sauberen, leicht lesbaren Code schreiben

  • Ändern Sie jede Bedingung in den if-else-Blöcken, um den aktuellen Zustand mit einem der möglichen Zustände zu vergleichen
  • Fügen Sie unten in den if-else-Blöcken eine Pause für einige Millisekunden hinzu und erstellen Sie eine Variable, die diese Zahl enthält. Wir werden es später initialisieren. Stellen Sie sicher, dass die Variable einen beschreibenden Namen hat, z. B. Tick oder Heartbeat. Da dies die Kernschleife des Geräts ist, bestimmt diese Pause die Geschwindigkeit, mit der das Gerät die Hauptschleife ausführt, daher ist dies ein sehr wichtiger Wert und zu wichtig, um eine magische Zahl ohne Namen zu sein.

Hinweis: Machen Sie sich keine Sorgen über die grauen Blöcke im dritten Bild. Zu denen komme ich später.

Schritt 4: Wir möchten auf Benutzereingaben reagieren

Wir möchten auf Benutzereingaben reagieren
Wir möchten auf Benutzereingaben reagieren
Wir möchten auf Benutzereingaben reagieren
Wir möchten auf Benutzereingaben reagieren

Jetzt möchten wir dem Gerät mitteilen, wie es mit Tastendrücken umgeht. Der erste Gedanke könnte sein, einfach die Blöcke "Wenn die Taste gedrückt wird" in der Kategorie "Eingabe" zu verwenden, aber wir würden uns eine genauere Steuerung wünschen. Wir werden den Block "on event from (X) with value (Y)" aus der Control-Kategorie im erweiterten Abschnitt verwenden, da wir in diesem Tutorial fortgeschritten sind.

  • Erstellen Sie vier "Bei Ereignis von…"-Blöcke.

    • Zwei davon sollten die Ereignisquelle "MICROBIT_ID_BUTTON_A" überprüfen.
    • Zwei davon sollten die Ereignisquelle "MICROBIT_ID_BUTTON_B" überprüfen.
    • Von den beiden Ereignissen, die auf jede Schaltfläche abzielen:

      • Es sollte nach dem Ereignis vom Typ "MICROBIT_BUTTON_EVT_UP" gesucht werden.
      • Es sollte nach dem Ereignis vom Typ "MICROBIT_BUTTON_EVT_DOWN" gesucht werden.
    • Hinweis: Diese Optionen in Großbuchstaben sind Beschriftungen, die im untergeordneten micro:bit-Code verwendet werden. Sie sind einfach Platzhalter, die später durch Ganzzahlen ersetzt werden, wenn der Code in eine ausführbare Binärdatei kompiliert wird. Es ist für Menschen einfacher, diese Labels zu verwenden, als nachzusehen, welche ganze Zahl eingegeben werden muss, obwohl beide auf die gleiche Weise funktionieren würden.
  • Aus Stilgründen habe ich mich dafür entschieden, dass jeder "on event from…"-Block eine Funktion aufruft, die das ausgelöste Ereignis beschreibt. Obwohl dies nicht unbedingt erforderlich ist, verbessert dies meiner Meinung nach die Lesbarkeit. Wenn Sie dies wünschen, können Sie den Ereignisbehandlungscode in den Block "on event from…" selbst einfügen.

    Hinweis: Der Codeblock, der die Reaktion des Geräts auf ein Ereignis verarbeitet, wird intuitiv als "Ereignishandler" bezeichnet

  • Fügen Sie in jedem Ereignishandler dieselbe if-else-Struktur hinzu, die verwendet wird, um den Kontrollfluss basierend auf dem Gerätezustand aufzuteilen, wie die Struktur in der Hauptzustandsschleife.
  • Fügen Sie Zuweisungsblöcke hinzu, die diesen Zustand des Geräts ändern, wie in unserem Zustandsdiagramm angegeben

    • Wir wissen, dass das Gerät, wenn sich das Gerät im Zustand UNASSIGNED befindet, auf das Drücken von Taste A durch einen Übergang in den Zustand LISTEN_A und auf das Drücken von Taste B durch einen Übergang in den Zustand LISTEN_B reagieren sollte
    • Wir wissen auch, dass das Gerät, wenn sich das Gerät im Zustand LISTEN_A oder LISTEN_B befindet, auf das Loslassen von Taste A bzw.
    • Schließlich wissen wir, dass, wenn sich das Gerät im Zustand TEAM_A oder TEAM_B befindet, das Gerät auf das Drücken der Taste A und das Drücken der Taste B durch Senden von SIG_A bzw. durch Senden von SIG_B reagieren sollte.

      Es ist an dieser Stelle nicht erforderlich, die Einzelheiten der Sendesignale einzutragen. Dazu kommen wir später. Wichtig ist, dass wir diese Funktionen anweisen, den Code zu verwenden, den wir schreiben werden, indem wir diesem Aktionsblock einen Namen geben, wie etwa BroadcastSignalSIG_A, der beschreibt, was an diesem Punkt zu tun ist

Schritt 5: Wir möchten Datenwerte im Gerätespeicher initialisieren, wenn das Gerät hochfährt

Wir möchten Datenwerte im Gerätespeicher initialisieren, wenn das Gerät hochfährt
Wir möchten Datenwerte im Gerätespeicher initialisieren, wenn das Gerät hochfährt
Wir möchten Datenwerte im Gerätespeicher initialisieren, wenn das Gerät hochfährt
Wir möchten Datenwerte im Gerätespeicher initialisieren, wenn das Gerät hochfährt
Wir möchten Datenwerte im Gerätespeicher initialisieren, wenn das Gerät hochfährt
Wir möchten Datenwerte im Gerätespeicher initialisieren, wenn das Gerät hochfährt

An dieser Stelle haben wir viele Variablen (Namen für Daten) verwendet, aber wir haben diesen Namen keine Werte zugewiesen. Wir möchten, dass das Gerät beim Booten die Werte all dieser Variablen in den Speicher lädt, daher platzieren wir die Initialisierung für diese Variablen in einem "beim Start"-Block.

Dies sind die Werte, die wir initialisieren müssen:

  • Signalkonstanten gemäß Signalprotokoll. Die Werte MÜSSEN sein:

    • SIG_R = 0
    • SIG_A = 1
    • SIG_B = 2
    • Hinweis: Ich habe diesen Konstanten "EnumSignals" vorangestellt, um anzuzeigen, dass sich diese Variablen so verhalten sollen, als wären sie Teil eines Aufzählungstyps namens Signals. Auf diese Weise können diese Variablen in anderen Programmiersprachen implementiert werden. Die Definition und Erläuterung von Aufzählungstypen würde den Rahmen meines Tutorials sprengen. Man kann es googeln, wenn sie es wünschen. Diese Präfixe sind lediglich stilistische Entscheidungen und für das ordnungsgemäße Funktionieren des Programms nicht unbedingt erforderlich.
  • Zustandskonstanten, die beliebig sein können, solange sie einen Wert haben. Ich habe eine Stilauswahl getroffen, um einfach Ganzzahlen von 0 aufsteigend zu verwenden, wie folgt:

    • NICHT ZUGEWIESEN = 0
    • HÖREN_A = 1
    • HÖREN_B = 2
    • TEAM_A = 3
    • TEAM_B = 4
    • Hinweis: Ich habe die gleiche Stilentscheidung bezüglich der Präfixe für diese Variablen getroffen. Außerdem werde ich erwähnen, dass alles an diesen Zuordnungen, den Werten und der Reihenfolge, völlig willkürlich ist. Dabei spielt es auch keine Rolle, dass diese Werte von Gerät zu Gerät konsistent sind, da sie nur intern und nicht für die Kommunikation über das Netzwerk verwendet werden. Wichtig ist nur, dass die Variablen einen Wert haben und dass sie miteinander verglichen werden können, um zu sehen, ob sie gleichwertig sind oder nicht.
  • Zur besseren Lesbarkeit eine Konstante namens BOOT_STATE und setzen Sie sie auf UNASSIGNED. Dies macht die Tatsache, dass wir auf den Boot-Zustand zurücksetzen, anstatt auf einen willkürlichen Zustand, deutlicher, wenn das Gerät ein Reset-Signal empfängt, das wir später implementieren werden.
  • Animationskonstanten, die im folgenden Schritt verwendet werden, um Animationen zu erstellen, die eine Unterbrechung mit extrem geringer Latenz über Benutzereingaben ermöglichen. Diese haben wir bisher noch nicht verwendet, aber sie werden sicherlich im folgenden Abschnitt erklärt und verwendet. Die Bedeutung einiger davon sollte aufgrund ihrer Namen intuitiv sein.

    • 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
  • Eine weitere Variable für die Animation, diesmal ein Zähler, der definitiv nicht konstant ist. Wie die meisten Zähler initialisieren wir ihn auf 0

    iTickLoadingAnimation = 0

  • Erstellen Sie zwei Serien von Variablen, um Frames von Animationen aufzunehmen. Die erste, die ich "Ladeanimation" nenne, sollte vier Bilder haben (die Sie vielleicht durch die letzte konstante Initialisierung erraten haben), und die zweite, die ich "Broadcast-Animation" nenne, die drei Bilder haben sollte. Ich empfehle, die Variablen so zu benennen, dass sie den Frames der Animation entsprechen, z. B. ringAnimation0, ringAnimation1…

    Erstellen Sie die gleichen Bildwerte wie ich oder erstellen Sie originellere und coolere Bilder

  • Zu guter Letzt müssen wir die Funkgruppe des Geräts über den Block „Funkgruppe (X)“auf 0 setzen
  • Schreiben Sie optional die Nachricht "Initialisierung abgeschlossen" in die serielle Ausgabe, um dem Benutzer mitzuteilen, dass alles reibungslos gelaufen ist.
  • Nachdem wir das Gerät eingerichtet haben, können wir unsere Zustandsschleifenfunktion aufrufen.

Schritt 6: Wir möchten Animationen und Grafiken mit dem 5 x 5 LED-Display anzeigen

Wir möchten Animationen und Grafiken mit dem 5 x 5 LED-Display anzeigen
Wir möchten Animationen und Grafiken mit dem 5 x 5 LED-Display anzeigen
Wir möchten Animationen und Grafiken mit dem 5 x 5 LED-Display anzeigen
Wir möchten Animationen und Grafiken mit dem 5 x 5 LED-Display anzeigen
Wir möchten Animationen und Grafiken mit dem 5 x 5 LED-Display anzeigen
Wir möchten Animationen und Grafiken mit dem 5 x 5 LED-Display anzeigen

Und jetzt etwas ganz anderes.

Wir wollen ein paar Animationen und ein paar Charaktere anzeigen, aber wir wollen die Hauptzustandsschleife nicht unterbrechen. Leider haben die Blöcke, die Bilder und Textzeichenfolgen anzeigen, standardmäßig eine Verzögerung von 400 ms. Es gibt keine Möglichkeit, dies zu ändern, ohne die Javascript-Darstellung des Codes zu bearbeiten. Das werden wir also tun.

  • Erstellen Sie für jedes Bild eine Funktion. Dies ermöglicht es einem, einen einzelnen Block zu verwenden, um das Bild anzuzeigen, anstatt jedes Mal Javascript zu bearbeiten. In diesem speziellen Programm wird kein Bild mehr als einmal verwendet, aber ich denke trotzdem, dass dieser Stil den Code leichter lesbar macht.
  • Fügen Sie in jeder neuen Funktion einen Block "Bild anzeigen (X) bei Offset 0" hinzu, wobei der entsprechende Bildvariablenname (X) ersetzt.
  • Fügen Sie in der Hauptzustandsschleife hinzu. "Show string (X)" blockiert jeden Block außer dem, der den Status UNASSIGNED behandelt. Fügen Sie ein Zeichen hinzu, das das Gerät anzeigen soll, um seine verschiedenen Zustände anzuzeigen. Folgendes habe ich getan:

    • LISTEN_A: 'a'
    • HÖREN_B: 'b'
    • TEAM_A: 'A'
    • TEAM_B: 'B'

      Rufen Sie für den Zustand UNASSIGNED eine Funktion auf, die die Ladeanimation aktualisiert. Wir werden die Details dieser Funktion unten ausfüllen

  • Wechseln Sie in den Javascript-Modus.
  • Finde jeden Aufruf von X.showImage(0) und basic.showString(X)
  • Ändern Sie jeden einzelnen in entweder X.showImage(0, 0) oder basic.showString(X, 0)

    • Durch Hinzufügen dieses zusätzlichen Arguments wird die Verzögerung nach der Aktion auf 0 gesetzt. Standardmäßig wird dies ausgelassen und das Gerät hält nach der Ausführung jedes dieser Blöcke 400 ms lang an.
    • Jetzt haben wir einen nahezu latenzfreien Mechanismus, um unsere Bilder in unseren Animationsblöcken anzuzeigen, die wir jetzt bauen können

Zuerst bauen wir die relativ einfache Broadcast-Animationsfunktion. Es ist einfacher, weil wir nicht möchten, dass der Benutzer etwas tun kann, bis die Funktion abgeschlossen ist, um ihn davon abzuhalten, die Broadcast-Funktion zu spammen. Um dies zu erreichen, können wir den Kontrollfluss einfach auf den Block beschränken, bis die Funktion abgeschlossen ist, was das Standardverhalten ist.

  • Erstellen Sie eine Funktion, die eine Broadcast-Animation anzeigt.
  • Fügen Sie in diesem Block drei Funktionsaufrufe hinzu, einen zu jedem Frame der Animation, in der Reihenfolge, in der sie angezeigt werden sollen
  • Fügen Sie nach jedem Aufruf einer Bildanzeigefunktion einen "wait (us) (X)"-Block hinzu.

    Hinweis: Dieser Block aus dem erweiterten Steuerungsabschnitt geht noch weiter als "Pause (ms)", da er den Prozessor vollständig einfriert, bis die angegebene Zeit abgelaufen ist. Bei Verwendung des Pausenblocks ist es möglich, dass das Gerät hinter den Kulissen andere Aufgaben übernimmt. Dies ist mit dem Warteblock nicht möglich

  • Ersetzen Sie (X) durch (MS_PER_FRAME_BROADCAST_ANIMATION x MICROSECONDS_PER_MILLISECOND)
  • Die Animation sollte jetzt richtig funktionieren

Zweitens bauen wir den Mechanismus zum Anzeigen der Ladeanimation auf. Die Idee dahinter ist, die LED-Anzeige in einem bestimmten Intervall zu aktualisieren, das wir in der Variablen MS_PER_DEVICE_TICK definieren. Dieser Wert, die Geräteticklänge, ist die Anzahl der Millisekunden, die das Gerät nach Abschluss jeder Iteration der Zustandsschleife pausiert. Da dieser Wert klein genug ist, können wir die Anzeige bei jeder Iteration der Anzeigeschleife einmal aktualisieren und es wird dem Benutzer angezeigt, dass die Animation nahtlos fortschreitet, und wenn sich der Zustand ändert, gibt es sehr wenig Latenz zwischen den Eingaben des Benutzers die Anzeige wird aktualisiert. Durch das Zählen von Ticks, was wir mit der Variable iTickLoadingAnimation machen, können wir den entsprechenden Frame der Animation anzeigen.

  • Erstellen Sie eine Funktion, die die Ladeanimation aktualisiert
  • Fügen Sie eine Bedingung hinzu, um zu überprüfen, ob der Tick-Zähler seinen Maximalwert erreicht hat. Diese Bedingung ist erfüllt, wenn der Wert des Tick-Zählers größer ist als die Anzahl der Frames in der Ladeanimation multipliziert mit der Anzahl der Ticks, um jeden Frame anzuzeigen

    Wenn die Bedingung wahr ist, setzen Sie iTickLoadingAnimation auf 0 zurück

  • Fügen Sie einen Block von if-else-Bedingungen hinzu. Diese bestimmen, welcher Frame der Animation angezeigt werden soll.

    Wenn der Tickzähler für jeden Frame der Animation kleiner ist als die Anzahl der Ticks in jeder Animation multipliziert mit der Framenummer der Animation (beginnend mit 1), dann zeigen Sie diesen Frame an, andernfalls prüfen Sie, ob der nächste Frame derjenige ist, der angezeigt werden

  • Erhöhen Sie am unteren Rand des Blocks iTickLoadingAnimation
  • Die Animation sollte jetzt richtig funktionieren

Hinweis: Alle grauen Blöcke, die in meinem Beispiel erscheinen, werden generiert, wenn man die Javascript-Darstellung eines Blocks bearbeitet. Es bedeutet einfach, dass der Block Javascript-Code darstellt, der nicht mit dem Standardsatz von Blöcken dargestellt werden kann und in Textform bearbeitet werden muss.

Schritt 7: Wir möchten Daten drahtlos mit dem Radio des Geräts übertragen

Wir möchten Daten drahtlos über das Funkgerät des Geräts übertragen
Wir möchten Daten drahtlos über das Funkgerät des Geräts übertragen

Dieser Schritt ist viel kürzer als der vorherige. Tatsächlich ist dies wahrscheinlich der kürzeste Schritt in diesem gesamten Tutorial.

Denken Sie daran, dass ich beim Programmieren der Reaktion des Geräts auf Benutzereingaben zwei Blöcke im Screenshot hatte, die in diesem Abschnitt nicht erklärt wurden. Dies waren Aufrufe an Funktionen, die Signale über das Radio senden. Genauer:

  • Auf Taste A gedrückt:

    • Wenn sich das Gerät im Zustand TEAM_A befindet:

      Sendesignal SIG_A

  • Auf Taste B gedrückt:

    • Wenn sich das Gerät im Zustand TEAM_B befindet

      Sendesignal SIG_B

Erstellen Sie diese Funktionen, falls sie noch nicht vorhanden sind.

In jeder Funktion:

  • Rufen Sie die Broadcast-Animationsfunktion auf. Dadurch wird verhindert, dass alles andere passiert, bis es abgeschlossen ist, was in MS_PER_FRAME_BROADCAST_ANIMATION * 3 = 1,5 Sekunden liegt. Die Konstante wird mit drei multipliziert, da die Animation drei Frames enthält. Dies ist willkürlich und es können weitere hinzugefügt werden, wenn das ästhetische Upgrade groß genug ist. Ein zweiter Zweck dieser Animation besteht darin, einen Benutzer daran zu hindern, die Broadcast-Funktion zu spammen.
  • Fügen Sie einen Block "Funksendenummer (X)" hinzu, wo die im Funktionsnamen erwähnte Signalkonstante ist

Das ist alles, was man braucht, um über das Radio zu senden.

Schritt 8: Wir möchten Daten über das Funkgerät des Geräts abhören und empfangen und entsprechend verarbeiten

Wir möchten Daten über das Funkgerät des Geräts abhören und empfangen und entsprechend verarbeiten
Wir möchten Daten über das Funkgerät des Geräts abhören und empfangen und entsprechend verarbeiten
Wir möchten Daten über das Funkgerät des Geräts abhören und empfangen und entsprechend verarbeiten
Wir möchten Daten über das Funkgerät des Geräts abhören und empfangen und entsprechend verarbeiten

Dies ist der letzte Schritt zum Erstellen der Hauptanwendung.

Wir werden dem Gerät sagen, wie es eingehende Funksignale verarbeitet. Zuerst wird unser Gerät das empfangene Signal benennen. Dann wird basierend auf dem Wert dieses Signals entschieden, welche Aktion gegebenenfalls durchgeführt wird.

Zuerst:

  1. Erstellen Sie einen Codeblock, der mit einem Block "On Radio Received (X)" beginnt.
  2. Weisen Sie diesen empfangenen Wert optional einer anderen Variablen mit einem aussagekräftigeren Namen zu.
  3. Rufen Sie eine Funktion auf, die das Signal verarbeitet

Zweitens in der Signalverarbeitungsfunktion:

  1. Erstellen Sie einen Block von if-else-Anweisungen, die den Kontrollfluss basierend auf dem Wert des Signals verzweigen.
  2. Wenn das Signal SIG_R. war

    Setzen Sie den Status des Geräts auf BOOT_STATE (aus diesem Grund haben wir diese Konstante zuvor erstellt)

  3. Wenn das Signal SIG_A war und der aktuelle Status LISTEN_A ist

    Setzen Sie den Status des Geräts auf TEAM_A

  4. Wenn das Signal SIG_B war und der aktuelle Status LISTEN_B ist

    Setzen Sie den Status des Geräts auf TEAM_B

Das ist es. Die Bewerbung ist fertig.

Schritt 9: Root-Gerät: Wir möchten ein Signal auswählen können

Root-Gerät: Wir möchten in der Lage sein, ein Signal auszuwählen
Root-Gerät: Wir möchten in der Lage sein, ein Signal auszuwählen

Jetzt schreiben wir eine einfache Anwendung für ein "Root"-Gerät, dh ein Gerät, das das Netzwerk steuert.

Dieses Gerät muss zwei Funktionen ausführen:

  • Wir möchten es dem Benutzer ermöglichen, eines unserer Signale auszuwählen
  • Wir möchten es dem Benutzer ermöglichen, das Signal zu senden

Da die Spezifikation dieser Anwendung eine Untermenge der vorherigen ist, werde ich einen Überblick geben, aber nicht so detailliert wie zuvor. Das obige Bild enthält den vollständigen Code für diese Anwendung.

Um dem Benutzer die Auswahl eines Signals zu ermöglichen:

  1. Initialisieren Sie 5 Variablen in einem "beim Start"-Block:

    1. Die drei Signale (0, 1, 2)
    2. Die Anzahl der Signale (3)
    3. Eine Variable zum Halten des aktuell ausgewählten Signals (anfänglich auf das erste Signal eingestellt, 0)
  2. Behandeln Sie einen Druck auf den A-Knopf:

    1. Erhöhen Sie das ausgewählte Signal
    2. Prüfen Sie, ob das ausgewählte Signal größer oder gleich der Anzahl der Signale ist

      Wenn ja, setzen Sie das ausgewählte Signal auf 0

  3. Führen Sie nach dem Ein-Start-Block eine "Forever"-Schleife aus, die den aktuell ausgewählten Signalwert ohne Verzögerung anzeigt

Um dem Benutzer die Übertragung eines Signals zu ermöglichen

  1. Setzen Sie die Funkgruppe im Block "beim Start" auf 0
  2. Behandeln Sie einen Druck auf die B-Taste:

    Senden Sie das ausgewählte Signal mit einem Block "Radio send number (X)"

Das ist es. Die Root-Knoten-Anwendung ist extrem einfach.

Schritt 10: Wir sind fertig

Wir sind fertig
Wir sind fertig

Oben sehen Sie ein Bild der Geräte, auf denen die Anwendung ausgeführt wird. Die beiden auf der rechten Seite führen die Hauptanwendung "Benutzer" aus, und die auf der linken Seite führt die "Root"-Anwendung aus.

Ich habe dieses Spiel bei CS Connections 2018 demonstriert, einer einwöchigen Sommerkonferenz für Lehrer der Mittel- und Oberstufe zum Thema Informatikunterricht. Ich habe etwa 40 Geräte an die Lehrer verteilt und die Regeln erklärt. Die meisten fanden das Spiel unterhaltsam und viele fanden es verwirrend, bis sie herausfanden, wie man es spielt. Die Demonstration war kurz, aber wir fanden, dass das Spiel unter einem recht unterschiedlichen Publikum Spaß machte.

Weitere Informationen zu CS Connections 2018 finden Sie hier.

Empfohlen: