Labels

Posts mit dem Label Wherigo Fortschrittskurs werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Wherigo Fortschrittskurs werden angezeigt. Alle Posts anzeigen

Dienstag, 26. März 2013

Wherigo debuggen, auch ohne Simulator

Voraussetzung: Einen Wherigo debuggen

Abbildung 1: Beim Loggen wird sowohl die print-Ausgabe für den Simulator als auch das Logbuch für den GPSr-Betrieb befüllt.
Im ersten Teil des Debug-Tutorial ging es um die Ausgabe mittels print, die nur im Urwigo-Simulator sichtbar ist. Was aber, wenn bestimmte Fehler / zu untersuchende Situationen erst draußen auf dem Oregon vorzufinden sind. Abhilfe gibt es duch ein Item names Logbuch (! Sowohl Name als auch Identifier sollte "Logbuch" lauten).
Da jeder Logeintrag zwei Arbeitsschritte nach sich zieht, lohnt es sich hier eine Methode zu spendieren, die die Arbeit erledigt. Hat auch den Vorteil, dass Erweiterungen an einer einzigen Stelle vorgenommen werden müssen. So kann man kurz vor Veröffentlichung die Zeilen 2 bis 4 auskommentieren und es werden nirgens Logausgaben geschrieben.

Abbildung 2: Der Aufruf im Spiel geht ganz einfach mittels eines Lua User Code Blockes
Die beste Methoden nützt nichts, wenn sie nicht aufgerufen wird. Dies geht ganz einfach wie man in Abb. 2 erkennt. Man kann aber auch in bestehendem lua Code (bei komplexen Projekten besteht das Spiel fast nur noch aus Direktprogrammierung in lua) die log-Zeile einfügen.

Abbildung 3: So siehts aus: In der Abteilung "Gegenstände" ist unser Logbuch zu finden, indem es für jeden Eintrag eine neue Zeile gibt.
Im Spiel haben wir im Inventar bzw. bei den Gegenständen unser Logbuch. Für jedes Ereignis, dass wir mit einer log-Zeile versehen, wird eine neue Zeile einfügt. Dies bewirken die eckigen Klammern um den Zeilenbruch Zeile 3 nach 4. Anders zu der print Ausgabe wird hier aber leserlicher die neueste Zeile unten eingefügt. Wer es andersherum mag, schreibt value .. Logbuch.Description
!Vorsicht mit zu vielen Logausgaben: Das Oregon wird irgendwann ziemlich langsam, wenn zu vielen Zeilen im Logbuch enthalten sind. Da hift nur eins: Oben wieder wegnehmen. string.sub ist hier das Stichwort nach dem man suchen muss. So gibt string.sub(Logbuch.Description, -50) die letzten 50 Zeichen zurück. Wer es ganz genau haben möchte ermittelt mittels string.find die passende Stellen des Zeilenumbruches, aber dafür sollte man Grundkenntnisse in Pattern Matching besitzen.

Freitag, 8. März 2013

Fehlersuche: Ein Wherigo-Projekt debuggen

Voraussetzungen: Zone als Kreis

Abbildung 1: Beim Erreichen der Zone wird eine Debug-Nachricht geschrieben und so der Stand der Variable Punkte kontrolliert.
Wer kennt es nicht, man hat stundenlang den schönsten Programmablauf und Variablen definiert, die ereignisabhänig manipuliert werden, aber beim Testlauf funktioniert es einfach nicht.
Manchmal kann man sich mit MessageBoxen behelfen. Aber das ist recht aufwändig und nicht immer einsetzbar, da MessageBoxen von nachfolgenden Events überlagert werden. Außerdem sind (vergessene) MessageBoxen auch später für den Spieler sichbar.
Wesentlich flexibler ist die Möglichkeit in lua einen print Befehl abzusetzen und so Wert von Variablen oder den Aktivitätsstatus einer Zone abzufragen.
In Abb.1 wird der Wert der Variable Punkte abgefragt um zu kontrollieren, welchen Wert sie an dieser bestimmten Stelle hat.
Wichtig ist, dass die Variable nicht nur den Namen sondern auch den Identifier "Punkte" besitzt, ansonsten müssten wir sie mit "ojbPunkte" ansprechen.

Abbildung 2: So sieht die Ausgabe im Simulator-Testlauf aus.
Wenn wir dann unser Projekt im Urwigo-Simulator starten, können wir im rechten Teil des Fensters auf Lua Debug umschalten um die print-Ausgabe im Bereich Result zu sehen. Im späteren Spiel -z.B auf dem Oregon- ist die Ausgabe nicht sichtbar, da auf dem Bildschirm nur MessageBoxen, Inputs & Co angezeigt werden.

Abbildung 3: Die Zone wurde verlassen. Da Punkte um einen Zähler erhöht wurde, hat die Variable nun den Wert 6
Was wir aber eigentlich testen wollten, war, ob der Wert für Punkte auch wirklich erhöht wird. Um das rauszufinden bauen wir im OnDistant Bereich wieder eine print-Anweisung ein. Und siehe da, beim Verlassen der Zone hat die Variable den Wert "6".
Etwas gewöhnungsbedürftig ist, dass wir "von unten nach oben" lesen müssen, denn die Print-Ausgaben werden nicht unter den bishierigen Text eingefügt, sondern oberhalb. Hier ist es sehr hilfreich nicht nur einfach den Wert der Variablen auszugeben, sondern auch den Kontext ("Zone X betreten", "Timer 1 gestart", usw) anzugeben.
Bitte nicht wundern, wenn in der untersten Zeile die Ausgabe "Punktestand beim Verlassen der Zone A 5" erscheinen sollte. Dies wird am Ende des Tutorials erläutert und kann erstmal ignoriert werden.

Abbildung 4: Man kann auch im Simulator während des Testlaufes Variablen abfragen.
Obwohl die print-Ausgaben nicht im späteren Lauf zu sehen sind, empfiehlt es sich, sie wieder herauszunehmen, wenn das Problem gelöst ist. Bei Abfragen, wo man vorher weiß, dass man sie nur einmal oder nur recht kurz gebraucht werden, oder bei Abfragen die einem während des Simulationslaufes erst einfallen, kann man sich auch des OnTheFly-Modus bedienen.
Dazu hackt man seine Anweisungen einfach in das Type in Lua Statements below Fenster und bekommt sofort die Ausgabe.
Doch Vorsicht: Im Gegensatz zur lua Programmierung im Urwigo-Projekt müssen mehrere Anweisungen durch das Semikolon getrennt werden.

Abbildung 5: Leere Ausgabe? Warum wurde nichts geschrieben?
Für diejenigen die sich über die tostring Methode aus Abb. 4 gewundert haben sei erklärt, dass die String-Konkatenation .. nicht mit Boolean-Werten funktioniert und wir deshalb den Boolean-Wert in einen String-Wert umwandeln müssen.
Bevor wir weitermachen leeren wir das Result Fenster mit einem Klick auf clear.
In Abb. 5 sehen wir was passiert, wenn wir diese Umwandlung weglassen ... nämlich gar nichts. Jedenfalls nichts sichbares. Im "unsichbaren Bereich" bricht die Routine ab. Weiter Anweisungen (z.B. "print(Player.Name);") würden nicht mehr ausgeführt.
Leider erscheint aber auch keine Fehlermeldung was schief gelaufen ist. Jedenfalls erscheit die Fehlermeldung nicht im Vordergrund. Wir können aber trotzdem geziehlt nachforschen warum nichts passiert indem wir auf MessageLog umschalten.
Hier sehen wir direkt, dass es zu einem ERROR gekommen ist. Nach einem Klick auf View Messages können wir die Fehlermeldung auch im Ganzen anschauen und erkennen so, den Grund für den N SCHWARZEN BILDSCHIRM HAB ICH! N SCHWARZEN BILDSCHIRM DAS GANZE WOCHENENDE!!. leeren Bildschirm:
[..] attempt to concatenate field 'Active' (a boolean value)
Als Lösung des Problems haben wir in Abb. 4 die tostring Methoden vor den Boolean-Wert geschaltet, sodass nicht String und Boolean, sondern zwei Strings konkateniert werden.

Abbildung 6: Fehleranlayse in "Message Log"
Wer ganz aufmerksam hinschaut, erkennt noch ein weiteres Problem. Obwohl der Player gerade erst gestartet war (die unteren beiden Zeilen) wird sofort das OnDistant Event auf ZoneA ausgelöst. (Eine Zeile darüber.) Ein Problem dass aus Zone als Kreis bereits bekannt ist. Es soll uns hier aber nicht weiter stören, erklärt aber warum die print-Ausgabe "Punktestand beim Verlassen der Zone A 5" beim Starten des Testlaufes erscheint.

Man kann auf diese Art natürlich nicht nur abfragen, sondern auch manipulieren. Hat man z.B. einen Fehler beim Final behoben und will nicht den ganzen WIG nochmals durchlaufen, kann man mit folgendem Code auch ganz leicht zum Final vorspringen:
Final.Active = true;
Final.Visible = true;

Doch Vorsicht: Manipulierender Code sollte man nur OnTheFly einsetzen, nie im Urwigo-Projekt verankern. Denn das Entfernen des Codes ist schnell vergessen und mit hochgeladen. Dann kann jeder Spieler direkt zum Final vorspringen, was wir natürlich nicht wollen.l
Wenn du dich etwas mehr mit lua-Programmierung beschäftigen möchtest, kann ich dir das "lua Einstiegs-Tutorial“ empfehlen.

Montag, 16. April 2012

Fortschrittskurs - Dynamische Programmierung

Fortschrittskurs - Dynamische Programmierung

Voraussetzung: "Einsteigerkurs“
Im Anfängerkurs haben wir gelernt, wie man ein statisches Wherigo-Projekt erstellt.
Im Fortschrittskurs wollen wir nun einen Schritt weiter gehen und lernen wie man diese beliebte Cacheart durch dynamischen Programmablauf noch interessanter machen kann.
Dynamisch bedeutet hier, dass der Spieler nicht einer festgelegten Route (Parkplatz – Kirche – Rathaus – Marktplatz) folgen muss, sondern während des Spiels selbst entscheidet, welche Station er als nächstes angehen möchte.
Außerdem werden wir keine Stadtrundführung programmieren, sondern eine Mini-Version des Brettspieles Scotland Yard, bei dem der Spieler den mysteriösen Mister X jagen muss.


Abbildung 1: Mini-Streckenfahrplan mit Taxi-, Bus- und U-Bahn-Verbindungen. Im fertigen Wherigo sind natürlich mehr Stationen vorhanden. Damit es nicht zu kompliziert wird begnügen wir uns mit vier Stationen

Konzept

Beginnen wir wieder mit einem Konzept:
Das Spielfeld besteht aus 4 Zonen - nachfolgend als Stationen bezeichnet -, die jeweils einen Taxistand darstellen. Zusätzlich zu der Taxiverbindung kann man zwischen Station 1 und Station 3 mit der U-Bahn und zwischen Station 2 und Station 3 mit dem Bus fahren.
Als Spieler steht man beim Erreichen einer Station vor zwei Entscheidungen: Mit welchem Verkehrsmittel möchte ich mich fortbewegen und wohin möchte ich fahren. Ihr seht schon, dass bereits an dieser Stelle das Spiel dynamisch verläuft.
Entgegen des Brettspieles gibt es Mister X nur virtuell, das einzige was wir im Programm für ihn benötigen ist eine Variable, die seine Position speichert. Mister X ist in der Regel unsichtbar, es wird nur offenbart, welches Verkehrsmittel er benutzt. Wir werden dies mit einer Message darstellen, die folgendermaßen aussehen könnte: “Mister X fährt Taxi“. Bei jedem dritten Zug zeigt er sich und teilt uns mit wo er ist, bzw. besser gesagt wo er war, bevor er das nächste Verkehrsmittel benutzt. Damit es nicht zu lange dauert, zeigt sich Mister X ab der zweiten Runde. Eine Offenbarung ist also mit Runde 2, 5, 8, 11, 14 … fällig.

Abbildung 2: Der Spielablauf verläuft rundenbasiert. Nachdem man eine Station erreicht hat, zeigt sich Mister X (ab und zu) und fährt sofort weiter. Wir entscheiden uns für ein Transportmittel und eine Station, laufen diese an und die nächste Runde beginnt.
Der Ablauf soll also folgendermaßen aussehen (siehe auch Abb.2):
  • Der Spieler erreicht eine Station.
  • Wenn sich Mister X ebenfalls in dieser Station befindet, ist das Spiel gewonnen und per Message teil Mister X mit wo sich der Final befindet.
  • Ansonsten ist Mister X an der Reihe und fährt zur nächsten Station
  • Abhängig von der Station wählt der Spieler zwischen Taxistand, Bushaltestelle oder U-Bahnstation
  • Abhängig von Station und Verkehrsmittel wählt der Spieler die nächste Station aus.
  • Der Spieler läuft, Verzeihung fährt zur nächsten Station
  • Der Spieler erreicht die nächste Station. Da wir uns in Runde 14 befindet teilt uns Mister X mit wo er sich befindet.
Um dieses Szenario abzubilden, benötigten wir folgende Bausteine:
  • 5 Zonen, „Station 1“ – „Station 4“ sowie „Final“
  • 4 Gegenstände Taxistand, Bushaltestelle und U-Bahnstation sowie Spoiler
  • pro Gegenstand ein Kommando („Taxi fahren“, „Bus fahren“ bzw. „U-Bahn fahren“)
  • Variablen für Position Mister-X „PosX“, Position Spieler „PosPlayer“ und Rundenzähler „RoundCount“
  • Bilder für Taxi, Bus und U-Bahn, Mister X, Stationen, Final und Spoiler

Abbildung 3: Beim Annähern an die Station wird der Programmablauf an eine Funktion weitergeleitet.

Zoneneintritt

Zu Beginn richtigen wir unser Augenmerk auf die "On proximity" Funktion der Zonen. Im Einsteiger-Tutorial haben wir unsere Zonen mit 4 Punkten abgesteckt und "On enter" implementiert. Bei dieser Vorgehensweise kommt es immer wieder zu Problemen im Spielablauf indem der Wherigo-Player entweder eine Ecke der Zone anvisiert um seinen Richtungspfeil auszurichten, oder der Wherigo-Builder aktualisiert den OriginalPoint der Zone, an dem sich viele Player orientieren, nicht wenn die Zone nachträglich verschoben wird. Wenn man die Zone nicht genau abstecken muss und auch gut mit einem Kreis leben kann, so ist "On proximity" mit dreimal Kreismittelpunkt als Koordinaten eine gute Alternative. Über "In proximity" lässt sich der Radius des Kreises bestimmen. Der Vorteil der Methode liegt auf der Hand: Der Spieler navigiert genau auf den Zonenmittelpunkt und als Cacheowner reicht es einen Punkt zu messen. Denn meistens möchte man den Spieler an einem bestimmten Punkt führen. Anstatt sich „On enter“ zu bedienen wird der Programmablauf beim Erreichen einer Station in „On proximity“ definiert. Ob ihr euren Wherigo mit Rechteck- oder Kreiszonen ausstattet, ist letztendlich euch überlassen und kann auch von der Situation vor Ort abhängen.

Funktionen

Da wir bei jeder Station beim Eintritt, bzw. nun bei Annäherung die gleichen Arbeitsschritte durchführen und wir diesen Ablauf nicht jedes Mal neu programmieren wollen, bedienen wir uns der Funktionen. Wie in Abb. 3 dargestellt, setzen wir beim Erreichen der Zone die aktuelle Position des Spielers und rufen anschließend die Funktion „enterZone“ auf.


Abbildung 4: Funktion „enterZone“ – Während die eigene Position und das Transportmittel von Mister X immer ausgegeben werden, wird seine Position nur jeden dritten Zug offenbart.
Die Funktion „enterZone“ bildet das Kernstück unserer Programmierung. Mit ihrer Hilfe wird ermittelt ob wir Mister X gefangen haben oder ob das Spiel weitergeht. Weiterhin werden hier die verfügbaren Verkehrsmittel freigeschaltet, Mister X zeigt sich (ab und zu) und plant dann seinen nächsten Zug.

Mister X wird sichtbar

Sobald ein Spieler eine Station erreicht erhöhen wir den Rundenzähler. Diesen benötigten wir um bei jeder dritten Spielrunde die Position von Mister X anzuzeigen. Da der Text der Message von dieser Bedingung abhängig ist, bauen wir erst den Text zusammen indem wir verschieden Textbausteine aneinander reihen und in der Variable „Message“ speichern. Im unteren Teil von Abb. 4 sehen wir die Abfrage der Rundennummer und die bedingte Erweiterung der Variable „Message“. Die beiden langen Sätze sind mit einem Zeilenumbruch versehen um die Lesbarkeit während des Spieles zu verbessern.

Abbildung 5: Funktion „enterZone“ – Spielende. Wenn die Position von Mister X mit der unseren übereinstimmt, haben wir ihn gefangen und können ihn verhören.
Darunter erkennen wir im Ansatz den Message-Baustein (fortgesetzt in Abb. 5), in dessen "On clicked" Bereich zunächst alle Station deaktiviert werden und danach überprüft wird, ob Mister X die gleiche Position hat wir wir selbst und sich damit in unser Zone befindet. Sollte dem so sein, ist Mister X geschlagen und stellt sich einem Verhör zur Verfügung. Dazu später mehr.

Mister X ist am Zug

Wenn wir Mister X nicht gefangen haben, ist nun die Stelle erreicht wo er seinen Zug ausführt. Dazu berechnet er abhängig von der Position des Spielers die nächste freie Station.
Mister X erhält also künstliche Intelligenz, auch wenn es sich hier nur um ein Minimum handelt. In den demnächst folgenden lua Tutorials werden wir erfahren wie wir mit Hilfe von direkter lua Programmierung und einem Path-Finding-Algorithmus ihn etwas schlauer machen, für diesen Kurs reicht uns folgender Ansatz :
  • Bestimme abhängig von seiner aktuellen Position eine durch Verkehrsmittel zu erreichende Station für Mister X
  • Befindet sich der Spieler auf der potentiellen Station?
    • Wenn nein, wähle die potentielle Station als aktuelle Station für Mister X
    • Wenn ja, wähle eine alternative Station
In beiden Fällen wird die Textvariable „Transport“ gesetzt, mit deren Hilfe der Text „Mister X fährt Taxi | Bus | U-Bahn“ zusammengesetzt wird. Anschließend wird in einer Message der gesamte Text ausgegeben.

Abbildung 6: Funktion „enterZone“ – Abhängig von unserem Standort sowie seiner eigenen Position plant Mister X seinen nächsten Zug.

Abbildung 7: Funktion „enterZone“ - Taxi fahren darf man an jeder Station. Welche Alternative zur Fortbewegung zusätzlich zur Verfügung steht, hängt von der Station ab.

U-Bahn, Bus oder doch nur Taxi ?

Während der Taxistand in jedem Fall aktiviert wird (Taxi fahren darf der Spieler von jeder Station aus) verzweigen wir für jeden möglichen Aufenthaltsort des Spielers um gegebenenfalls die Bushaltestelle oder die U-Bahnstation freizuschalten. Wie man in Abb. 7 erkennt, gibt es keine Fallunterscheidung für Station 4. Dies liegt daran, dass dort außer dem bereits aktivierten Taxi kein Transportmittel zur Verfügung steht.
In den ersten drei Runden geben wir noch den Hinweis aus, was der Spieler nun unternehmen soll. Nach den drei Runden wird er (hoffentlich) verstandenen haben wie der Hase läuft und wir ersparen ihm die nun lästige Nachricht.

Abbildung 8: Abhängig von der Station, in der wir einsteigen, dürfen wir mit dem Taxi unterschiedliche Station anfahren

Türen zu und los – Aber wohin dürfen wir mit Taxi & Co fahren?

Im nächsten Schritt schauen wir uns die Taxifahrt ein wenig genauer an, soll heißen wir implementieren das Kommando „Taxi fahren“ für den Gegenstand „Taxistand“.
In Abb. 8 erkennen wir, dass wir nichts weiter machen müssen als die Stationen aktiv zu schalten, die von der aktuellen Station des Spielers aus mit dem Taxi erreichbar sind.
Da jede Station einen Taxistand beinhaltet, müssen wir auch für jede eine Fallunterscheidung einbauen.

Abbildung 9: Bus fahren gestaltet sich recht übersichtlich, da lediglich zwei Stationen mit Bushaltestellen ausgestattet sind.
Dies ist beim Busfahren schon ein wenig übersichtlicher. Ein Blick auf den Spielplan zeigt, dass lediglich Station 2 und 3 über eine Bushaltestelle verfügen. Wie wir Abb. 9 erkennen, müssen wir auch nur diese beiden Station in die Fallunterscheidung einbeziehen. Für die U-Bahnfahrt sieht es ähnlich übersichtlich aus.

Abbildung 10: Nachdem wir uns für eine Art des Fortkommens entschieden haben, werden alle Transportmittel deaktiviert

Sammelfunktion „afterTransportChoice“

Bleibt nur noch zu klären, was es mit der Funktion „afterTransportChoice“ auf sich hat. In dieser Funktion werden alle Aktivitäten zusammengefasst, die nach der Wahl des Transportmittels ausgeführt werden müssen. In unserem Fall setzen wir die Sichtbarkeit von Taxistand, Bushaltestelle und U-Bahnstation auf "False", denn nachdem der Spieler sich für ein Transportmittel entschieden hat, soll er dies nutzen und zur nächsten Station laufen. Eine erneute Wahl macht also wenig Sinn. Wer beim Blick auf Abb. 10 denkt „Wozu soll ich diese drei mickrigen Anweisungen in eine extra Funktion auslagern?“, dem sei gesagt, dass es nicht nur darum geht sich das Wiederholen der drei Anweisungen zu sparen. Nein, spätestens wenn wir uns in Zukunft dazu entscheiden zusätzlich einen Flughafen oder eine Bootsanlegestelle anzubieten oder einen Text „Gute Wahl. Viel Spaß bei der Fahrt“ auszugeben, spätestens dann zeigt sich ein entscheidender Vorteil der Auslagerung des Codeteils in eine Funktion: Die Wartbarkeit. Es gibt nichts schlimmeres bei der Programmierung als Änderungen oder Fehlerbehebungen an gefühlten tausend Stellen durchführen zu müssen.

Abbildung 11: Das Verhör mit Mister X endet ziemlich schnell. Wer möchte, kann es zu einem Dialog ausbauen, bei dem Mister X überführt werden muss.

Mister X in Gefangenschaft

Abhängig von Größe der Spielfeldes und Spielablauf haben wir Mister X nach mehr oder weniger Zügen gefangen. Damit haben wir aber noch keine Dose in der Hand. Um den Bezug vom Spiel zum Final zu realisieren bauen wir ein Verhör mit Mister X ein, der uns somit die Finalzone freischaltet. Dazu erstellen wir ein Kommando „Mister X verhören“, deren Implementierung aus Message und zwei Set-Baustein besteht, mit deren Hilfe wir die Zone „Final“ sichtbar und aktiv schalten.

Damit ist der dynamische Ablauf unseres Wherigos komplett und wir können uns um weitere Details kümmern.

Abbildung 12: Lediglich beim ersten Erreichen der Station wird die Frage nach der Hausnummer vorgelegt.

Simulationsschutz

Als erstes betrachten wir den Aspekt des Simulationsschutzes. Über die Frage, ob man dem Spieler freistellt den schön gemachten Wherigo am Simulator auf der Couch durchzuspielen oder ob man ihn zwingt mehr Spielspaß vor Ort zu erleben, kann man ewig diskutieren und die Antwort hängt unter anderem vom Wherigo und der Örtlichkeit ab. Jetzt mag sich der ein oder andere denken „Moment, Urwigo bietet doch dieses nette Häkchen im Hauptfenster an“, der Praxistest zeigt aber einige Probleme des vorgefertigten Simulationsschutzes auf. Dieser verlässt sich auf die Messungenauigkeit der Geräte und meint einen echten von einem simulierten Programmablauf daran zu erkennen, dass sich innerhalb bestimmter Zeiten Höhen- und Positionsangaben ändern müssen. Nun lassen sich auf der einen Seite diese Schwankungen durch weiterentwickelte Emulatoren vortäuschen, auf der anderen Seite, und dies ist der entscheidende Part, kommt es bei bestimmten Geräten zu der Situation, dass „ehrliche“ Cacher auf der Wiese stehen und zu Unrecht eine Fehlermeldung à la „Du sollst nicht simulieren“ erhalten. Die einzig wirksame Maßnahmen den Couch-Potato in die Natur zu kriegen, bleibt die Abfragen von Ortsmerkmalen wie Hausnummern, Farben, Anzahl von Schrauben usw. Dies ist bisher auch noch nichts neues. Jeder Multi und die meisten statischen Wherigos arbeiten mit diesen Ortsmerkmalen.

Hausnummernsuche? Bitte nur beim ersten Stationsbesuch

Bei unserem dynamischen Spiel haben wir allerdings das Problem, dass wir Stationen mehr als einmal erreichen und jedes Mal die Hausnummer „85a“ einzugeben führt nicht zwangsweise zur Attraktivitätssteigerung. Wir wollen dieses Problem mit einer Booleanvariablen lösen. Dies Variable kann lediglich zwei Werte annehmen: "True" oder "False". Abb. 12 zeigt die Verwendung für die erste Frage. Nach dem Erreichen der Station wird abgefragt, ob die erste Frage (noch) aktiv ist. Wenn dem so ist –wir erreichen diese Station zum ersten Mal– wird die Frage „Question1“ vorgelegt. Wenn wir die Station das nächste Mal anfahren wird die Funktion „enterZone“ sofort ausgeführt.

Abbildung 13: Wenn die Antwort korrekt ist, wird das Vorlegen der Frage beim nächsten Erreichen der Station deaktiviert.
Wie aber erreichen wir nun dass die Variable „Question1Active“ beim nächsten Mal auf "False" steht. Die Antwort zeigt uns Abb. 13. Die Rekursion bei falscher Antwort kennen wir ja bereits aus dem Anfängerkurs. Neu ist nun, dass wir die Booleanvariable mittels Set-Baustein auf "False" setzen und anschließend unsere bekannte Funktion für das Erreichen einer Zone aufrufen. Hier zeigt es sich wieder vorteilhaft, dass wir diesen Ablauf in eine Funktion ausgelagert haben.

Abbildung 14: Spiel beenden. Anstatt mühsam mehrere Message-Bausteine über „On clicked“ zu synchronisieren nutzen wir den komfortablen Weg des Dialogs. Der „Completition code“ wird von wherigo.com erzeugt wenn das Cartrigde vor dem Download online kompiliert wird.

Spoiler, Complete-Flag und Unlock-Code beim Final

Beim Annähern an den Final verwenden wir die "On proximity" Logik aus dem Einsteigerkurs. Zusätzlich zum Aktivieren des Spoilers und dem Complete-Flag wollen wir aber noch mehr durchführen. Als erstes neues Element sehen wir den Dialog. Mit dessen Hilfen lassen sich mehrere Bildschirmausgaben hintereinander schalten, ohne dass wir uns selbst um die Synchronisation via „On clicked“ kümmern müssen, wie wir das bei mehreren Message Boxen hätten tun müssen. Beim Dialog lassen sich einfach mehrere Dialog Entries untereinander platzieren, die nacheinander aufgerufen werden, sobald ein Dialog Entry mit „OK“ bestätigt wird. Im mittleren Entry sehen wir, wie wir den Unlock-Code ausgeben können, damit das Cartridge auch ohne gws Datei auf wherigo.com als gefunden markiert werden kann.

Abbildung 15: Durch den breiten Set-Baustein braucht die gesamte Anweisung viel Platz

Etwas mehr Übersichtlichkeit bitte

Sobald man die erste komplexe Idee implementiert wird man schnell feststellen, dass der Bildschirm gar nicht groß genug sein kann um alles darstellen zu können. Besonders bei verketteten if/else Bedingungen stößt man schnell an die Grenze der Lesbarkeit. Eine Form der Abhilfe ist die Zoomfunktion, die man benutzen kann indem man bei gedrückter Strg-Taste mit dem Mausrad vor und zurück scrollt. Der angezeigte Ausschnitt des Flussdiagramms wird somit heran bzw. heraus gezoomt, wobei zu viel herauszoomen dazu führt, dass man die Schrift nicht mehr lesen kann. Eine Alternative zum Zoomen ist das Ausblenden von Bausteinen, die gerade nicht bearbeitet werden indem man auf das schwarze Dreieck neben dem Bausteinnamen klickt (kleiner Rahmen in Abb. 15).

Abbildung 16: Nachdem wir den Set-Baustein minimiert haben erscheint die Anweisung übersichtlicher
Somit bleiben lediglich die Bausteine sichtbar, die bearbeitet werden sollen bzw. zur Bearbeitung benötigt werden. Ein weiterer Klick auf das Dreieck blendet den Baustein dann wieder ein.

Wo kann ich diesen Wherigo spielen?

Wer Scotland Yard nicht nur in der Mini-Version, sondern als kompletten Wherigo erleben möchte, sollte nach Scotland Yard in Heide Ausschau halten. Hier wurde die Intelligenz von Mister X verbessert, sodass er zu Beginn recht schwierig zu fangen ist. Wie man so etwas direkt in lua programmiert und wie man Mister X im Laufe des Spieles dümmer werden lässt, könnt ihr in der Kategorie Wherigo für Experten herausfinden.

Samstag, 7. Januar 2012

Timer II - Countdown

Fortschrittskurs „Timer II - Countdown“


Voraussetzung: "Forschrittskurs „Timer I - Zeitstress“


Wir nehmen uns das Cartrigde aus „Timer I – Zeitmessung“ und ersetzen den normalen Timer durch einen Countdown. Ziel ist es permanent die Restzeit („Noch 50 Sekunden“, „Noch 49 Sekunden“ usw.) anzugezeigen.
Dazu erstellen wir eine Zahlvariable „TimeLeft“, die auf 60 (Sekunden) eingestellt ist und einen zweiten Timer names „Countdown“, bei deren Ablauf folgendes passieren soll: Ist „TimeLeft“ noch größer als 0, so wird die Restzeit in einer Message angezeigt, die sich jede Sekunden erneuert und damit die alte überlagert. Anschließend minimieren wir den Wert um einen Zähler.
Ist der Spieler nicht schnell genug und „TimeLeft“ erreicht den Wert 0 wird wie beim alten Timer die Booleanvariable „SatelliteReached“ auf „False“ gesetzt, der Misserfolg gemeldet und der Timer gestoppt.
Der letzte Schritt muss unbedingt im „On clicked“-Bereich erfolgen, da der Timer nur gestoppt werden kann, wenn er sich nicht in einem Verarbeitungsschritt befindet. Wie bereits im Anfängerkurs angesprochen stoppt der Programmablauf nicht, wenn die MessageBox erscheint. D.h. Im Hintergrund wird die „On elapsed“ Routine weiter abgearbeitet. Wenn der Spieler die Meldung mit OK bestätigt, ist die Routine beendet und der Timer kann gestoppt werden.

Abbildung 1: Der „Countdown“-Timer läuft jede Sekunde ab, zeigt die Restlaufzeit an und verringert den Wert anschließend

Wenn wir Abb. 1 genauer betrachten, entdecken wir, dass der Timer auf Interval mit einer Laufzeit von einer Sekunde eingestellt ist. In diesem Modus startet der Timer immer wieder automatisch bis er explizit gestoppt. Das Ereignis „On start“ wird aber nur beim ersten Start ausgeführt.
Da durch die ständig erscheinende Message der Spieler die Zone „Satellitenstation“ nicht mehr auswählen kann, ist es wichtig dass er vor Erreichen des Labors weiß, wohin er laufen muss. Gegebenenfalls sollte man früh genug eine Wegbeschreibung („Laufe die Straße runter, dann rechts bis zum Baum“) anzeigen.
Um die Änderung wirksam werden zu lassen, ersetzen wir in der Labor- bzw. der Satellitenstationsannäherung beim „Start Timer“ / „Stop Timer“ Modul den „SatelliteLifeline“- durch den „Countdown“-Timer.
Abbildung 2: Beim Erreichen des Labors muss nun der Countdown-Timer gestartet werden Abbildung 3: Auch in der Satellitenstation wurde der alte Timer durch unseren Countdown ersetzt


Das urwigo Projekt zu diesem Tutorial als Download"

Timer I - Zeitstress

Fortschrittskurs „Timer I - Zeitmessung“


Voraussetzung: "Forschrittskurs „Zone als Kreis“


In diesem Tutorial wollen wir unser Augenmerk auf das Thema Timer richten.
Mit Timern lassen sich viele nützliche Features im Cartrigde einbauen. So wollen wir zu Beginn den Spieler ein wenig sprinten lassen und die benötigte Zeit mittels Timer kontrollieren.
Der Spieler erreicht im Demospiel eine Bergstation und erhält dort die Nachricht, dass er zur Finalsuche einen Spoiler bei der Satellitenstation erhalten kann. Diese muss er aber in den nächsten 60 Sekunden erreichen, sonst ist der Satellit weitergeflogen und kann nicht mehr genutzt werden.
Die Grundidee unserer Implementierung zielt darauf ab, dass ein Timer gestartet wird, der – wenn nicht rechtzeitig gestoppt den Satellit verschwinden lässt. Sicherlich könnte man diese Aufgabe auch mit einer Start- und einer Endzeit lösen, in der späteren Erweiterung sehen wir aber wie praktisch Timer sein können.
Das erste Problem besteht im Zeitpunkt des Startes. Wenn wir den Countdown sofort starten, geht dem Spieler wertvolle Zeit während des Lesens verloren.
Wenn der Timer erst nach dem Bestätigen der Nachricht startet, kann der Spieler mogeln indem er bequem zur Satellitenstation schlendert und dann erst auf OK drückt.
Eine Möglichkeit wäre den Start mit dem „On leave“ Event der Zone zu verknüpfen. Da wir aber die Stationen als Punkte und nicht als Vieleck definieren (Siehe Tutorial „Zone als Kreis“) würde „On leave“ nie ausgelöst. Man kann das Problem zwar mit „On distant“ lösen, dies hat aber einige Nebenwirkungen, wie wir im Kreistutorial gesehen haben.
Wir wählen deshalb einen Lösungsansatz mit einer Zwischenstation namens Labor. Sie liegt auf dem Weg zur Satellitenstation und beim Annähern kündigt ein Bildschirm an, dass es nun losgeht.
Dies hat zudem den Vorteil, dass sich der Spieler in Ruhe auf die kommende „Stresssitualtion“ einstellen kann.
Abbildung 1: Ankündigung eines Spezialjokers wenn die sportliche Aufgabe erfüllt ist

Abb. 1 zeigt uns den Ablauf beim Annähern an die Bergstation.
Neben der Informationsmeldung an den Spieler, werden die beiden Zone Labor und Satellitenstation aktiv geschaltet. Ich setze diese Werte bewusst bereits jetzt, damit der Spieler genügend Zeit besitzt, sich ein Bild zu machen wo er hin laufen muss.
Abbildung 2: Beim Erreichen des Labors nimmt die Sache Fahrt auf


Sobald wir das Labor erreichen (Abb. 2), nimmt unser Spielablauf Fahrt auf, der Timer wird gestartet.
Was uns noch auffällt ist die Variable „SatelliteReached“, die hier auf „True“ gesetzt wird. Mit ihrer Hilfe können wir beim Erreichen der Satellitenstation feststellen, ob wir schnell genug waren oder nicht.
In Abb. 3 sehen wir, dass dieser Wert auf False gesetzt wird, wenn der Timer abgelaufen ist.
Ansonsten ist ein Timer nicht wirklich schwer zu verstehen. Neben dem Namen können wir den Modus (Countdown oder Intervall) wählen sowie die Laufzeit in Sekunden einstellen.
Wir haben drei verschiedene Ereignisse, die verarbeitet werden können. Neben dem Starten und dem Stoppen gibt es ein eigenes Ereignis für den Ablauf des Timers, das vom System aufgerufen wird wenn die eingestellte Zeit abgelaufen ist.

Abbildung 3: Wenn die 60 Sekunden abgelaufen sind, verfällt die Chance auf den Spoiler


Wenn wir völlig außer Puste das Ziel unseres Kurzstreckenlaufes erreicht haben, kommt die Stunde der Wahrheit. Als allererstes stoppen wir den Timer, denn nun soll er nicht mehr ablaufen bzw. den Wert von „SatelliteReached“ zu unserem Nachteil verändern. Wenn wir schnell genug waren, dann besitzt diese Variable immer noch den Wert „True“ und wir stellen dem Spieler den Spoiler zur Verfügung
Da wir die Satellitenstation nun nicht mehr brauchen, deaktiviert wir sie und der Spieler kann zum Final vorrücken

Abbildung 4: Beim Erreichen der Satellitenstation entscheidet sich, um man schnell genug war

Noch ein Tipp nebenbei: Da wir „SatelliteReached“ erst in der Laborstation von „False“ auf „True“ gesetzt wurde, haben wir uns auch gegen den Mogelversuch gewehrt, dass der Spieler direkt zur Satellitenstation läuft ohne vorher beim Labor vorbeizuschauen. Denn dann wird der Variablenwert nie auf „True“ gesetzt.


Man kann diesen Sicherheitscheck auch aufwändiger betreiben und eine Booleanvariable „LaborPassed“ einfügen, die beim Erreichen des Labors auf „True“ gesetzt wird. Diese Variante hätte den Vorteil, dass man dem User gezielt mit einer Message auf seinen Fehler hinweisen könnte.


Das urwigo Projekt zu diesem Tutorial als Download"

Sonntag, 1. Januar 2012

Zone als Kreis

Fortschrittskurs „Zone als Kreis“

Voraussetzung: Einsteigerkurs

Als Eintritt in den Fortschrittskurs wollen wir uns nochmal mit der Definition von Zonen beschäftigen.
Im Einsteigerkurs haben wir unsere Zonen mit 4 Punkten abgesteckt und "On enter" implementiert.
Abbildung 1: Statt alle vier Ecken zu messen reicht es den Mittelpunkt und den Radius anzugeben
Bei dieser Vorgehensweise kommt es immer wieder zu Problemen im Spielablauf indem der Wherigo-Player entweder eine Ecke der Zone anvisiert um seinen Richtungspfeil auszurichten, oder der Wherigo-Builder aktualisiert den OriginalPoint der Zone, an dem sich viele Player orientieren, nicht wenn die Zone nachträglich verschoben wird.
Wenn man die Zone nicht genau abstecken muss und auch gut mit einem Kreis leben kann, so sparen wir uns die Mühe mehrere Punkte zu messen und bauen ein Minidreieck mit dreimal Kreismittelpunkt als Koordinaten.
Über "In proximity" lässt sich der Radius des Kreises bestimmen.
Anstatt sich „On enter“ zu bedienen wird der Programmablauf beim Erreichen einer Station in „On proximity“ definiert.
Wenn ihr Objekte in der Zone platzieren wollt, die dem Spieler angezeigt werden sollen, müsst ihr „Show Objects“ auf „On proximity“ umstellen.

Der Vorteil der Methode liegt auf der Hand: Der Spieler navigiert genau auf den Zonenmittelpunkt und als Cacheowner reicht es einen Punkt zu messen. Denn meistens möchte man den Spieler an einem bestimmten Punkt führen.
Als weiteren Nebeneffekt simuliert ihr Geschwindigkeit und reibungslosen Programmablauf, denn der Spieler erhält sehr genaue Angaben wo er hin laufen soll und das Ereignis tritt schneller ein als erwartet.
Das Verlassen der Zone muss mit "On distant" erfasst werden,
da unsere eigentlicht Zone nur aus einem Punkt besteht
„Noch 12 Meter ... noch 11 Meter ... 'Welche Zahl steht auf den Boden gemalt?' “

Fairerweise sollte nicht unerwähnt bleiben, dass diese Lösung auch Nachteile besitzt. Genauso wie „On enter“ steht auch auch „On exit“ nicht zur Verfügung, da die eigentliche Zone ja nur aus einem Punkt besteht.
Abhilfe kann da aber „On distant“ verschaffen. Dieses Ereignis stellt quasi das Gegenteil zu „On proximity“ dar und wird ausgelöst, wenn der Annäherungsbereich verlassen wird.
Aber auch hier steckt der Teufel im Detail, denn „On distant“ wird von den Playern auch beim Start des Cartridges ausgelöst.

Ob "On distant" ausgeführt wird, hängt von der Variable "GameOnStart" ab
Wir lösen dieses Problem mit einer Booleanvariable „GameOnStart“, die initial auf „True“ steht und beim Erreichen der ersten Zone auf „False“ gesetzt wird.

Beim Erreichen der ersten Zone wird "GameOnStart" auf "False" gesetzt
TEXT Im „On distant“ Block muss dann lediglich eine Überprüfung auf den „GameOnStart“ Wert stattfinden.
Man kann also die Kreisfunktion mit der „On distant“ Methode nutzen, evtl. ist aber ein traditionelles Vieleck als Zone mit „On exit“ Funktion einfacher zu implementieren.
Es kommt halt immer auf die Situation an.

Die urwigo Datei zu diesem Tutorial als Download

Donnerstag, 14. Juli 2011

User-Input in der Endlosschleife

Wie soll die Benutzereingabe behandelt werden, wenn die Eingabe falsch ist?

Cartridge beenden und neu starten lassen, ist auf jeden Fall keine Option.

Einfach wieder den Input aufrufen ist schon besser. Aber wie kommt der Benutzer aus dieser Endlosschleife wieder heraus? Wie kann er die Eingabe abbrechen, um z.B das Cartridge zu speichern, zu einer anderen Zone zu gehen, oder irgendwelche Dialoge mit Personen führen, die ihm weiterhelfen?

Die Lösung ist nicht ganz einfach.


In der hinter den Input geschalteten MessageBox, in der informiert wird, dass die Antwort falsch ist, kann man 2 Buttons definieren. Einen für "Nochmal" und einen für "Hauptmenü".
Im OnClicked Bereich der Message kann abgefragt werden welche Button (Button1, Button2 oder Cancel) gedrückt wurde, wobei der Button Cancel im Simulator nicht angezeigt wurde.