Leg-das-Rohr-Minispiel

  • #50, z sebastianFriday, 21. April 2017, 19:59 hodinky 8 years ago
    joar, macht Sinn^^

    Die Funktion, die per L-Klick auf ein Feld dann aufgerufen wird, sollte dann eine Bedingung abfragen, ob dieses Feld schon Wasser hat und falls nicht die Werte des Rohrs mit dem des auf dem "Ersatzstapel" liegenden tauschen.

    Kapitán

    2346 Posts


  • #51, z MachtnixFriday, 21. April 2017, 22:09 hodinky 8 years ago
    Die Funktion game.currentObject gibt mir wie gewünscht den Objektnamen aus, aber dahinter stehen noch zwei Zahlen in Klammern: (6,22). Bei anderen Objekten (6,20). Was bedeuten die? Die Mausposition ist es nicht und die Objekt-ID auch nicht. Die Anzahl der Zeichen bei Feld1 ist 5.

    Kapitán

    1097 Posts

  • #52, z sebastianFriday, 21. April 2017, 22:18 hodinky 8 years ago
    da du hier wahrscheinlich hinter der Anfrage kein zB :getName() oder :getId() genutzt hast, gibt er dir das komplette Objekt zurück. Zusätzlich zum Namen steht dahinter in Klammern die TableId in der das Objekt zu finden ist (6 = Objekte) und die ObjektId innerhalt der Tabelle ( 20/22)

    Kapitán

    2346 Posts

  • #53, z MachtnixSaturday, 22. April 2017, 01:42 hodinky 8 years ago
    Ich finde, es gibt zuwenig konkrete Hilfstellung für das Visionaire-Objektmodell (ich meine nicht für Lua!!).
    Meine Herangehensweise ist, die Data Struktur oder das Scripting oder das Wiki zu durchsuchen, was ungefähr zu meinem Problem passen könnte. Dann probiere ich stundenlang tausend verschiedene Schreibweisen aus, bis - meistens - irgendeine funktioniert. Aber WARUM manches geht, manches nicht, bleibt mir ein Rätsel... Dieses ganze Visionaire-Tabellen-Objekt-Modell verstehe ich nicht.

    Nehmen wir z.B. das game.CurrentObject. Füge ich mit einem Doppelpunkt getName() dahinter, bekomme ich den Namen angezeigt. So schön, so gut. (Danke an Sebastian).
    Mit get hole ich mir also was. Jetzt möchte ich den Inhalt des Wertes "Typ2" in Lua rausbekommen, der im aktuellen Objekt liegt. Er ist 2.

    Also probiere ich x=game.CurrentObject:getValue(Typ2). Ich will ja den Inhalt vom Wert "Typ2" als Integer-Variable x haben. Funktioniert nicht. Also schaue ich nach und finde getInt. Ich probiere game.CurrentObject:getInt(Typ2). Klappt auch nicht.

    Dann setze ich Anführungsstriche: game.CurrentObject:getInt("Typ2"). Genauso Murks. Wie sieht es mit eckigen Klammern aus? Was passiert, wenn ich getInt(VValues(Typ2)) schreibe? Brauche ich noch zusätzlich eine Pfad-Angabe? Und, und, und... Es ist sehr frustrierend, weil ich nirgendwo (außer der englischen Kurzanleitung im Wiki) irgendwelche Hilfen finde. Und da sind viele Einträge außerdem noch leer.

    Manchmal stehen dort Beispielzeilen, die vielleicht den richtigen Einsatzzweck schon andeuten; dann klaue ich mir die und setze meine eigenen Namen ein. Es gibt auch einige Scripte, in die ich schauen kann, und wenn ich dort auf eine Formatierung stoße, die zu meiner passt, kopiere ich die. Aber wirklich sinnvoll ist das alles nicht...

    Und jedesmal Spiel starten und in die Kommandozeile und "print log" aufrufen...

    Nachdem das alles nicht klappt, überlege ich mir vielleicht, dass ich den Wert "Typ2" gar nicht brauche, weil im Namen "Feld2" ja die 2 bereits enthalten ist. Ich muss jetzt irgendwie die 2 aus dem Namensstring herausextrahieren, was unterm Strich aber noch aufwendiger ist. Jedenfalls habe ich keinen Plan, wie das gehen könnte. Ich könnte mit String-Funktionen, die ich (noch) nicht kenne, die ersten vier Buchstaben entfernen, und die verbleibenden 1-25 Ziffern jeweils irgendwie in Zahlen umwandeln (ich nehme an, dass "Feld2" ein String ist, die 2 also keine Zahl, sondern ein Zeichen).... VIEL zu kompliziert für mich.

    Und was mache ich, wenn ich tatsächlich einen Wert auslesen möchte, der sich ändert??? Dann bin ich wieder da, wo ich zu Anfang stand...

    Ich hätte wirklich gerne ein schönes dickes Handbuch mit Erläuterungen, Programmierbeispielen und Fallstricken (in deutsch)... roll

    Machtnix (Name ist z.Z. Programm...)

    Kapitán

    1097 Posts

  • #54, z sebastianSaturday, 22. April 2017, 10:36 hodinky 8 years ago
    was das Handbuch angeht gebe ich dir vollkommen recht ^__^

    Das Wiki ist hier größtenteils als Dokumentation zu verstehen, nicht als Leitfaden. 

    ich versuche mal zu erklären warum deine Ansätze noch nicht ganz gefruchtet haben. 

    Du willst also den Int Wert mit dem Namen Typ2 vom aktuellen Objekt haben. 

    Hier führen mehrere Wege nach Rom

    Zum ersten : getXXX()  ist eine Funktion, die ein Eintrag aus der Datenstruktur zurückgibt. 

    Ein setValue z.B.  wird universell genutzt um ein Feld in der Datenstruktur zu schreiben. Quasi alle Felder, wo du einen grünen Haken auf der Datenstruktur Seite im Wiki siehst kannst du hiermit neu beschreiben. 

    getValue gibt es an sich gar nicht, sondern nur getBool, getId, getInt, etc. Speziell für deren Nutzen.  Beziehen sich wie gesagt auf die Datenstruktur... 

    Das Problem ist, dass ein Objekt laut Datenstruktur keinen Int Wert für die enthaltenen Values besitzt. Lediglich Verweise (Link) auf alle Values die dieses Objekt besitzt:

    ObjectValues ,  t_links to Value (parent) ,  All values of the object.

    Du kannst also hier noch nicht den Wert abfragen, weil das Objekt diesen gar nicht kennt. Nur den Weg zu ALLEN seinen Values. 

    Du willst aber einen speziellen Wert einer Value haben. 

    Jetzt kannst du den harten Weg gehen, sie die Links zu jeder Value  "holen" (getLinks)  und per for Schleife jede Value des Objekts durchgehen, checken ob sie "Typ2" heißt und falls ja dessen Wert (endlich) zurückgeben. Kann man machen, ist aber grausig... 

    Es gibt auch eine getLink Funktion, die bringt dir aber nur etwas, wenn ein DataStructure Objekt auf ein anderes verweist, nicht auf ein Array /eine Liste an Objekten. 

    Doof. 

    Hier würde ich jetzt zur neuen Schreibweise (auch so ein nerviges Thema) tendieren. 
    Erstmal das Objekt in eine Lua Variable speichern :

    obj = game.CurrentObject

    und dann

    x = obj.ObjectValues["Typ2"].Int

    woher habe ich jetzt "ObjectValues"? steht ja nicht in der Datenstruktur... 
    Naja. Es gibt ein paar Shortcuts, wie hier beschrieben :

    In eines der Beispiele wird hier noch per alter Methode 

    getObject("Scenes[scene_name].SceneObjects[object_name].ObjectValues[value_name]") 

    genutzt um die Value als Objekt zu bekommen (nich nicht deren Int oder String Wert), aber wie oben gezeigt sollte das auch in der neuen Schreibweise klappen . 

    Verwirrend hier ist wohl auch zu wissen wo man jetzt Sachen in "Anführungszeichen" setzen muss/darf. 

    Generell ist alles, was in "" steht als String und nicht als Wert zu verstehen. Sprich 

    x = y dann würde x den wert von y annehmen. 
    x = "y" wäre x = dem Wort/String "y" - dem Buchstaben. 

    Ich nutze die "" also da, wo ich den Wortlaut innerhalb der "" benötige. 

    Kapitán

    2346 Posts

  • #55, z MachtnixSaturday, 22. April 2017, 20:24 hodinky 8 years ago
    Vielen, vielen Dank! Jetzt habe ich erstmal einige Tage genug zu probieren... smile



    Bei Strings sind mir die Anführungsstriche übrigens schon klar: "das ist ein Text".

    Aber wann in einem Visionaire-Objekt Anführungsstriche (sogar einfache), eckige oder runde Klammern gesetzt werden, ist mir noch ein Buch mit sieben Siegeln, z.B aus dem Scripting:

    getObject("Conditions[mwtoggle]"):getBool(VConditionValue) oder
    Conditions["puzzle_solved"].ConditionValue oder
    startAction( "Game.GameCurrentScene.SceneActions[act_puzzle_solved]" ) oder
    if condition 'mwtoggle' is false

    Genau genommen wirkt das total beliebig zusammengewürfelt auf mich...

    Mein Hauptproblem ist eben, dass ich ein Programmieralgorithmuslegastheniker bin... wink

    Kapitán

    1097 Posts

  • #56, z sebastianSaturday, 22. April 2017, 20:56 hodinky 8 years ago
    Funktionen werden immer mit () gemacht

    getObject(übergabewert)
    getBool(übergabewert)
    getInt(übergabewert)
    startAction(übergabewert)

    VS "Objekte" selber werden in [] genauer definiert:

    Conditions["condition_name"]
    Values["value_name"]
    Scenes["scene_name"].SceneObjects["object_ame"]
    Interfaces["interface_name"]

    In der alten/langen schreibweise sind die "" etwas anders zu setzen. Siehe unten.


    Manche Objekte muss man nicht genauer definieren, weil sie Einzigartig sind:

    Game.GameCurrentScene
    ist das gleiche wie
    getObject("Game.GameCurrentScene")


    Was dich jetzt vielleicht verwirrt ist wohl die Mischung von alter und neuer Schreibweise:

    alt (lang):
    getObject("Conditions[puzzle_solved]"):getBool(VConditionValue)

    neu (kurz)
    Conditions["puzzle_solved"].ConditionValue oder

    sind exakt dasselbe.

    BEI ALT: hier ist der Übergabewert von getObject() komplett in "", damit die getObject-Funktion etwas damit anfangen kann. Der ganze String wird hier übergeben.

    BEI NEU: hier sind die "" nur um den eigentlichen String, der kontrolliert werden soll

    man kanns auch mixen:

    getObject("Conditions[puzzle_solved]").ConditionValue



    Kapitán

    2346 Posts

  • #57, z MachtnixMonday, 24. April 2017, 20:13 hodinky 8 years ago
    Ich bin jetzt erstmal zurück auf "Los"...

    Ich habe Riesenprobleme damit, ein Ziel in Programmiercode umzusetzen. Das heißt, ich muss mir vorerst klar werden, wie ich die Probleme überhaupt angehe. Anfangs bastele ich mir meist einfache "Wenn-dann"-Abfragen. Die funktionieren meistens, aber manchmal auch nicht - und ich suche dann tagelang, warum sie nicht funktionieren.

    1. Das beginnt bereits mit der Spieleröffnung. Ich weiß, dass jedes der 26 Felder zufällig mit einem Rohrstück von 1-6 belegt werden soll. Anfangs habe ich bei jedem Feld den Zufallsgenerator neu laufen lassen - das führte aber auch manchmal dazu, dass ich 24 gerade Rohre bekam (übertrieben ausgedrückt), das Spiel war also gar nicht lösbar. Ich muss also aus einer vorher festgelegten Zahl von Rohrstücken zufällig auswählen. Theoretisch weiß ich, wie das geht:
    Tabelle Typen = {1,1,1,1,1,2,2,2,2,3,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6}
    
    Würfele die 26 Zahlen durcheinander:
    
    Tabelle Typen = {5,3,4,1,1,5,4,3,3,1,6,3,...}
    
    Ordne jedem Indice-Eintrag ein Feld von 1-26 zu. 
    
    Feld1 = 5
    Feld2 = 3
    Feld3 = 4
    Feld4 = 1
    
    usw.
    

    Deswegen versuche ich erstmal, Tabellen in Lua zu verstehen. Jedenfalls habe ich keine Ahnung, wie man eine bestehende Tabelle zufällig sortiert. Es gibt irgendwas mit "pair", aber das ist mir noch zu hoch.

    2. Der Austausch klappt in Lua nicht (irgendwas mit den globalen oder nicht-globalen Variablen??), aber er funktionierte auch schon im Editor nicht. Es muss ein großer Denkfehler drin sein. Das Prinzip scheint richtig zu sein, aber die Aktualisierung jedesmal beim Linksklick ist noch falsch.

    3. Das größte Problem ist natürlich die Abfrage. welche Puzzleteile miteinander verbunden sind.
    Da habe ich nicht den blassesten Schimmer. Jeder Rohrtyp ermöglicht bestimmte Kombinationen. Ich werde testweise deshalb folgendes probieren:

    Wenn n (bei Linksklick) = Typ 1 ist (ein waagerechtes liegendes Rohr), dann
         wenn n+1 (nach rechts) = Typ1, dann
               Rohr verbunden (weil zwei waagerechte Rohre nebeneinander verbunden sind)
         end
         .
         . (insgesamt sechs Abfragen)
         wenn n+1 = Typ2 (senkrechtes Rohr), dann
               Rohr nicht verbunden
         end
         .
         .
         wenn n+5 (nach unten) = Typ1, dann
               Rohr nicht verbunden
         end
         .
         .
    end
    Wenn n = Typ2, dann
         wenn n+1 = Typ1, dann
               Rohr nicht verbunden
         end
         .
         .
    end
    usw.
    
       
    Das ist natürlich der Wahnsinn. Müsste zwar funktionieren, aber ein Programmierer würde sich an die Stirn fassen. Nun gut, ich bin keiner. Dann kommt noch hinzu, wie ich die Resultate dieser Abfragen nachher verarbeite. Keine Ahnung. Und am Schluss noch die Frage: wie fließt das Wasser? Woher weiß ich, wo der Einlass, wo der Ausfluss ist...?

    Versuche gerade, zu Fuß die Erde zu umrunden, glaube ich. Und ohne Schuhe...

    Kapitán

    1097 Posts

  • #58, z sebastianMonday, 24. April 2017, 23:09 hodinky 8 years ago
    zu 1:
    Tauschen der Tabellen würde ich, indem ich natürlich die beiden Tabellen habe:

    global fields = {}

    global fields_sorted = {1,1,1,1,1,2,2,2,2,3,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6}

    und dann per while schleife , die von 1 bis #fields_sorted geht folgendes (in der Art) machen:
    function shuffle()
       while fields_not_sorted() do                  --solange nicht alle felder sortiert sind
    
          field = math.random(1,#fields_sorted)      --zufallszahl zwischen 1 und "anzahl felder" wählen
          if not feld_sorted[field] == -1 then       --wenn das feld in dem sortieren feld nicht -1 ist haben wir es noch nicht angefasst und können es in die richtige tabelle einsortieren.
            fields[i] = feld_sorted[field]           --richtige tabelle platz i mit inhalt von aktuell zufällig ausgewählten, nicht -1 beinhaltenden feld setzen
            feld_sorted[field] = -1                  --feld in der sortierten tabelle auf -1 setzen, damit er nicht nochmals gewählt wird
          end
    
       end
    end
    <span>
    </span>
    <span>(# < tabellenname ><tabellenname> = anzahl der beinhaltenden felder)</tabellenname></span>
    function fields_not_sorted() --solange nicht alle felder in fields_sorted nicht -1 sind, gibt true zurück
    
     for for i=1, #fields_sorted do 
    
     if not fields_sorted[i] == -1 then return true end 
    
     end
     return false
    
    end


    !!!ungetestet!!!

    zu 2: variablen haben (auch in anderen Sprachen) einen gewissen "scope", also eine Art Bereich in der sie gültig sind bzw. existieren.

    schreibst du ein "global" vor die variable ist die variable von allen scripten nutzbar
    variablen, die du "local" setzt, aber außerhalb von funktionen sind nur in diesem Script verfügbar. und zu  guter letzt gibt es noch locale variablen innerhalb der funktionen. wenn du hier cariablen anlegst sind diese nur innerhalb der funktion gültig.

    zu 3: hier würde ich das Problem angehen, wenn das Wasser erst fließt. Sprich bei einer Funktion, die den Wasserstand eines JEDEN Rohrs aktualisiert* (+1 setzt) und voll ist (sagen wir mal wenn der Wert 5 erreicht ist das Rohr voll), checkst du zum einen vom rohr den Typ und von welcher Seite ursprünglich wasser eingeflossen ist. Wenn du diese beiden Sachen weißt, kannst du auch bestimmen wo das Wasser rausfließen würde. Wenn das Wasser rechts rausfließen würde checkst** du Feld n+1, wenn links, n#1, wenn oben n-5 und unten n+5 und wenn das Rohr daneben passt setzt du hier dessen Wasser Bedingung auf true. 

    *hier macht es sinn eine function zu schreiben, die eine for schleife setzt, die für jedes Feld, welches "Wasser = true" hat den Wert Wasserstand + 1 setzt.

    **das checken sollte also folgendes machen: anhand des aufrufs kannst du natürlich auch einen wert übergeben, der angibt, dass von links/rechts/oben/unten das Wasser reinkommen WÜRDE. ist der Typ des neu zu überprüfenden Felds aber nicht kompatibel (also falsch ausgerichtet) wird "false" zurückgegeben und das Spiel muss beendet werden, da das Wasser nicht weiter kommt.


    Das ganze ist jetzt recht unausgereift niedergeschrieben und ggf. ist die oben geschriebene Funktion auch nocht nicht 100%ig toll, aber ich hoffe du bekommst hier zumindest auf die richtige Spur für dein Projekt =)


    Kapitán

    2346 Posts

  • #59, z MachtnixMonday, 24. April 2017, 23:56 hodinky 8 years ago
    OMG... würde ich das Spiel verkaufen, würdest du (mindestens) die Hälfte der Einnahmen kriegen... wink
    Danke.

    Aber ganz ehrlich: ich würde auf soetwas nie im Leben kommen. Diese "Denkungsweise" ist mir sowas von fremd. Ich fertige lieber die Bilder und Animationen an (die alle schon fertig sind...).

    Ich habe eine abartig schmutzige und schnelle Lösung genommen: ich habe einfach 20 Tabellen zufällig angelegt und lasse sie nacheinander durchlaufen. Ich habe keinen Zufallsgenerator genommen, sondern das Mischen selbst besorgt (frei nach Zorg: wenn man will, dass etwas gemacht wird, muss man es selbst machen...). Klar ist das blöd. Aber wenigstens gibt es jetzt keine 24 geraden Rohre mehr...

    Wer das Spiel 20mal hintereinander spielt, merkt vielleicht, dass es sich wiederholt - die anderen nicht. Da sieht man, dass es mir immer nur um die Lösung eines Problems - egal wie - geht. Elegantes Programmieren ist toll, aber wenn es auch ohne klappt, verzichte ich darauf...

    Deine Tipps mit dem Wassereinlauf sind - nun - auch noch in Kladde. Das Konzept ist einleuchtend. Ich brauche eine Variable dafür, ob schon Wasser drin ist (das ist für mich aber nur eine Randbedingung - die Überprüfung der Verbindungen sind das größte Hindernis), aber wie und mit was für Mitteln ich das alles überprüfe, ist mir KONKRET noch nicht klar. Deine Überlegungen
    Sprich bei einer Funktion, die den Wasserstand eines JEDEN Rohrs aktualisiert* (+1 setzt) und voll ist (sagen wir mal wenn der Wert 5 erreicht ist das Rohr voll), checkst du zum einen vom rohr den Typ und von welcher Seite ursprünglich wasser eingeflossen ist. Wenn du diese beiden Sachen weißt, kannst du auch bestimmen wo das Wasser rausfließen würde.
    sind absolut logisch, aber... an der konkreten Umsetzung hapert es. Ich würde übrigens den letzten Frame der Einfließanimation als Trigger dafür wählen, ob das Rohrstück voll ist. Aber dafür brauche ich auch unterschiedliche 25 Werte (oder etwas weniger).

    Für die Überprüfung der Verbindungen benötige ich anscheinend tatsächlich vier verschiedene Werte, aber müssen die Öffnungen jetzt nun für jedes Feld eindeutig sein (also z.B. Feld8_Ost, Feld15_Süd) - oder reicht es, wenn jedes Feld einfach viermal dieselben Variablen hat (Nord, Ost, Süd, West)? Dann dürften die nur lokal gelten, werden abgefragt und in einer eindeutigen Variablen gespeichert. Oder wie auch immer... Vielleicht gehts auch ohne diese vier Werte, denn jedes Rohrstück ist ja an sich schon durch seine Kombinationsmöglichkeiten definiert. Wenn also ein bestimmtes Rohr neben einem anderen liegt, stehen die Chancen, ob sie passen, ja bereits fest.

    Ich habe 20 A4-Seiten vollgekritzelt, wie ich was überprüfen könnte, aber letztendlich bin ich kein Stück weiter. Die Methode, die vier Öffnungen mit 1-4 zu bennen und sie jeweils mit den vier angrenzenden Feldern (+1, -1, +5, -5) zu vergleichen, ist wohl vielversprechend. Und die Überprüfung der Spielfeldränder kommt ja auch noch...

    Abgesehen davon soll die Animation natürlich auch flüssig ablaufen. Ich muss also bei jedem Übergang abzählen, wie lange die vorherige Animation gedauert hat, bevor ich die im nächsten Rohr starte. Oder die nächste im letzten Frame der vorherigen anstoßen. Da gehört noch ein Script hin, denn woher weiß die alte Ani, welche neue Ani als nächstes gestartet werden muss...? Hört sich nach Peanuts an, aber ich befürchte, daran knabbere ich genauso lange. Aber das kommt GAAANZ am Schluss smile

    Ich glaube jedoch, dass es sinnvoll ist, einen Programmierdenkerfreund hinzuzuziehen, weil es sonst zu lange dauert und ich dann auch die Motivation verliere.

    Deswegen mache ich mir so früh einen Kopf über alle Probleme, weil ich vielleicht doch das 7x7-Feld wähle, weil das besser zu rechnen ist. Nachher kann ich das nur mühsam abändern.

    Kapitán

    1097 Posts

  • #60, z MachtnixTuesday, 25. April 2017, 13:37 hodinky 8 years ago
    Heute morgen beim Frühstück hatte ich eine Ahnung von einer Idee: ich muss nicht die Einzelfelder untersuchen, sondern die Verbindungen. Es gibt 40 gültige Verbindungen. Ich muss klären, ob Osten mit Westen des nächsten Feldes, und ob Süden und Norden zusammenpassen. Dazu bekommt jedes Feld eine Tabelle: Tab1={0,1,0,1} für die Boolschen Werte der vier Eingänge, die ich miteinander vergleiche (über Kreuz). 

    Kapitán

    1097 Posts