2010-01-18 26 views
6

Ich versuche mich Gedanken über den besten Weg zu verschachtelten Zustand Übergänge in einer einzigen Thread-Programmiersprache (Actionscript) zu implementieren. Nehmen wir an, dass jeder Blattknoten ein Zielpunkt auf einer Website ist, wie ein Bild in einer Galerie, oder ein Kommentar, der in einer in einer Seitenansicht eingebetteten Postansicht verschachtelt ist. .. Und das Ziel ist es, animierte Übergänge vom Blattknoten zum Blattknoten ausführen zu können, indem der vorherige Baum animiert wird (von unten nach oben) und im aktuellen Baum animiert wird (von oben nach unten).Best Practice für den Übergang vom geschachtelten Zustand zum geschachtelten Zustand (siehe Diagramm)

Wenn wir also in den unteren linken meisten Blattknoten waren, und wollten wir unten rechts am weitesten Blattknoten gehen, würden wir müssen:

  • Übergang aus unten links -node
  • auf vollständig (etwa nach einer Sekunde der Animation), Übergang heraus, es ist Eltern,
  • auf vollständige, Übergang aus ist es Eltern
  • auf vollständige, Übergang in der am weitesten rechts stehenden Mutter
  • auf komplett, Übergang in rechts mo st-Kind
  • auf komplett, Übergang in Blatt

Meine Frage ist:

Wenn Sie jeden dieser Knoten als HTML-Ansichten (wo die Blätter sind ‚partials‘, leihen den Begriff von Schienen vorstellen) oder MXML-Ansichten, in denen Sie Unterkomponenten verschachteln, und Sie kennen die Verschachtelungsebenen nicht unbedingt aus dem Anwendungsstamm, was ist der beste Weg, um den Übergang wie oben beschrieben zu animieren?

Eine Möglichkeit besteht darin, alle möglichen Pfade global zu speichern und dann zu sagen: "Anwendung, Übergang aus diesem Pfad, Übergang in diesem Pfad". Das funktioniert, wenn die Anwendung sehr einfach ist. So funktioniert Gaia, ein Actionscript-Framework. Aber wenn Sie es möchten in der Lage sein, beliebig verschachtelt Pfade, um den Übergang in/out, Sie nicht, dass global speichern kann, weil:

  1. Actionscript nicht alles, was die Verarbeitung umgehen konnte
  2. nicht wie eine gute Kapselung scheinen

so kann diese Frage, umformuliert wird, wie Sie das am weitesten links stehenden Blattknoten animieren heraus tun und es Eltern, aus dem Blatt beginnen, und in dem am weitesten rechts-Blattknoten animieren, mit dem Start Wurzel? Wo werden diese Informationen gespeichert (was wird ein- und ausgeschaltet)?

Eine andere mögliche Lösung wäre, einfach zu sagen "Anwendung, vorherigen Knoten untergehen, und wenn das abgeschlossen ist, Übergang im aktuellen untergeordneten Knoten", wobei der "untergeordnete Knoten" das direkte Kind des Anwendungsstamms ist. Dann würde das unterste Kind des Anwendungsstamms (das zwei untergeordnete Knoten hat, die jeweils zwei untergeordnete Knoten haben) prüfen, ob es sich im richtigen Status befindet (wenn es untergeordnete Elemente sind). Wenn nicht, würde sie "transitionOut()" auf ihnen aufrufen ... So wäre alles vollständig gekapselt. Aber es scheint, als wäre das ziemlich prozessorintensiv.

Was denkst du? Hast du noch andere Alternativen?Oder können Sie mir zeigen zu jedem guten Ressourcen auf AI Behavior Trees oder Hierarchical State Machines, die beschreiben, wie sie praktisch asynchronen Zustandsübergänge implementieren:

  • Von wo kommen sie „transitionOut“ auf ein Objekt aufrufen? Von der Wurzel oder einem bestimmten Kind?
  • Wo ist der Status gespeichert? Weltweit, lokal? Was definiert der Bereich, der "transitionIn()" und "transitionOut()" aufruft?

Ich habe/gesehen viele Artikel/Bücher über AI und State Machines lesen, aber ich habe noch etwas zu finden, die beschreiben, wie sie tatsächlich asychronous/animierte Übergänge in einem komplexen MVC Object Oriented Projekt mit 100s von Ansichten/Grafik implementieren Teilnahme am Verhaltensbaum.

Sollte ich die Übergänge vom übergeordneten Objekt oder vom Kind aufrufen?

Hier sind einige der Dinge, die ich untersucht habe:

Dies ist zwar nicht unbedingt ein AI Problem ist, gibt es keine andere Ressourcen beschreiben, wie verschachtelten Zustand Architekturen auf Websites anzuwenden; Das sind die nächsten Dinge.

Eine andere Möglichkeit, die Frage zu formulieren: Wie senden Sie Statusänderungen an die Anwendung? Wo halten Sie die Event-Listener? Wie finden Sie, welche Ansicht animiert werden soll, wenn sie willkürlich verschachtelt ist?

Hinweis: Ich versuche nicht, ein Spiel zu bauen, nur um animierte Websites zu erstellen.

+0

ist die Baumstruktur immer konstant oder könnte sich ändern, abhängig vom Pfad, den der Benutzer genommen hat? – Anurag

+0

Baumstruktur ist immer konstant. aber die Anzahl der Knoten könnte sich ändern (sagen wir, eine Galerie hat 10 Bilder und eine hat 100). im Grunde kennen wir die möglichen Strukturen vorher (welche Eltern können welche Kinder haben), aber wir kennen die Anzahl oder Tiefenstufe nicht unbedingt. –

Antwort

1

Ich werde versuchen, das Problem zu vereinfachen, bevor Sie einen möglichen Ansatz vorschlagen. Die Übergänge scheinen auf die Ansichten und nicht auf das Modell bezogen zu sein. Wenn ich die Übergänge überspringe und direkt zu einem anderen Blattknoten gehe, funktioniert die Anwendung immer noch, aber es gibt keinen visuellen Hinweis für die Benutzer. Daher würde ich vorschlagen, dass Sie einen View-Controller verwenden, der speziell den aktuellen Zweig und die Übergänge verschiedener Ansichten enthält.

Es ist wahrscheinlich ein besserer Ansatz, die beiden Arten von Übergängen in eine Art Stapel zu teilen, bei dem ein Pop-Übergang zu einem vorherigen Knoten zurückkehrt, während ein Push-Übergang in der Hierarchie vorwärts geht. Apple verwendet eine ähnliche Technik zur Verwaltung von Navigationsanwendungen unter Verwendung einer navigation view controller. Es verwaltet im Wesentlichen einen Stapel von View-Controllern, denen der Benutzer folgt, um zu einem bestimmten Knoten zu gelangen. Wenn ein Benutzer zurückgeht, wird das oberste Element vom Stapel entfernt, und der Benutzer sieht das vorherige Element.Wenn der Benutzer tiefer in die Hierarchie einsteigt, wird ein neuer Ansichtscontroller auf den Stapel geschoben.

Sie würden immer noch einen globalen Weg benötigen, um die Hierarchien im Fliegengewicht darzustellen, während der Navigationsstapel nur den aktuell sichtbaren Zweig bis zum Blattknoten speichert.

Wenn ein Benutzer von einem Blattknoten zu einem anderen wechselte, wird der aktuelle Stapel bis zum gemeinsamen übergeordneten Element ausgegeben. Dann wird die globale Baumstruktur aufgefordert, den Pfad von diesem Elternteil zu dem neuen Blattknoten zu erhalten. Dieser Pfad von Knoten wird in den aktuellen Navigationsstapel geschoben, und wenn jedes Element gedrückt wird, wird der Übergang angezeigt.

In einem einfachen Algorithmus, gäbe es diese beiden Datenstrukturen und eine Möglichkeit, die gesamte Branche zu erhalten, den Blattknoten enthält:

  1. eine globale Darstellung des Baumes
  2. ein Stapel der aktuellen Zweig

zunächst:

stack = [] 
tree = create-tree() 

Algorithmus:

// empty current branch upto the common ancestor, root node if nothing else 
until stack.peek() is in leaf-node.ancestors() { 
    stack.pop() // animates the transition-out 
} 
parent = stack.peek(); 
// get a path from the parent to leaf-node 
path = tree.get-path(parent, leaf-node) 
for each node in path { 
    stack.push(node) // animates the transition-in 
} 

aktualisieren:

Die gesamte Anwendung könnte einen einzigen Navigationscontroller oder mehrere solche Controller verfügen. Ein Navigationscontroller muss nur wissen, dass es einen Baum gibt, der bestimmte Operationen freigibt. Sie könnten also eine Schnittstelle mit diesen Operationen erstellen und dies von konkreten Unterklassen implementieren lassen.

kann ich nur denken Sie an zwei Operationen, die (pseudo-Java-Syntax) ausgesetzt werden müssten:

interface Tree { 
    List<Node> getAncestors(node); 
    List<Node> findPath(ancestorNode, descendantNode); 
} 

, dass eine ausreichende Abstraktion liefern, um die Navigationssteuerung Stossen in der globalen Baumstruktur zu halten. Um es auf die nächste Ebene zu bringen, verwenden Sie dependency injection, damit das Tree-Objekt in den Navigationscontroller injiziert wird, wodurch die Testbarkeit verbessert und die Baumverbindung vollständig unterbrochen wird.

+0

Hey Mann, danke für diese tolle Antwort, und das iPhone Link! Das bringt mich viel näher. Daher lautet meine Frage: Wie greifen die Dinge auf diesen globalen Baum zu, damit die Anwendung modular und skalierbar bleibt? –

+0

genial .. ich habe die Antwort mit ein paar mehr Informationen über den globalen Baum aktualisiert. – Anurag

1

Ich rate nur hier, aber Sie können diesen Baum in einem tatsächlichen Baum in Ihrem Code speichern und dann, wenn Sie navigieren möchten, rufen Sie eine Funktion namens findPath (fromNode, toNode) diese Funktion wird den Weg zwischen den beiden Knoten finden, besteht eine Möglichkeit, das zu tun, ist mit diesem Pseudo-Code:

Define array1; 
Define array2; 

loop while flag is false { 
    store fromNode's parent node in array1; 
    store toNode's parent node in array2; 

    loop through array1 { 
     loop through array2 { 
      if array1[i] == array2[j] { 
      flag = true; 
      } 
     } 
    } 
} 

path = array1 + reverse(array2); 

Und es ist dein Weg.

UPDATE:

Eine andere Lösung wäre, jede Seite machen so etwas wie dieses Pseudo-Code haben - ich bin beginnen, um wirklich Pseudo-Code lieben:

function checkChildren(targetNode) { 
    loop through children { 
     if children[i] == target { 
      return path; 
     } 
     if children[i].checkChildren == target node { 
      return path; 
     } 
    } 
} 

Das ist sehr, sehr abstrakt Es hat viel mehr Details und viele Optimierungen, hier ist, was ich im Sinn habe:

  • Übergeben Sie den Pfad hinauf den Baum auf die Funktion s o Der Pfad kann direkt gefunden werden, anstatt zur aufrufenden Funktion zurückzukehren.
  • Übergeben Sie das aktuelle aufrufende untergeordnete Element an die Eltern checkChildren, damit es nicht gestört wird, dieses untergeordnete Element zu überprüfen, damit es schneller überprüft wird.
  • Ich denke immer noch, dass ich meine Idee nicht gut geliefert habe, also werde ich versuchen, es zu erklären.

    Angenommen, Sie gehen von pic2 in der Galerie zu project3 in Projekten.

    pic2 würde seine Kinder überprüfen (falls vorhanden), wenn es das Ziel findet, geht es dort, sonst ruft es seine Eltern (Galerie) 's CheckChildren übergibt sich an die Eltern wird nicht die Mühe zu überprüfen, und übergibt sich an das übergeordnete Element in einem Array, sodass das übergeordnete Element weiß, welcher Pfad verwendet wurde.

    Das übergeordnete Element überprüft seine untergeordneten Elemente, wenn eines der Objekte das Ziel ist, wenn es sich selbst und das untergeordnete Element zum Pfadarray addiert und das ist der Pfad.

    Wenn keines seiner direkten Kinder das Ziel ist, dann ruft es jedes childChildren seiner Kinder auf und fügt sich selbst dem Array des Pfades hinzu, so dass eines seiner Kinder das Ziel selbst und das Kind dem Array hinzufügt und du hast deinen Weg.

    Wenn keiner der untergeordneten Objekte die Zielgalerie aufruft, ruft die übergeordnete Seite (Homepage) die untergeordneten Elemente auf und übergibt sich selbst dem Array des Pfads.

    Die Homepage überprüft alle ihre Kinder außer Galerie mit der gleichen Methode.

    Alternativ können Sie festlegen, dass der Pfad nicht als Array übergeben wird, sondern nur zur übergeordneten Seite wechselt, wenn keines der untergeordneten Elemente oder untergeordneten Elemente übereinstimmt, weil Sie sicher sind, dass sich das Ziel nicht auf dieser Seite befindet.

    Ich hoffe, ich habe mich klar gemacht. Ich kann checkChildren für dich schreiben, wenn du willst.

    +0

    hey, danke. Ich mache jetzt so etwas, wo ich einen zentralen Manager habe, der die Hierarchie des Anzeigeobjekts anhand einer vordefinierten Menge von Pfaden durchläuft, um herauszufinden, welche Objekte aus- und übergehen sollen. Das funktioniert, aber ich ' Ich suche nach etwas Abstraktem, also musste ich diese Pfadinformationen nicht zentral speichern oder berechnen. Oder ist das die bevorzugte Methode? –

    +0

    Ich denke nicht, dass das Speichern der Pfade selbst eine gute Idee ist, denn du wirst mit (n-1) + (n-2) .. 1 Pfaden enden, also für den Baum, den du in der Frage gezeichnet hast, Ich glaube, es ist einfacher, den Computer die Arbeit für Sie erledigen zu lassen;) Wenn Sie möchten, dass Sie alle Pfade abrufen und zwischenspeichern können, benötigt es mehr Ressourcen, aber es wird den Pfad finden wenn es schneller geht .. Machst du das? Alternativ können Sie meine neue Lösung ausprobieren. –

    0

    entweder ich nicht ganz verstehen Sie Problem, oder Sie machen es viel zu kompliziert ... versuchen einfach zu denken, denn im Grunde alles, was Sie tun wollen, ist die folgende:

    1. Empfängt ein Pfad innerhalb eines Baumes
    2. mehrere Animationen sequentiell auszuführen (entlang jenem Pfad)

    keines dieser Dinge zu schwer, viel ist. Für letztere Aufgabe können Sie wahrscheinlich eine der besseren AS3-Animationsbibliotheken verwenden. aber zum Verständnis, lassen Sie mich Ihnen eine leichte Lösung anbieten:

    Ihr Problem ist einfach asynchrone Aufgaben sequentiell durchzuführen. das Abrufen der Sequenz selbst ist nicht wirklich schwierig. Sie sollten jedoch berücksichtigen, dass nicht jeder Pfad durch die Wurzel verläuft (z. B. von einem Blatt zu seinem Nebeneinander). In der Flash-Welt können Callbacks nur asynchron ausgeführt werden. Wenn eine Aufgabe beendet ist, ruft sie zurück und von dort aus entscheiden Sie, was als nächstes zu tun ist.

    kommt so hier einige Code:

    lassen Sie uns zuerst eine Schnittstelle für alle Ihre Blätter/Knoten definieren ... die folgenden sollte es tun:

    package { 
        public interface ITransitionable { 
        //implementors are to call callback when the show/hide transition is complete 
        function show(callback:Function):void; 
        function hide(callback:Function):void; 
        function get parent():ITransitionable; 
        } 
    } 
    

    jetzt die folgende Klasse wird in der Lage sein, Übergänge zu tun, wie Sie auf den Bäumen von ITransitionable angegeben:

    package { 
    public class Transition { 
        ///Array of animation starting functions 
        private var funcs:Array; 
        ///Function to call at the end of the transition 
        private var callback:Function; 
        public function Transition(from:ITransitionable, to:ITransitionable, callback:Function) { 
        this.callback = callback; 
        this.funcs = []; 
        var pFrom:Array = path(from).reverse(); 
        var pTo:Array = path(to).reverse(); 
        while ((pFrom[0] == pTo[0]) && (pTo[0] != null)) {//eliminate common path to root 
        pFrom.shift(); 
        pTo.shift(); 
        } 
        pFrom.reverse();//bring this one back into the right order 
        //fill the function array: 
        var t:ITransitionable; 
        for each (t in pFrom) this.funcs.push(hider(t)); 
        for each (t in pFrom) this.funcs.push(shower(t)); 
        this.next(); 
        } 
        ///cancels the overall transition 
        public function cancel():void { 
        this.funcs = []; 
        } 
        ///creates a function that will start a hiding transition on the given ITransitionable. 
        private function hider(t:ITransitionable):Function { 
        return function():void { 
        t.hide(this.next()); 
        } 
        } 
        ///@see hider 
        private function shower(t:ITransitionable):Function { 
        return function():void { 
        t.show(this.next()); 
        } 
        } 
        ///this function serves as simple callback to start the next animation function 
        private function next(...args):void { 
        if (this.funcs.length > 0) this.funcs.shift()(); 
        else this.callback(); 
        } 
        ///returns the path from a node to the root 
        private function path(node:ITransitionable):Array { 
        var ret:Array = []; 
        while (node != null) { 
        ret.push(node); 
        node = node.parent; 
        } 
        return ret; 
        } 
    } 
    
    } 
    

    es ist keine perfekte Schönheit, aber es sollte Sie in die richtige DIREC genügen, um zu zeigen tion. keine Notwendigkeit für ausgefallene Zustandsmaschinen, was auch immer ... hoffe, das hilft ...