API: Leitfaden zur erweiterten Nutzung

Der Skripteditor

Um deine Spielskripte zu bearbeiten, klicke auf den Link "API-Skripte" auf der Seite mit den Spieldetails für dein Spiel (dort, wo sich auch Optionen wie "Chatprotokoll" und "Spiel kopieren/erweitern" befinden). Es werden dir verschiedene Funktionen angezeigt:

  • Eine Liste mit Registerkarten oben. Dein Spiel kann mehrere Skripte haben, um die Organisation zu erleichtern. Beachte, dass alle Skripte weiterhin im selben Kontext ausgeführt werden. Das bedeutet, dass nicht mehrere Skripte gleichzeitig versuchen sollten, dieselben Werte zu überschreiben, da du sonst unbeabsichtigte Ergebnisse erhalten könntest.
  • Ein Skriptcode-Editor. Du kannst diesen Editor verwenden oder deine Skripte in einem externen Editor deiner Wahl bearbeiten und sie dann hier einfügen.
  • Unten befindet sich eine „API-Konsole“ (siehe unten).

Jedes Mal, wenn du auf die Schaltfläche "Skripte speichern" klickst, wird die Sandbox für dein Spiel neu gestartet (dabei gehen alle Daten im Speicher verloren, die nicht im Objekt state oder in Roll20-Objekten gespeichert wurden). Das gilt auch, wenn du ein neues Skript hinzufügst, ein Skript löschst oder ein Skript umschaltest, um es zu aktivieren/deaktivieren.


Die API-Konsole

Die API-Konsole ist das "Fenster" zu deinen Skripten. Da API-Skripte in einer Sandbox ausgeführt werden, hast du keinen direkten Zugriff auf sie, während sie laufen, um Informationen über die Ergebnisse oder Fehler des Skripts anzuzeigen. Die API-Konsole zeigt diese Informationen außerhalb der Sandbox an, damit du sie sehen kannst, während du deine Skripte bearbeitest. Hier werden allelog()Befehle sowie alle Fehler angezeigt, die während der Ausführung Ihrer Skripte auftreten. Weitere Informationen findest du im Artikel über das Debuggen von Skripten.


Reaktive Skripte: Ereignisse abhören, Objekte ändern

Die erste (und einfachste) Art der API-Nutzung besteht darin, auf Änderungen auf dem Spieltisch zu reagieren und dann mit zusätzlichen Funktionen auf die geänderten Objekte zu reagieren. Diese Art von Skript besteht aus einer Reihe von Funktionen, die Ereignisse überwachen, die während des Spiels passieren. Anschließend werden die während dieser Ereignisse übergebenen Objekte geändert, wodurch sich das Geschehen auf dem Spieltisch ändert.

Ein einfaches Skript, das ein Stück um weitere 5 Fuß verschiebt (unter der Annahme der Standardseiteneinstellungen), wäre:


        on("change:graphic", function(obj) {
  obj.set({
    left: obj.get("left") + 70    
  });
});

Wie du sehen kannst, haben wir eine einfache on-Funktion erstellt, die immer dann ausgeführt wird, wenn das change:graphic-Ereignis eintritt. Der Funktion wird das Grafikobjekt obj übergeben. Um eine Änderung vorzunehmen, modifizieren wir obj einfach mit der Funktion set -- was auch immer wir ändern, wird erkannt und auf der Spieltisch geändert.

Du musst set und get verwenden, um die aktuellen Werte von Objekten zu setzen und zu erhalten, sonst werden deine Änderungen nicht gespeichert. (Weiter unten findest du eine Liste der Objekttypen und ihrer Eigenschaften sowie eine Liste aller Ereignisse und der Argumente, die jedem Ereignis übergeben werden).

Ein Hinweis zu Utility-Funktionen

Natürlich ist das vorherige Beispiel nicht besonders hilfreich, da es der Position des Spielmarkers immer 70 Pixel hinzufügt. Was aber, wenn der Benutzer den Maßstab so geändert hat, dass 5 Fuß 140 Pixel entspricht? Die Roll20-API bietet mehrere praktische Hilfsfunktionen, die bei diesem (und anderen) häufigen Szenarios helfen. Ändern wir unser vorheriges Beispiel, um diedistanceToPixels-Funktionzu verwenden, die uns sagt, wie viele Pixel „fünf Fuß“ (oder Zoll, oder Meter, oder was auch immer für einen anderen Abstandstyp festgelegt wurde) auf der Tischplatte sind.

on("change:graphic", function(obj) {    
  obj.set({        
    left: obj.get("left") + distanceToPixels(5);    
  });
});

Wenn nun die aktuelle Seite so eingerichtet ist, dass sie die Standard-Rastergröße verwendet,distanceToPixels(5);gibt immer noch 70 Pixel zurück, aber wenn die Seite so eingerichtet ist, dass sie eine doppelt so große Skalierung wie normal hat, würde sie 140 zurückgeben.

Es ist immer eine gute Idee, Hilfsfunktionen zu verwenden, wenn sie verfügbar sind, damit dein Skript nicht zusammenbricht, wenn sich die Einstellungen einer Seite oder eines Spielmarkers ändern.


Proaktive Skripte: Dinge ohne Benutzereingriff erledigen

Du kannst nicht nur auf Nutzerereignisse reagieren, sondern mit der API auch automatisch Dinge tun, die nicht an ein bestimmtes Ereignis von den Spielern gebunden sind. Nehmen wir zum Beispiel einen Token an, der auf der Karte hin und her patrouilliert.

Hinweis: Auch wenn diese Art von Skript nicht von der Interaktion der Nutzer/innen abhängig ist, werden die API-Skripte für dein Spiel trotzdem nur ausgeführt, wenn mindestens eine Person mit deinem Spiel verbunden ist.

on("ready", function() {
   //Warte, bis das ready-Ereignis ausgelöst wird, damit wir wissen, dass das Spiel vollständig geladen ist.
   //Erhalte eine Referenz zu unserem Patrouillen-Spielmarker.
   var patroltoken = findObjs({_type: "graphic", name: "Guard A"})[0]; //Wir wissen, dass es im Spiel einen Token namens „Guard A“ gibt.
   var Direction = -1*distanceToPixels(5); //Gehe 70 Pixel nach links.
   varstepstaken = 0; //Wie viele Schritte sind wir in der aktuellen Richtung gegangen?
   setInterval(function() {
     if(stepstaken > 3) {
       //Richtung wechseln!
       Richtung = Richtung * -1; //wird die Richtung, in die wir gehen, „umdrehen“ 0stepstaken =
       ; //reset geht auf 0 zurück.
     }
     patroltoken.set("left", patroltoken.get("left") + Richtung); //gehen!
     Schritte gemacht++;
   }, 5000); //alle 5 Sekunden eine Aktion ausführen
});

Eine Abhandlung über asynchrone Funktionen

Eine asynchrone Funktion ist eine Funktion, die den Steuerungsthread sofort an den aufrufenden Bereich zurückgibt und im Hintergrund bestimmte Aufgaben ausführt. Hier ist ein sehr einfaches und leicht verständliches Beispiel, das Sie in eine API-Skript-Registerkarte einfügen können:

on('load',function(){
  log('Übergeordneter Bereich – Vor Aufruf der asynchronen Funktion.');

  setTimeout(function(){
    log('Asynchroner Funktionsbereich – Ausführung der asynchronen Funktion.');
  },10 /* 10 Millisekunden */);

  log('Übergeordneter Bereich – nach Aufruf der asynchronen Funktion.');
});

Im API-Protokoll siehst du etwas wie das hier:

„Übergeordneter Bereich – Vor dem Aufruf einer asynchronen Funktion.“
„Übergeordneter Bereich – nach Aufruf der asynchronen Funktion.“
„Asynchroner Funktionsumfang – Ausführung der asynchronen Funktion.“

Wenn du dir den Code ansiehst, denkst du: "Natürlich wird das später passieren, du hast ihm ja gesagt, dass er in 10 Millisekunden laufen soll, duh? Hier ist ein weniger offensichtliches Beispiel, das dieselben Protokollmeldungen enthält:

on('load',function(){
  log('Parent Scope – Vor Aufruf der asynchronen Funktion.');

  sendChat('Async Function','Evaluate this: [[1d6]]',function(msg){
    log ('Asynchroner Funktionsbereich – Ausführen der asynchronen Funktion.');
  });

  log('Übergeordneter Bereich – nach Aufruf der asynchronen Funktion.');

Asynchrone Funktionen sind erforderlich, um zu verhindern, dass die API ständig hängt. Wenn jeder Würfelwurf synchron verarbeitet würde, wäre die API sehr träge und reagiert nicht. Fast jede Funktion, die einen Rückruf annimmt, ist asynchron. (Mit Ausnahme einiger der Funktionen _.map, _.reduce usw. handelt es sich hierbei um Beispiele funktionaler Programmierung im Gegensatz zur prozeduralen Programmierung, an die die meisten Menschen gewöhnt sind.)

War dieser Beitrag hilfreich?
30 von 52 fanden dies hilfreich