Best practices for transitioning from a nested state to a nested state (see Diagram)

I'm trying to think about the best way to implement nested state transitions in a single stream programming language (Actionscript). Let's say I have a structure like this behavior tree: behavior tree

Now imagine that each node sheet is a destination on a website, for example, an image in a gallery, or a comment embedded in a message embedded in a page view ... And the goal is to be able to trigger animated transitions from the node sheet to the node sheet, by animating the previous tree (bottom to top) and animating in the current tree (top to bottom).

So, if we were on the lowest left leaf node, and we wanted to go to the very bottom leaf node, we would have to:

  • transition from bottom-left- node
  • upon completion (say, after the second animation), move its parent,
  • when done go to parent
  • on complete, transition to the rightmost parent
  • upon completion, the transition in the majority-child
  • upon completion, go to sheet

My question is:

If you represent each of these nodes as HTML representations (in which the leaves are "partial", borrowing the term from the rails) or MXML representations where you insert subcomponents and you do not necessarily know the level nest from the application root, what is the best way to animate transition as described above?

One way is to save all possible paths around the world, and then say "Application, transition from this path, transition along this path." This works if the application is very simple. This is how Gaia does it, ActionScript framework. But if you want it to be able to enter / exit arbitrarily nested paths, you cannot store this globally because:

  • ActionScript cannot handle all this processing.
  • Not like good encapsulation.

So, this question can be reformulated as follows: how do you animate the left leaf of the node, and the parents, starting from the leaf, and liven up in the right leaf of the node, starting from the root? Where is this information stored (what is needed to go inside and out)?

Another possible solution would be to simply say “Application, translate the previous child node, and when it is done, jump to the current child node”, where “child node” is the direct descendant of the application root. Then the left-most root of the application root (which has two child nodes, each of which has two child nodes) will check if it is in the correct state (if its children "crossed"). If not, it will call "transitionOut ()" on them ... Thus, everything will be fully encapsulated. But it looks like this would be a pretty intense processor.

What do you think? Do you have other alternatives? Or you can point me to any useful resources on AI behavior trees or hierarchical state machines that describe how they practically implement asynchronous state transitions:

  • Where do they call the "transition" to the object? From a root or a specific child?
  • Where is the state stored? Globally, locally? What is the scope that defines what causes "transitionIn ()" and "transitionOut ()"?

I have seen / read many articles / books about AI and state machines, but I still have to find something that describes how they actually implement asynchronous / animated transitions in a complex object-oriented MVC project with 100 views / graphics, involved in a tree of behavior.

Should I call transitions from the parent itself or from the child?

Here are some of the things I learned:

Although this is not necessarily an AI problem, there are no other resources that describe how to apply nested state architectures to websites; these are the closest things.

Another way to formulate the question is: how do you translate state changes into the application? Where do you keep event listeners? How do you find what kind of animation when it is arbitrarily nested?

Note. I'm not trying to create a game, I'm just trying to create animated websites.

+6
optimization actionscript state-machines hsm
source share
3 answers

I will try to simplify the problem before suggesting a possible approach. Transitions are apparently related to representations, not to the model. If I skipped the transitions and went straight to another node sheet, the application still works, but there is no visual hint for users. Therefore, I suggest you use a view controller specifically to store the current branch and transitions of various types.

This is probably the best approach to split the two types of transitions into multiple stacks, in which the pop transition should return to the previous node, while the current transition goes forward in the hierarchy. Apple uses a similar method to control navigation applications using the navigation view controller . It basically supports the view controller stack, which the user executed to go to a specific node. When the user returns, the top item is removed from the stack and the user sees the previous item. If the user goes deeper into the hierarchy, a new view controller will be pushed onto the stack.

You still need a global way of representing the hierarchy in flies, while only the currently visible branch to the node sheet is saved in the navigation stack.

If the user has moved from one leaf node to another, the current stack will be unloaded to the common parent. Then, the global tree structure will be asked to get the path from this parent to the new node sheet. This node path is inserted into the current navigation stack, and as each item is pressed, a transition is displayed.

In a simple algorithm there would be two of these data structures and a way to get the whole branch containing the node leaf:

  • global tree view
  • stack of current branch

at first:

stack = [] tree = create-tree() 

algorithm:

 // 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 } 

Update

An entire application can have one navigation controller or several such controllers. The navigation controller only needs to know that there is a tree that provides certain operations. This way you can create an interface with these operations and implement specific subclasses.

I can only think of two operations that should be exposed (pseudo-Java syntax):

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

This should provide enough abstraction to support the navigation controller, which digs inside the global tree structure. To go to the next level, use the injection dependency so that the tree object is entered into the navigation controller, improving testability and completely destroying the tree connection.

+1
source share

I just guess, but you can save this tree in the actual tree of your code, and then when you click, you want you to go through a function call that called, say, findPath (fromNode, toNode), that the function will find the path between by two nodes, one way to do this is with this pseudocode:

 Define array1; Define array2; loop while flag is false { store fromNode parent node in array1; store toNode parent node in array2; loop through array1 { loop through array2 { if array1[i] == array2[j] { flag = true; } } } } path = array1 + reverse(array2); 

And there is your way.

UPDATE:

Another solution would be to make each page have something like this pseudo code - I really love the pseudo code:

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

This is very abstract, it has a lot more details and a lot of optimizations, this is what I mean:

  • Pass the path going up the tree to the function, so the path can be found directly, rather than returning to the calling function.
  • Pass the parent checkChildren to the current caller so that he does not check that the child runs the test faster.

I still think that I did not put my idea well, so I will try to explain this.

Suppose you switch from pic2 to the gallery to project3 in projects.

pic2 will check its children (if any) if it finds the target it is heading for, otherwise it will call its parent (gallery) checkChildren passing itself to the parent will not check it and pass itself to the parent element an array so that the parent knew which way was made.

The parent checks its children if any of them is the target, if it adds itself and the child to the array of paths and this path.

If none of his direct children is the target, then he calls each of his checkChildren checks, adding himself to the path array, so that if any of his children finds the target, he adds himself and the child to the array and you have have your way.

If none of the child objects discovers that the target gallery calls the parent object (home page), check that the children walk by themselves and add themselves to the path array.

The home page checks all its children except the gallery using the same method.

Alternatively, you can not skip the path as an array, but instead go to the parent page if none of the child or child children matches, because you are sure that the target is not on this page.

I hope I get it. I can write checkChildren for you if you want.

+1
source share

either I don’t fully understand your problem, or you make it too complicated ... try to think simply, because basically all you want to do is the following:

  • get the path in the tree
  • sequentially perform several animations (along this same path)

None of these things can be heavy. for the last task, you can probably use one of the best AS3 animation libraries. but for the sake of understanding, let me offer you an easy solution:

your problem is simply performing asynchronous tasks sequentially. getting the sequence itself is not very difficult. however, you should be aware that not every path goes through the root (for example, from one leaf to its sibling). in the world of flashes, the only way to perform asynchronous tasks is callback. when one task ends, it calls back, and from there you decide what to do next.

so here comes the code:

first define an interface for all your leaves / nodes ... the following should do the trick:

 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; } } 

Now the following class will be able to perform transitions, as indicated on the ITransitionable trees:

 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; } } } 

This is not perfect beauty, but just point you in the right direction. there is no need for fancy state machines, which is always so ... I hope this helps ...

0
source share

All Articles