Optimization 从嵌套状态转换到嵌套状态的最佳实践(参见图表)

Optimization 从嵌套状态转换到嵌套状态的最佳实践(参见图表),optimization,actionscript,state-machine,hsm,behavior-tree,Optimization,Actionscript,State Machine,Hsm,Behavior Tree,我正在尝试用单线程编程语言(Actionscript)实现嵌套状态转换的最佳方法。假设我有这样一个行为树结构: 现在,假设每个叶节点都是网站上的目标点,就像图库中的图像,或者页面视图中嵌套的帖子视图中嵌套的注释。。。我们的目标是能够通过设置上一棵树的动画(从下到上)和当前树的动画(从上到下),运行从叶节点到叶节点的动画转换 因此,如果我们位于最左下角的叶节点,并且我们想要转到最右下角的叶节点,我们必须: 转出左下角节点 完成后(比如在动画播放一秒钟后),将其转换为其父对象 完成后,将其转换为父

我正在尝试用单线程编程语言(Actionscript)实现嵌套状态转换的最佳方法。假设我有这样一个行为树结构:

现在,假设每个叶节点都是网站上的目标点,就像图库中的图像,或者页面视图中嵌套的帖子视图中嵌套的注释。。。我们的目标是能够通过设置上一棵树的动画(从下到上)和当前树的动画(从上到下),运行从叶节点到叶节点的动画转换

因此,如果我们位于最左下角的叶节点,并且我们想要转到最右下角的叶节点,我们必须:

  • 转出左下角节点
  • 完成后(比如在动画播放一秒钟后),将其转换为其父对象
  • 完成后,将其转换为父级
  • 完成时,在最右侧的父级中进行转换
  • 完成时,在最右边的子对象中进行转换
  • 关于叶的完全过渡
我的问题是:

如果您将这些节点想象成HTML视图(叶子是“partials”,借用rails的术语),或者MXML视图,嵌套子组件,并且您不一定从应用程序根知道嵌套级别,那么如上所述,设置转换动画的最佳方法是什么

一种方法是全局存储所有可能的路径,然后说“应用程序,转换出这条路径,在这条路径中转换”。如果应用程序非常简单的话,这是可行的。这就是它的工作原理,一个Actionscript框架。但如果您希望它能够转换入/出任意嵌套的路径,则不能全局存储该路径,因为:

  • Actionscript无法处理所有这些处理
  • 似乎不是很好的封装
  • 所以这个问题可以改写为,如何从叶子开始设置最左侧叶节点及其父节点的动画,以及如何从根开始设置最右侧叶节点的动画?信息存储在哪里(输入和输出什么)

    另一种可能的解决方案是只说“应用程序,转换出前一个子节点,完成后,转换到当前子节点”,其中“子节点”是应用程序根的直接子节点。然后,应用程序根的最左边的子节点(它有两个子节点,每个子节点有两个子节点)将检查它是否处于正确的状态(如果它的子节点被“转换出去”)。如果不是,它将对它们调用“transitionOut()”。。。这样,所有内容都将被完全封装。但这似乎是相当处理器密集型的

    你觉得怎么样?你还有其他选择吗?或者,您能给我指出一些关于或分层状态机的好资源,这些资源描述了它们如何实际实现异步状态转换:

    • 在对象上,他们从何处调用“transitionOut”?从根目录还是特定的子目录
    • 状态存储在哪里?全球,本地?定义调用“transitionIn()”和“transitionOut()”的范围是什么
    我已经看过很多关于AI和状态机的文章/书籍,但我还没有找到描述它们如何在一个复杂的MVC面向对象项目中实现非同步/动画转换的内容,该项目有100多个视图/图形参与到行为树中

    我应该从父most对象调用转换,还是从子对象调用转换

    以下是我检查过的一些情况:

    虽然这不一定是一个AI问题,但没有其他资源描述如何将嵌套状态架构应用于网站;这些是最接近的东西

    另一种表达问题的方式是:如何向应用程序广播状态更改?事件侦听器放在哪里?当视图被任意嵌套时,如何找到要设置动画的视图


    注意:我不是在尝试构建游戏,只是在尝试构建动画网站。

    我只是在这里猜测,但您可以将该树存储在代码中的实际树中,然后,当单击要导航时,调用一个名为findPath(fromNode,toNode)的函数,该函数将查找两个节点之间的路径,一种方法是使用以下伪代码:

    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);
    
    function checkChildren(targetNode) {
        loop through children {
            if children[i] == target {
                return path;
            }
            if children[i].checkChildren == target node {
                return path;
            }
        }
    }
    
    这就是你的道路

    更新:

    stack = []
    tree = create-tree()
    
    // 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
    }
    
    另一个解决方案是让每个页面都有类似于此的伪代码——我开始真正喜欢伪代码了:

    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);
    
    function checkChildren(targetNode) {
        loop through children {
            if children[i] == target {
                return path;
            }
            if children[i].checkChildren == target node {
                return path;
            }
        }
    }
    
    这是非常抽象的,它有很多细节和很多优化,以下是我的想法:

    • 将路径向上传递到函数,以便直接找到路径,而不是返回到调用函数
    • 将当前正在调用的子项传递给父项checkChildren,这样它就不会麻烦检查该子项,从而加快检查速度
    我仍然认为我没有很好地表达我的想法,所以我会尽力解释

    假设你要从图库中的pic2转到项目中的project3

    pic2会检查它的子级(如果有),如果它找到了它要去的目标,否则它会调用其父级(gallery)的checkChildren将自己传递给父级不会麻烦检查它,并将自己以数组形式传递给父级,以便父级知道所采用的路径

    父对象检查其子对象是否是目标,如果是,则将自身和子对象添加到路径数组中,这就是路径

    如果它的直接子级中没有一个是目标,那么它会调用每个子级的checkChildren,并将其自身添加到路径的数组中,这样,如果它的任何子级找到目标,它会将其自身和子级添加到数组中,您就有了自己的路径

    如果没有任何子项找到目标库
    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;
      }
     }
    
    }