Leg-das-Rohr-Minispiel

  • #10, z sebastianSaturday, 15. April 2017, 21:19 hodinky 8 years ago
    Ist schon etwas her, dass Thema. Wie genau sollte nochmal das Spiel funktionieren? Wäre doch gelacht, wenn es da nicht irgendwie auch eine einfachere Herangehensweise geben würde...
    (ich steige immer noch nicht so ganz dahinter oder verstehe das irgendwie falsch)

    Kapitán

    2346 Posts


  • #11, z MachtnixSaturday, 15. April 2017, 22:12 hodinky 8 years ago
    Ja, stimmt. Ich hatte es lange Zeit verbuddelt, weil es einfach zu schwer war. Aber mir kribbelt es unter den Fingernägeln: wäre doch gelacht... wink Soviel: ich muss mehr Lua und Visionaire-Objektssystem dazu lernen...

    Die Minispiel-Idee stammt aus Bioshock.

    Ich habe anfangs ein Feld mit (bei mir 5x5, bei Bioshock waren es mehr) 25 geschlossenen Deckeln und einem Austauschplatz, auf dem bereits ein Rohrelement offen liegt. Klicke ich irgendwo ein Feld das erste Mal an, wird der Deckel entfernt und ein zufälliges Rohrelement erscheint. Es gibt sechs Elemente: zwei gerade Stücke (senkrecht, waagerecht) und vier gebogene Stücke. Keine Kreuzungen und kein Drehen. Von links beginnt nach einer gewissen Zeit die Flüssigkeit einzulaufen, die rechts unten durch ein komplett verbundenes Rohr abließen muss. Es gibt keine T-Stücke, der Spieler könnte aber auch S-Kurven legen, das heißt die Flüssigkeit kann auch mal nach oben oder nach links fließen - was das Problem bei der späteren Animation ausmacht (ich muss irgendwie bestimmen, von wo die Flüssigkeit in das Rohrstück einläuft und danach die passende Animation auswählen). Variable Ein- und Ausflüsse habe ich (noch) nicht vorgesehen.

    Beim zweiten Klicken wird also das Rohrelement auf dem Feld mit dem Element auf dem Tauschplatz vertauscht. Ich habe also KEINE nachrückende Reihe von Rohrelementen, woraus ich das oberste auswählen kann, wie bei Pipe Mania. Als Spieler sehe ich nicht, was unter den Deckeln schlummert; der Spieler muss also fix sein und schnell viele Elemente aufdecken, um durch zielgerichtetes Austauschen das Rohr nach rechts unten zu verbinden. Ein Austausch sollte auch noch bei den leeren Rohrstücken möglich sein, selbst wenn die Flüssigkeit bereits läuft, aber nicht mehr bei den gefüllten. Sind sehr viele Deckel geöffnet, hat der Spieler also die Chance, durch geschicktes Hin- und Heraustauschen das Rohr zu vervollständigen. Der Spieler tauscht also ununterbrochen ein Puzzle-Element vom Feld mit dem Puzzle-Teil auf dem Austauschplatz, das deshalb immer ein anderes ist.

    Es hapert bereits am Anlegen eines zufälligen Rasters von Rohrelementen. Die Idee von AFRLme, jedem Objekt eine Animation mit 7 Frames zuzuordnen und den entsprechenden Frame anzeigen zu lassen (ein Deckel, und 6 Rohrelemente), ist die beste Lösung.
    Die Abfrage, welches Rohr mit welchem zusammenpasst, damit die Flüsigkeit fließen kann, habe ich nicht mal ansatzweise gelöst, da fällt mir einfach keine mathematische Methode ein (außer 1000 if-Abfragen).

    ____

    Yes, that's right. I had been burying it for a long time because it was just too hard. But I'm tingling under the fingernails: would be laughing ...wink So much: I have to learn more Lua and Visionaire object system ...

    The idea is from Bioshock.

    I have initially a field with (with me 5x5, with Bioshock it was more) 25 closed covers and an exchange place on which already a tube element is open. If I place a field somewhere for the first time, the lid is removed and a random pipe element appears. There are six elements: two straight pieces (vertical, horizontal) and four curved pieces. No crossings and no turning. From the left, after a certain time, the liquid begins to flow into the lower right through a completely connected tube. There are no tees, but the player could also put S-curves, that is, the liquid can also flow upwards or left-which makes the problem with the later animation (I have to somehow determine from where the liquid in And then select the appropriate animation). Variable inputs and outputs I have not (yet) provided.

    In the second click, the pipe element on the field is then interchanged with the element on the exchange site. So I do NOT have an advancing set of pipe elements, from which I can pick the top one, as with Pipe Mania. As a player I do not see what is sleeping under the covers; So the player must be fixed and quickly reveal many elements to connect the pipe to the bottom right by purposefully exchanging the pipe. An exchange should also be possible with the empty pipe pieces, even if the liquid already runs, but not with the filled. If a lot of lids are opened, the player has the chance to complete the pipe by cleverly exchanging them. The player is thus constantly exchanging a puzzle element from the field with the puzzle part on the exchange place, which is therefore always different.

    It is already impossible to me  to create a random grid of tube elements. The idea of AFRLme to assign to each object an animation with 7 frames and to display the corresponding frame (one cover, and 6 tube elements) is the best solution.
    The query, which pipe with which fits, so that the liquid can flow, I have not even solved, because I do not find a mathematical method (except 1000 if queries).

    Kapitán

    1097 Posts

  • #12, z sebastianSaturday, 15. April 2017, 23:17 hodinky 8 years ago
    ok, habs glaub ich gerafft. 
    Ich denke die grafische Umsetzung ist erstmal nebensächlich, denke aber auch, dass es mit Animation und entsprechenden Frames am sinnvollsten ist.
    Wir kümmern uns auch erstmal rein um das Aufdecken und Austauschen der Felder, behalten aber im Hinterkopf, dass wenn Wasser im jeweiligen Feld ist dieses nicht mehr ausgetauscht werden kann.

    Jedes Feld hat also folgende Eigenschaften:
    typ: gerade n-s, gerade w-e, kurve n-e , kurve e-s , kurve s-w, kurve w-n
    aufgedeckt: true, false 
    wasser: true, false
    wasser_eintritt: n,s,w,e

    Es gibt 25 Felder + 1 Austauschfeld

    Du legst also ...Achtung...eine Lua Tabelle an mit 25 Feldern:
    global gametable = {}

    Dann willst du natürlich 25 Felder mit content Füllen. Dabei ist bis jetzt nur zufällig welcher Typ/Art diese Felder haben. Der Rest ist beim Start statisch.

    Also gehst du eine for-Schleife durch, die 25x ein neues Feld dem Spielfeld hinzufügt und eine zufälligen Typ zuordnet. für das Feld type rufen wir eine Funktion auf, die uns die Art des Feldes auslost. Ich habe sie mal "select_type()" genannt. Sie gibt später eines der oben genannten typen zurück.
    function create_table()
    for i=1 , 25 , 1 do
        local field = { 
            ["type"] = select_type(),
            ["revealed"] = false,
            ["water"] = false,
            ["water_from"] = nil
        } 
        table.insert(gametable, i, local field) --fügt das kreierte feld dem spielfeld hinzu
        end
    end

    Du fragst dich jetzt bestimmt wie die Felder nun miteinander verknüpft sind, da es ja ein 5x5 Spielfeld ist, aber meine Tabelle 25 nebeneinander/untereinander liegende Felder hat.
    Nunja. Wenn man das Feld aufteilt in 5er schritten:
    01 02 03 04 05
    06 07 08 09 10
    11 12 13 14 15
    16 17 18 19 20
    21 22 23 24 25

    , dann ist natürlich der Weg von Feld 13 nach 12 "-1" bzw. nach 14 "+". Will das Wasser später nach oben oder unten -also auf Feld 08 oder 18, rechne ich einfach "-5" oder "+5".
    Dazu aber später mehr. Ich will hier nur darauf hinweisen, dass es im Hintergrund dieses Feldes um eine reine Tabelle handelt, die keinerlei 2 Dimensionen hat.

    Soweit so gut. Die select_type() funktion wird nun 25x innherlab der create_table() aufgerufen. Hier weiß ich jetzt nicht, ob es für dich egal ist, welches Feld zufällig kommen soll, oder ob es pro Typ auch noch begrenzte Elemente gibt. Sprich dass es im ganzen Spiel nur 6 geraden gibt, der rest sind kurven oder so. Dann müsste man das innerhaln dieser Funktion tracken und ggf. den Zufallswürfel nochmal werfen, wenn eine Maximalgrenze erreicht wurde und ein anderes Feld gelost werden soll.

    Innerhalb der select_type könntest du die math.random funktion verwenden, um eine Zufallszahl zwischen 1 und 6 wählt. Je nach zahl gibt es das entsprechende typ wieder.
    function select_type()
        local type = math.random(1,6)
        local typename
        if type == 1 then
            typename = "gerade n-s"
        elseif type == 2 then
            typename = "gerade w-e"
        elseif type == 3 then
           ...
        end
        return typename
    end

    wo ich das gerade jetzt sehe würde ich sogar lieber auf zahlen zurückgreifen. Sprich Type einfach nur 1-6, dann brauchst du auch keine select_type() funktion und kannst direkt beim anlegen der Tabelle math.random(1,6) nutzen, fertig. Aber falls du wie gesagt noch begrenzte typen machen willst, wo manche rohrstücke nur bestimmt oft vorkommen dürfen, macht eine exra funktion Sinn.


    So jetzt hast du erstmal ein 5x5 Feld mit zufälligen Typen.
    Für das Austauschfeld kannst du ebenfalls eine funktion schreiben, die folgendes hat beinhaltet:
        global change_field = { 
            ["type"] = select_type()
        } 

    Hier brauchen wir ja eigentlich nur den Typ.

    Etwas zur grafischen Umsetzung:
    Erstelle auf einem Interface oder Szene ein Feld mit 5x5 Objekten, dessen Feld per Leftclick Aktion anklickbar ist. Hier brauchst du eine Execute script Action Part, welches das angeklickte Feld gegen das im Austauschfeld austauscht.

    Wir kreieren also (später eine Funktion, die dies tut. Nennen sie switch_field(i)

    Feld 1 oben Links hat die ID1, Feld 5 oben Rechts ID 5 , usw... ID25 ist unten Rechts.
    Im Execute Script Action Part für Feld 1 kommt also rein:
    switch_field(1)

    das Feld oben rechts bekommt 
    switch_field(5)

    verpasst, usw.
    (hier müsste man später noch eine condition drumbauen, dass dies nur passiert, wenn KEIN Wasser im Feld ist bzw. das Feld erstmal aufgedeckt sein muss)

    Zurück im Lua Workspace legen wir also eine Funktion switch_field(i) an, wobei i die ID des angeklickten Feldes ist:
    function switch_field(i) 
        local mixer = {["type"] = gametable[i].type} --used to swap the contents
        
        gametable[i].type = change_field[type]
        change_field[type] = mixer[type]
    end

    Da noch kein Wasser durchfließt, ist ja das einzige, was sich ändert der Typ.

    Du tauschst also hier nur inhalt von tabellenfeldern miteinander aus. Hier wird nichts bewegt oder Positionen abgefragt wink

    Das ganze ist jetzt erstmal ausm Kopf und untested, sollte aber schonmal das Spielfeld mit den einzelnen unterfeldern austauschbar machen. Hier und da könnte man jetzt mit einem print() das ausgeben, was im hintergrund passiert, da du ja noch keinerlei grafische Darstellung hast. 



    Ich mach jetzt erstmal schluss für heute. Kopf raucht grin
    Aber ich hoffe ich konnte einigermaßen den Gedankengang hinter meinen Ansatz erläutern.

    Grüße
    Sebastian

    Kapitán

    2346 Posts

  • #13, z MachtnixSunday, 16. April 2017, 00:03 hodinky 8 years ago
    Wahnsinn. Total geil.
    Ich merke schon: ich bin Grafiker und kein Programmierer. Es ist wie bei den ollen Textaufgaben aus der Schule: ich durchschaue zwar das Problem und weiß, was verlangt ist, kriegs aber nicht in eine Formel extrahiert wink
    Hier weiß ich jetzt nicht, ob es für dich egal ist, welches Feld zufällig kommen soll, oder ob es pro Typ auch noch begrenzte Elemente gibt
    Für mich wäre es egal, aber damit das Spiel lösbar bleibt, muss ich natürlich die Anzahl der Einzelelemente begrenzen. Sonst würden z.B. nur gerade Stücke erzeugt. Ich muss also eine Menge von ungefähr gleich vielen Elementen erzeugen und daraus die Puzzleteile zuordnen. Aber das ist eine Formsache: ich lege eine Menge / Tabelle von - sagen wir mal -, vier Stück Typ 1 an, vier Stück Typ 2, usw. und lasse den Zufallsgenerator aus dieser Menge auswählen (auch hier: ich weiß, was getan werden muss, aber ich weiß nicht, wie...)  roll 
    dann ist natürlich der Weg von Feld 13 nach 12 "-1" bzw. nach 14 "+". Will das Wasser später nach oben oder unten -also auf Feld 08 oder 18, rechne ich einfach "-5" oder "+5".
    Hmm... Ich hatte überlegt, dass ein Feld, das am rechten Rand liegt (z.B. die 10), mich fälschlicherweise mit +1 auf die 11 ganz links lenkt, was natürlich unsinnig ist. Rechts von der 10 ist Niemandsland und deshalb hatte ich am Rand rundum Pseudo-Zahlenfelder eingesetzt. Komme ich also von der 10 mit +1 auf die 11, bin ich außerhalb des Spielfeldes. Ich frage also alle Randfelder mit einer If-Bedingung oder sonstwie ab .

    Die for-Schleife  (die kenne ich ja aus HTML- oder PHP-Tabellen) von 1-25 hatte ich deshalb verworfen. Ich habe 5 diskontinuierliche Zahlenbereiche (9-13, 16-20 usw.).

    Möglich, dass es eleganter zu lösen ist. Aber wenn man die +1/-1 und +5/-5-Konstruktion beibehalten möchte, sehe ich keinen anderen Ausweg.
    Sprich Type einfach nur 1-6, dann brauchst du auch keine select_type() funktion und kannst direkt beim anlegen der Tabelle math.random(1,6) nutzen
    Ja, ich hatte die Typen einfach von 1-6 numeriert. Ich brauche ja keine Bezeichnungen. Das Nord-Süd-Dingsbums war nur für mich.

    Die Vorgehensweise mit der Switch-Funktion habe ich noch nicht ganz verstanden, aber das kommt irgendwann noch. IDs habe ich verworfen, weil ich in Visionaire (soweit ich weiß) keine expliziten IDs zuweisen kann. Das macht Vis von alleine. Am Anfang wird sicher von 1-25 durchnumeriert, solange ich kein anderes Objekt zuvor verwendet habe, doch ich würde mich nicht darauf verlassen, welche IDs die Objekte nun wirklich haben. Bei mir sehen die IDs von 1 bis... meist so aus: 2,6,24,25,87,145,... (grob übertrieben), das heißt, ich würde Namen vergeben. Oder meinst du mit ID etwas anderes?

    Vielen Dank. Da ist doch bereits Licht am Horizont. smile

    Machtnix

    Frohe Ostern!!!

    Kapitán

    1097 Posts

  • #14, z sebastianSunday, 16. April 2017, 00:29 hodinky 8 years ago
    es sind die ids deiner selbst erstellten Lua Tabelle/Arrays. Keine interne VS datenstruktur wird bisbjetzt verwendet. Die Felder heißen hier 1-25 und haben als Inhalt jeweils ein array mit inhalt type, etc. 
    Durch mein tavle. insert habe ich "i" als bezeichner des feldes angelegt. Fängt also bei 1 an und wird durch die do schleife immer +1 gezählt. 

    Die switch Funktion soll nur den inhalt (type) vom angeklickten Element mit dem des Austausch feldest wechseln. Dazu braucht man eine temporäre Variable, die eines der Felder kurz zwischen speichert:

    a=5 
    b=6
    c=' '

    setze c = a

    a=5
    b=6
    c=5

    setze a = b

    a=6
    b=6
    c=5

    setze b=c

    a=6
    b=5
    c=5 

    inhalt von a wurde mit b getauscht
    ------------------
    Dass das Wasser nicht an Randfeldern weiterfliessen kann würde ich später abfragen nachm Motto "ist das Wasser momentan auf Feld 10, Typ des Feldes ist eine s-e kurve und eintritt des wassers ist S, ist der austrittspunkt logischerweise Osten, also theoretisch das Niemandsland" Also +1 verwehren, damit es nicht auf 11 auftauchen kann. 


    Kapitán

    2346 Posts

  • #15, z MachtnixSunday, 16. April 2017, 00:53 hodinky 8 years ago
    Das mit den IDs habe ich mir fast gedacht. Danke.
    Ich war bloß irritiert, weil ich den Begriff für die Visionaire-ID verwende. Die habe ich in machen Spielprojekten nämlich gebraucht.

    Die Austauschfunktion habe ich dann tatsächlich vor einem Jahr auch schon so gelöst. Ich brauchte die zwischengespeicherten Werte außerdem, um beim nächsten Klick die alten Werte "vorrätig" zu haben (oder wenn der Spieler abspeichern möchte), die dann später überschrieben werden. Mit der vereinfachten Frame-Methode muss ich eigentlich gar nichts abspeichern, die Variablen dienen nur als Verschiebebahnhof. Dann war ich ja auf dem richtigen Dampfer.

    Bei mir sah es vor einem Jahr aber noch ziemlich in Kladde aus wink

    Kapitán

    1097 Posts

  • #16, z sebastianSunday, 16. April 2017, 11:09 hodinky 8 years ago
    ist der wasser Eintritts- und Austrittspunkt immer an derselben Stelle oder auch zufällig? 

    Kapitán

    2346 Posts

  • #17, z MachtnixSunday, 16. April 2017, 16:13 hodinky 8 years ago
    Vorerst sind Ein- und Auslass fest. Die auch noch zufällig zu verteilen, war mir zu schwer. 

    Kapitán

    1097 Posts

  • #18, z MachtnixSunday, 16. April 2017, 22:44 hodinky 8 years ago
    Den ganzen Tag habe ich rumprobiert, wie man ein Frame aus einer Animation herauspickt. Mit SetAnimFrames kann ich zwar die Ani aufrufen und Startframe und Endframe definieren, aber was ich auch probiere: die Ani läuft immer durch und bleibt nicht auf einem Frame stehen... Muss ich in jeden Frame eine Aktion setzen? Wie kriege ich ein Standbild aus einem Frame hin?

    Vor einem Jahr habe ich statt einer Animation mit 7 Frames 7 Animationen mit jeweils einem Frame definiert. Um den Puzzletyp zu wechseln, habe ich ganz einfach eine andere Ani aufgerufen. Das hat wenigstens funktioniert, bedeutet aber, 181 Animationen einzupflegen und händisch zu plazieren (!), weil nämlich die Start-Position immer links oben in der Ecke ist und nie da, wo das Objekt liegt... Mit dem Explorer kann man nun  wenigstens den Nullpunkt der Ani festlegen, allerdings dauert das Auffinden und Aufklappen mindestens genauso lange...

    Da fehlen mir zwei Dinge: Position einer Ani numerisch festlegen und die Möglichkeit von Instanzen. Immerhin ist es prinzipiell nur EINE Animation, die ich brauche, und nicht 181 Kopien. Wäre toll, wenn das irgendwann in den nächsten Versionen möglich wäre.

    Kapitán

    1097 Posts

  • #19, z sebastianSunday, 16. April 2017, 23:09 hodinky 8 years ago
    Ich glaube nicht, dass es möglichkeiten geben wird neue Objekte in Visionaire durch scripting oder ähnliches zu kreieren. Das gibt die Engine aus meiner sicht nicht her. Finales Wort hat da natürlich das Dev-Team wink

    Was die Animation angeht um daraus ein Frame zu picken:
     ActiveAnimations["animation_name"].AnimationFirstFrame = 3
     ActiveAnimations["animation_name"].AnimationLastFrame = 3

    würde Frame 3 als Start und End Frame von Animation "animation_name" setzen, sprich diesen Loopen. Animation selbst sollte natürlich "infinite"/"unendlich" sein.

    Die Animation muss zum Zeitpunkt des Setzens der obigen Befehle auch abspielen.

    Kapitán

    2346 Posts

  • #20, z MachtnixSunday, 16. April 2017, 23:43 hodinky 8 years ago
    Genauso habe ich es gemacht, aber es funktioniert nicht. Wahrscheinlich unterläuft mir beim Aufrufen der Ani ein Fehler. Setze ich die Ani aber als Standardanimation ein, läuft die natürlich immer unendlich durch. Egal, ob das Script den Frame auf 1,2 oder sonstwas festlegt. Das Script wird ignoriert. Nehme ich sie als Standardanimation weg, ist gar kein Objekt zu sehen. Setze ich ein Startbild ein (den Deckel), passiert gar nichts, obwohl ich bei "Linksklick" das Script aufrufe. Ich krieg die Logik nicht gebacken... roll
    Vielleicht muss ich ein transparentes Bild drüberlegen, damit das Objekt überhaupt anklickbar wird?

    Kapitán

    1097 Posts