Donnerstag, 25. Oktober 2012

7 Zonen und das Oregon unmöglich! ... Oder doch?

Voraussetzung: "Mit lua alle Zonen des Wherigos erfassen“

Als Oregon Nutzer aber auch als Wherigo-Programmierer ist man bestimmt schon des Öfteren über das Problem gestolpert, dass die Garmin-Hardware einfach nicht mehr auf Höhe der Zeit und somit viel zu langsam ist. Recht deutlich wird dies, wenn in einem WIG recht viele Zonen gleichzeitig aktiv sein sollen. Im Geoclub liest man von maximal sieben Zonen, die das Oregon gerade noch so vertragen soll. Meine 300er Version funktionierte zwar auch noch mit 9 gleichzeitig aktiven Zonen, doch die Reaktionsfähigkeit inkrementierte in den Minutenbereich. Auf deutsch: Will man ne Zone, eine Person auswählen oder einfach nur den Bildschirmtext mit "OK" bestätigen muss man darauf gefasst sein, dass das Gerät erst nach 30 bis 60 Sekunden reagiert. Von Spielvergnügen ist da schon lange keine Rede mehr und die Gefahr eines Absturtzes steigt ebenso an.

Abbildung 1: Die Zonen nach Entfernung sortieren
Es muss also Abhilfe her. Die einfache Lösung lautet daher: Nie so viele Zonen gleichzeitig aktiv schalten.
Das ist natürlich meistens nicht so einfach möglich. Besonders bei dynamischen Spielen, wo der User selbst entscheiden kann, wo er hingehen möchte scheint dies erstmal unmöglich. Was aber möglich ist, dass nur die nächsten n (z.B 5) Zonen angezeigt werden. Die Idee dabei ist, dass man die Zonen nach Entfernung zum Spieler sortiert und die ersten n Zonen angezeigt werden. Ein Timer kann alle paar Sekunden überprüfen, welche Zonen am Nähesten liegen und entsprechend den Active-Status umsetzen. Einfach gesprochen, doch wie wird es umgesetzt.
Als Voraussetzung muss man auf alle Zonen zugreifen können. "Mit lua alle Zonen des Wherigos erfassen“ zeigt wie man automatisch alle Zonen erfasst und in ein Array namens zones steckt.
Haben wir alle Zonen greifbar, so messen wir unsere eigene Position mit Player.ObjectLocation. Anschließend iterieren wird über alle Zonen und messen die Entfernung des Spielers zur Zone (also zum Mittelpunkt zone.OriginalPoint).
Diese Entfernung wird in der neu geschaffene Eigenschaft DistanceToPlayer gespeichert (Lua ist hier sehr großzügig und erlaubt eine Erweiterung der Objekte um neue Attribute zur Laufzeit, da Objekte intern auf Tabellen umgesetzt werden) bevor wir die Zone in die distTable einfügen. Zeile 18 bis 20 definiert einen Komparator, der in Zeile 21 der Tabelle zum Sortieren mitgegeben wird.
Für die technisch versierten Leser sei erläutert, dass bei jedem table.insert das neue Element mit dem ersten Element der bestehenden Tabelle verglichen wird. Dazu wird die Vergleichsoperation aufgerufen, die wir oben definiert haben. Ist der Vergleich positiv (hier die Entfernung zum Spieler ist kleiner), so wird das neue Element an die aktuelle Stelle eingefügt und die nachfolgenden Elemente eins weiter nach hinten geschoben. Resultiert aus dem Boolean-Vergleich false, so wird das neue Element mit dem zweiten Element der Tabelle verglichen, u.s.w. bis entweder eine Stelle im Vergleich true liefert oder das Ende der Tabelle erreicht ist.
Beim Verlassen der Methode buildSortedActiveZoneMap() wird die nach Entfernung sortierte Tabelle der Zonen zurückgeliefert.
Bleibt noch die Frage warum in Zeile 11 zone.Active2 und nicht zone.Active verwendet wird. Die Antwort darauf ist relativ simple. Active2 signalisiert nicht, dass die Zone gerade aktiv ist, sondern dass sie zu den Zonen gehört, die aktiv sein dürfen. (Manche Zonen werden ja erst im Laufe des Spieles freigeschaltet). Auch Active2 gehört zu den Attributen, die wir zur Laufzeit einfach an das lua-Objekt Zone anhängen.

Abbildung 2: Die nähesten Zonen werden aktiviert
Nachdem die Zonen nach Entfernung sortiert sind, werden alle Zonen deaktiviert und anschließend die n nähesten Zone aktiviert. Damit aber nachher im Spiel nicht der Effekt auftritt, dass nacheinander die aktiven Zonen verschwinden um dann langsam wieder aufzutauchen, erweitern wir das Zonenobjekt um ein weiteres Attribute Active3. Dadurch bleibt für bereits aktive Zonen, die immer noch nahe genug sind der Status erhalten und wecheselt nicht von zone.Active = true auf false und wieder zurück auf true. Sieht nicht sehr schön aus im Oregon.
Zeile 30 bis 32 stellt sicher, dass es zu keinem Fehler kommt wenn mehr Zonen erlaubt sind als Zonen im Spiel freigeschaltet sind (beim Start darf der Spieler nur zwei Zonen sehen, das Oregon würde aber 5 verkraften)

Abbildung 3: Bei Initialisierung und Aktivierung darf Active2 nicht vergessen werden
Um die Sortierfunktion aufrufen zu können, bedarf es ein wenig Vorbereitung und Pflege der Zonen. So muss man beim Start des Spieles für alle Zonen den Wert Active2 initialisieren, da sonst der Vergleich in Zeile 11 fehlschlagen würde.
Außerdem darf beim Freischalten einer Zone dieses Attribute nicht vergessen werden, sonst wird dises bei dem timergesteuerten Nähecheck nie einbezogen.
Zum Schluss noch ein paar Worte zur Performance: Gerade die table.sort Funktion ist sehr rechenintensiv und kostet viel CPU. Man sollte auf Geräten wie Oregon also vorsichtig damit sein. Je weiter die Zonen voneinander entfernt sind, desto seltener muss der Timer den Vergleich auslösen. Wenn ich mindestens ne Minute brauche um die nächste Zone zu erreichen, so reicht es aus, alle 30 Sekunden zu aktualisieren. Sind dagegen meine Zonen nur 20 Meter entfernt, ist es besser das Interval auf 2 oder 3 Sekunden zu stellen.

Abbildung 2: Der Timer wird nur für die Garmin Geräte aktiviert.
Jetzt ist dieses Problem ja hauptsächlich ein Garmin Problem, da die meisten Nicht-Oregon-Cacher mit dem Smartphone unterwegs sind und da das Hardwareproblem nicht so entscheidend ist. Es wäre folglich schön, wenn man diesen Workaround auf die Garminserie einschränken können. Dies kann über die Abfrage des Environments geschehen. Dort sind viele nützliche Infos enthalten, u.a. die Geräteplatform. Diese lässt sich mit Env.Platform abfragen und lautet bei Garmin Geräte "Vendor 1 ARM9". Diese Abfrage lässt sich nutzen um zu entscheiden, ob man den ZoneChecker-Timer startet und nicht. Wird er nicht gestartet, so werden keine Zonen deaktiviert und es kommt wieder zum alten Verhalten. Ob das sinnvoll und übersichtlich ist, hängt natürlich von der Spielidee ab.
Weitere Infos zum Thema herstellerspezifische Spielsituationen findet ihr im Geoclub Thread Spielverlauf durch Geräteabfrage bestimmen und im WherigoBuilder-Wiki.

Kommentare:

  1. Na, das kenn ich doch irgendwie
    "Bleibt noch die Frage warum in Zeile 11 zone.Active1"
    muss heissen Active2

    Und noch eine Anmerkung:
    Wenn die Zeilen ab 53 verwendet werden, muss man aufpassen, dass man in Urwigo den On-Start-Event nicht benutzt - der wird nämlich überschrieben...
    bodenseepingu

    AntwortenLöschen
  2. Vielen Dank für den Hinweis. Text ist korregiert

    AntwortenLöschen