Beispiele/Codeschnipsel in LUA

Aus Das Wherigo-Handbuch
Wechseln zu: Navigation, Suche

Inhaltsverzeichnis

Einfaches Beispiel zur Überprüfung, ob es sich um ein Garmin-Gerät handelt

Dann fangen wir mal mit einem ganz einfachen Beispiel an:

Unter Urwigo unter Benutzerdefinierten Funktionen öffnet sich ein Editor - dort kann man LUA-Funktionen eintragen.

Hier schreibt man dann z.b. rein:

    function pruefe_garmin()
      if Env.Platform == "Vendor 1 ARM9" then
          return true
      else
          return false
      end
    end

Benutzen kann man das in Urwigo einfach mit Hilfe des Konstrukts Benutzerdefinierter Ausdruck oder Benutzerdefinierte Funktion. Mit Hilfe des Benutzerdefinierten Ausdrucks kann man sich z.b. das Ergebnis einer LUA-Funktion zurückgeben lassen, einer Variable zuweisen oder per IF-Bedingung abfragen.

man kann z.b. direkt ein IF-Konstrukt machen und dort Benutzerdefinierter Ausdruck reinziehen - dort schreibt man dann einfach

     pruefe_garmin()

rein - Wenn auf einem Garmin gespielt wird, dann wird true zurückgegeben und man kann in der Cartridge drauf reagieren.. (Klar geht das ganze auch direkt in Urwigo - aber das war ja nur zur Demonstration)

Zusammenspiel zwischen grafischer Urwigo-Programmierung und LUA-User-Code

Meistens möchte man ja die grundlegenden Dinge in Urwigo programmieren und nur spezielle Dinge in LUA erledigen.

Im Urwigo wird User-Code in einem eingebauten Editor unter "Ansicht - Benutzerdefinierte Funktionen" angezeigt / editiert. Es gibt beim Zusammenspiel zwischen Urwigo-Objekten und LUA-Code eigentlich nur ein paar wesentliche Dinge zu beachten:

Zugriff auf Urwigo-Objekte / Variablen

Die einfachste Art und Weise, die Verbindung zwischen Urwigo-Objekten und LUA herzustellen besteht darin, dass in Urwigo bei den entsprechenden Objekten (Cartridge, Zonen, Inputs, Timern, Items, Variablen, Medien-Objekten, Tasks...) anstatt Kennung "automatisch" ein selber definierter Bezeichner dort eingegeben wird. Achtung - man ist selber dann dafür verantwortlich, dass es einen Bezeichner nur einmal in einer Cartridge gibt.

Ich empfehle hier die Verwendung von aussagekräftigen Namen mit Prefixes - z.b. obj_zone_start, obj_timer_tuewas..., var_is_garmin Weiterhin empfehle ich, daß im LUA-Code auf Variablen, die im Urwigo modifiziert werden nur lesend zugegriffen wird und nur in Ausnahmesituationen mal schreibend..

Speicherung / Wieder Laden von LUA-Objekten - z.B. Tables

LUA-Variablen und LUA-Tables werden nicht automatisch beim Speichern mitgesichert und wieder geladen. Bei allen Variablen, die mitgesichert werden sollen, muss man dafür sorgen daß diese in die Table "ZVariables" eingetragen werden (im LUA-Code, der generiert wird). In Urwigo kann man das erreichen, dass in Urwigo Variablen angelegt werden, die in LUA überschrieben werden. Es ist z.B. kein Problem in Urwigo eine Variable "meine_table" anzulegen vom Typ String - im LUA-User-Code dann aber mit meine_table = { "Hallo", "Dies", "ist", "eine", "Table" } zu überschreiben.

Meine Empfehlung: Macht Variablen, die ausschließlich in LUA verwendet werden und nur dort modifiziert werden sollen kenntlich - z.b. Variable u1, Kennung "tbl_fragen", am besten in der Description noch was dazu reinschreiben, wofür ihr die Variable im LUA-Code verwendet

Entkopplung von Urwigo und LUA

Immer dort wo es geht kann im Zusammenspiel zwischen Urwigo und LUA auch mit Funktionen und deren Rückgabeparametern gearbeitet werden. Ihr könnt ohne Probleme in Urwigo folgende Konstrukte verwenden: Festlegen Urwigo-Variable = <benutzerdefinierter Ausdruck lua_funktion(objxyz, ...)>

Über das Urwigo-Konstrukt Benutzerdefinierter Ausdruck könnt ihr sehr schön LUA-Bruchstücke und LUA-Funktionen in Kombination mit Urwigo-Elementen einsetzen.

Play-Anywhere Cartridges

Ich bin ja ein bekennender Fan von WIG-Cartridges, die überall gespielt werden können - viele mögen diese Cartridges nicht.

Einen Play-Anywhere WIG zu erstellen ist nicht sonderlich schwierig - in Wherigo.com sind Play-Anywhere-Cartridges dadurch gekennzeichnet, dass die hochzuladende Cartridge keine Koordinate hat und beim Erstellen der Cartridge auch play anywhere angeklickt ist.

Grundsätzlich wird bei einem Play-Anywhere WIG eine Zone in die Nähe des Spielers bewegt - wie man das geografisch gestalten will ist vom Spiel abhängig - bei Brettspielen gibt es eine regelmässige Anordnung - dazu muss man etwas Geometrie beherrschen - bei anderen Spielen sind es eher zufällige Koordinaten, die einen Bezug zur Koordinate des Spielers haben.

Play-Anywhere-Cartridges werden üblicherweise mit LUA-Code gemacht - ich kenne jetzt keinen, der ohne LUA oder benutzerdefinierten Code/Funktionen welche realisiert hat.

Folgende LUA-Funktionen benötigt man:

    Wherigo.TranslatePoint(Player.ObjectLocation , dist, bearing)   
    --macht eine Koordinatenprojektion - hier von der Spieler-Koordinate um eine Distanz und einen Winkel
    --Achtung - die Distanz muss vom Typ ein Wherigo.Distance() sein - siehe Beispielcode
    --bearing ist eine Richtung - ausnahmsweise hier mal in Grad - das ist halt so implementiert
    d, b = Wherigo.VectorToPoint(last_pos, Player.ObjectLocation)
    --hier kann man sich zwischen zwei Koordinaten die Distance (Wherigo.Distance()!) und den Winkel geben lassen

Wichtig zu wissen:

  • eine Zone kann nur bewegt werden, wenn sie deaktiviert und wieder aktiviert wird
  • überfordert den Player nicht - eine einzige Zone pro Sekunde wild in der Gegend rumzuschieben ist ausreichend für die meisten Player, um sie zu beschäftigen
  • die Geografie einer Zone wird über die Attribute OriginalPoint und Points (eine Table) bestimmt - wichtig auf jeden Fall die Points - hierzu setzt man meist eine Funktion GetZonePoints ein, die eine regelmässige Zonenform aus einem Punkt zurückgibt (siehe Beispiel)

Hier ein Beispiel für eine schöne achteckige Zone

   function GetZonePoints(refPt, radi)
     local dist = Wherigo.Distance(radi, 'm')
     local pts = {
     Wherigo.TranslatePoint(refPt, dist, 22.5),
     Wherigo.TranslatePoint(refPt, dist, 67.5),
     Wherigo.TranslatePoint(refPt, dist, 112.5),
     Wherigo.TranslatePoint(refPt, dist, 157.5),
     Wherigo.TranslatePoint(refPt, dist, 202.5),
     Wherigo.TranslatePoint(refPt, dist, 247.5),
     Wherigo.TranslatePoint(refPt, dist, 292.5),
     Wherigo.TranslatePoint(refPt, dist, 337.5),
     }
     return pts
   end

Und hier ist eine Beispielcartridge in Urwigo, die das demonstriert: Datei:Play anywhere.zip

Eine Spezialversion der Play-Anywhere-Cartridges ist die von mir so bezeichnete "Walk-Anywhere" Cartridge. Dabei muss sich der Spieler nicht nach der Zone richten, sondern die Zone legt sich dem Spieler in den Weg.

Hier eine mit Urwigo / Benutzerdefinierter Code geschriebene Beispielcartridge Datei:Walk anywhere.zip

Zeilenumbruch

Es ist gar nicht so einfach, in einen String einen Zeilenumbruch einzubauen. Die Standard-C-Syntax, bzw. HTML-Syntax funktioniert nicht zuverlässig.

In LUA kann man das so machen:

cr = [[
]]

Legt man jetzt in URWIGO eine Variable mit Namen und (wichtig !) Identifier "cr", dann kann man diese Variable so verwenden:

"Zeile1"..cr.."Zeile2"

Ebenso kann man sie in Urwigo selber natürlich auch ansprechen, z.b über ein Concatenate in Messageboxen :
Zeilenumbruchvariable.png

Zone bei Betreten vergrößern und bei Verlassen wieder auf Originalgröße verkleinern

Da beim Wherigo-Spielen häufig die Spieler nur zögerlich in die Zonen laufen und daher an den Zonengrenzen stehen bleiben, führt dies häufig zu Problemen, daß infolge von GPS-Schwankungen die Zone oftmals wieder verlassen wird. Dadurch sind Items, die sichtbar sein sollen unsichtbar und die Spieler oftmals verwirrt oder überfordert.

Abhilfe schafft hier ein kleines LUA-Script, das - eingebaut in Zonen betreten bzw. Verlassen die Zonen vergrößert.

Achtung, Player-Abhängig kann u.U. ein onEnter/onExit-Event mehrfach gefeuert werden, da die Zone zum Vergrößern bzw. Verkleinern auf inactive und wieder active gesetzt werden muß.

Die LUA-Funktion resize_zone muss einfach bei einem OnEnter bzw. OnExit-Event in Urwigo aufgerufen werden. Wichtig: Identifier der Zone setzen und in der Nutzer-Funktion verwenden - siehe Beispiel-Cartridge Datei:Resize zone.zip

Korrigierte Version - Endlosschleife für Android behoben und Absturz I-Phone (Wherigo.Distance-Objekte)

Realisierung eines Tagebuchs

Mit Hilfe einer LUA-Table wird ein Tagebuch realisiert. Das Tagebuch hat mehrere Seiten - über Kommandos kann vor- und zurückgeblättert werden. Die Detail-Einträge des Tagebuchs werden über einen Dialog angezeigt. Im Beispiel hier sind immer 3 feste Dialogseiten definiert, d.h. Die Tagebucheinträge eines Tages werden noch auf 3 Seiten verteilt. Da die Einträge in einer Table sind, könnte das Tagebuch auch ohne Probleme dynamisch erweitert werden - im angegebenen Beispiel schaltet ein Timer alle 30 Sekunden den nächsten Tagebucheintrag frei. Datei: Datei:Tagebuch.zip

Alarmierung bei Überschreiten einer Linie

Manchmal möchte man beim Überschreiten einer Linie irgendetwas machen. Da das I-Phone anscheinend einen bösen Bug hat, daß u.U. auch versteckte Zonen angezeigt werden, habe ich hier mal eine LUA basierte Lösung gestrickt, die beim Überschreiten einer Linie einen Alarm auslöst. Das ganze wird mit einem 1-sekunden-Timer überprüft.

Hier das Beispiel Datei:Crossline.zip

Messung der zurückgelegten Strecke

Mit Hilfe von ein wenig LUA und einem Timer sollte man relativ genau die durch den Spieler zurückgelegte Strecke messen können.

Hier die Cartridge - das LUA-File ist separat - die Cartridge ist mit Urwigo ab V1.13 erstellt worden (Inline LUA)

Hier das Beispiel Datei:Distance measure.zip

Verfolgung durch einen Agenten

Folgende Problemlösung soll mit Hilfe von etwas LUA-Code angegangen werden.

Der Spieler bewegt sich irgendwohin - mal schneller - mal langsamer. Ein feindlicher (oder freundlicher?) Agent beschattet ihn. Er folgt ihm in gewissem Abstand....

Später kann man das ganze natürlich ausbauen, dass z.b. der Agent (in Form einer Zone) mal zu sehen ist - oder auch zwischendurch unsichtbar ist.

Wenn man dem Agenten zu nahe kommt - passiert irgendwas - in diesem Fall wird eine Meldung ausgegeben.

Wir benötigen hierfür

  • Eine Zone für den Agenten
  • eine LUA-Funktion zur Initialisierung - z.b. soll der Agent bei Spielstart in eine bestimmte Richtung/Entfernung positioniert werden
  • einen Timer
  • und eine LUA-Funktion, die die Agentenzone abhängig von der Spielerbewegung bewegt - allerdings nur mit bestimmten Geschwindigkeiten

Na dann fangen wir mal an:

  • Zone anlegen für den Agenten - meinetwegen auch mit einem Character Agent, der sich in der Zone Agent befindet und sichtbar ist, sobald man die Zone betritt.
  • die erste LUA-Funktion: nennen wir sie mal zone_projection(zone, point, bearing, distance,radius) - diese soll eine gewünschte zone von einem gewünschten Punkt aus (könnte z.B. Player.ObjectLocation sein) um ne bestimmte distanz in ne bestimmte Richtung setzen. Der Radius der Zonengrösse soll festgelegt werden können. Die Initialisierung machen wir mal verzögert, manche Geräte haben beim Start der Cartridge keine gültige Position
  • ein Timer wird jede Sekunde aufgerufen
  • und da rufen wir dann ne LUA-Funktion move_zone(zone, point, v_min, v_max) auf mit Rückgabeparameter Entfernung zum Punkt...Die Funktion ermittelt die Richtung hin zu point und bewegt sich mit einer Geschwindigkeit zwischen v_min und v_max auf den Point zu (Point z.b. Player.ObjectLocation)
  • Die Steuerung - wann die Zone bewegt wird (deaktivieren und wieder aktivieren) machen wir im Timer..
  • weitere Erweiterungen - siehe aktuelle Cartridgeversion


Und hier die Cartridge Datei:Agent.urwigo.zip


Automatische Aktivierung / Deaktivierung von Zonen / Nicht Lineare Cartridges

Folgende Problemlösung soll mit Hilfe von etwas LUA-Code angegangen werden. Es soll eine Cartridge erstellt werden, bei der bestimmte Zonen in beliebiger Reihenfolge besucht werden können - Es sind auf jeden Fall mehr als die magischen 7 Zonen (Garmin) vorgesehen, so daß ohne zusätzliche Mechanismen (Deaktivierung nicht benötigter Zonen) die Cartridge auf Garmin nicht laufen wird.

Ähnliche Problemstellungen gab es bereits - sowie entsprechende Posts auf Geoclub (100 Zonen auch auf Garmin) - jedoch wurde hier nicht mit Zonen gearbeitet, sondern mit Tables, die Koordinaten enthalten (Beispiel: Geo-Memory, Leipziger Stadtführung).

Aufgrund eines Posts im Earwigo-Forum habe ich hierzu eine Lösung überlegt, die mit Urwigo und LUA umgesetzt wurde.

Grundsätzliche Lösung

  • Ein zyklischer Timer in Urwigo ruft die LUA-Funktion activate_deactivate_zones auf
  • Alle Zonen, die automatisch aktiviert- / deaktiviert werden sollen sind über einen gemeinsamen Namensteil identifizierbar. ACHTUNG Dieser Namensteil darf in keinem anderen Objekt verwendet werden (z.B. STAGE 1...n)
  • Die Funktion activate_deactivate_zones identifiziert alle Wherigo-Objekte, die den Namensteil beinhalten und bestimmt die Entfernung zum Spieler (Übergabeparmameter basename, cartridge Variable)
  • Die Entfernungen zum Spieler werden in den Zonen-Objekten gespeichert und gleichzeitig in einer Table, die aufsteigend sortiert wird - diese Tabelle wird dazu benutzt, die Entfernung zu bestimmen, bis zu der die Zonen aktiviert werden - liegen Zonen weiter vom Spieler entfernt, werden sie deaktiviert.
  • Über den zusätzlichen Übergabeparameter max_active wird festgelegt, wieviele der nächstgelegenen Zonen maximal sichtbar sind (maximal deshalb, weil hier auch versteckte Zonen erlaubt sind, die ebenfalls aktiviert / deaktiviert werden - diese sind dann natürlich nicht sichtbar)
  • Zonen nach Erledigung der in der Zone zu erfüllenden Aktivitäten aus der automatischen Aktivierung/Deaktivierung rausnehmen --> einfachste Realisierung: Änderung des Namens der Zone und Deaktivierung.

Anbei der Code der LUA-Routinen sowie die Urwigo-Cartridge

 function get_object_by_name(my_cartridge, object_name, start_index)
   local objects = my_cartridge.AllZObjects
   if object_name == "nil" then return nil
   elseif object_name == "Player" then return Player
   else
       for index = start_index, #objects, 1 do
           local inhalt = objects[index]
           local start = 0
           local ende = 0
           if inhalt.Name ~= nil then
               local start, ende = string.find(inhalt.Name, object_name)
               --print(start,ende)
               if start ~= nil then
                   if start > 0 then
                       return index, inhalt
                   end
               end
           end
       end
   end
   return 0, nil
 end
 function activate_deactivate_acc_distance(cartridge_var, basename, distance)
   local index = 0
   local object = nil
   repeat
      index, object = get_object_by_name(cartridge_var, basename, index + 1)
      if object ~= nil then 
           if object.distance2player <= distance then
               object.Active = true
           else
               object.Active = false
           end
      end
    until index == 0   
 end
 function activate_deactivate_zones(cartridge_var, basename, max_active)
   local index = 0
   local object = nil
   local distances = {}
   repeat
      index, object = get_object_by_name(cartridge_var, basename, index + 1)
      if object ~= nil then 
          local d, b = Wherigo.VectorToPoint(object.OriginalPoint, Player.ObjectLocation)
          local dn = d:GetValue'm'
          object.distance2player = dn --store distance in object
          table.insert(distances,dn) --store distance also in a table which will be sorted
       end
   until index == 0
   table.sort(distances)
   if max_active > #distances then
       max_active = #distances
   end
   activate_deactivate_acc_distance(cartridge_var, basename, distances[max_active]) 
 end

Datei:Wherigo auto activate.zip

Meine Werkzeuge
Namensräume
Varianten
Aktionen
Navigation
Werkzeuge