更快地判断精灵是否在另一个精灵附近?

时间:2012-06-22 20:56:50

标签: actionscript-3 flex flex4.5

当我的一个精灵被拖动(四处移动)时,我正在画布上的其他精灵骑行,检查它们是否在范围内,如果它们是,我会在它们上面设置背景光。我现在就是这样做的:

//Sprite is made somewhere else
public var circle:Sprite;

//Array of 25 sprites
public var sprites:Array;

public function init():void {
    circle.addEventListener(MouseEvent.MOUSE_DOWN, startDrag);
}

private function startDrag(event:MouseEvent):void {
    stage.addEventListener(MouseEvent.MOUSE_MOVE, glowNearbySprites);
    stage.addEventListener(MouseEvent.MOUSE_UP, stopDrag);

    circle.startDrag();
}

private function stopDrag(event:MouseEvent):void {
    stage.removeEventListener(MouseEvent.MOUSE_MOVE, glowNearbySprites);
    stage.removeEventListener(MouseEvent.MOUSE_UP, stopDrag);

    circle.stopDrag();
}

private function glowNearbySprites(event:MouseEvent):void {
    for (var i = 0; i < sprites.length; i++) {
        var tSprite = sprites.getItemAt(i) as Sprite;
        if (Math.abs(tSprite.x - circle.x) < 30 && 
                   Math.abs(tSprite.y - circle.y) < 30) { 
            tSprite.filters = [new GlowFilter(0xFFFFFF)];
        }
        else {
            tSprite.filters = null;
        }
    }
}

基本上我每次触发MOUSE_MOVE事件时都会遍历每个精灵。这工作正常,但拖动精灵周围的滞后是非常明显的。有没有办法做到这一点更有效率,没有或更少滞后?

4 个答案:

答案 0 :(得分:3)

嗯,根据你所拥有的精灵量的大小,它可能是微不足道的。但是,如果您正在处理超过1k的精灵 - 使用数据结构来帮助您减少检查量。看看这个QuadTree Demo

基本上你必须为所有精灵创建索引,这样你就不会检查所有精灵。由于阈值为30,当精灵移动时,您可以将其放入int(x / 30),int(y / 30)的行/列索引中。然后,您可以只检查鼠标位置的行/列索引周围9列中存在的精灵。

虽然这看起来更麻烦,但实际上如果你有更多的项目它会更有效率 - 即使你添加更多的精灵,检查的数量也保持一致。通过这种方法,我假设您可以在没有任何打嗝的情况下运行10k精灵。

其他性能优化将是:

  • 使用精灵的矢量/数组而不是getChildAt
  • preincrement i(++ i)
  • 存储一个静态单实例glowfilter,因此它只是一个数组,而是为所有精灵创建一个单独的过滤器。
  • GlowFilter非常占用CPU。可能有意义的是一次性将所有精灵画在一起,然后将GlowFilter一次应用到它 - (这当然取决于你如何设置东西 - 甚至可能比你自己的位图更加麻烦)。
  • 使你的变量声明var sprite:Sprite = ....如果你不是很难输入它,它必须按字符串执行“过滤器”变量查找,而不是通过更快的getlex操作码。

答案 1 :(得分:2)

尝试将其他人的建议编译成基于原始代码的解决方案,到目前为止我只创建了一次GlowFilter并重新使用,其次我改变了循环以使用a而不是迭代基于循环,第三我已更新为使用ENTER_FRAME事件而不是MOUSE_MOVE。到目前为止我唯一被遗漏的东西是我看到的是使用Vector,我的知识几乎没有,所以我不打算建议或尝试直到我做自学。 另一个编辑

刚刚更改了sprite的声明,在这里输入了Vector no code以及它是如何填充的,但是下面的文章说你基本上可以像对待Array一样,因为它实现了所有相同的方法但是你应该注意几个警告,即你不能在Vector中有空白点,所以如果有可能你必须用一个大小声明它。鉴于它知道对象的类型,这可能会获得性能增益,因为它能够在恒定时间内计算数组中任何元素的确切位置(sizeOfObject * index + baseOffset = item of offset)。确切的性能影响并不完全清楚,但如果不是更好的话,这似乎总会导致至少与阵列时间一样好。 http://www.mikechambers.com/blog/2008/08/19/using-vectors-in-actionscript-3-and-flash-player-10/

//Array of 25 sprites
public var sprites:Vector.<Sprite>;
private var theGlowFilterArray:Array;
public function init():void
{
    theGlowFilterArray = [new GlowFilter(0xFFFFFF)];
    circle.addEventListener(MouseEvent.MOUSE_DOWN, startDrag);
}

private function startDrag(event:MouseEvent):void
{
    stage.addEventListener(MouseEvent.MOUSE_UP, stopDrag);
    addEventListener(Event.ENTER_FRAME, glowNearbySprites);

    circle.startDrag();
}

private function stopDrag(event:MouseEvent):void
{
    stage.removeEventListener(MouseEvent.MOUSE_UP, stopDrag);
    removeEventListener(Event.ENTER_FRAME, glowNearbySprites);

    circle.stopDrag();
}

private function glowNearbySprites(event:Event):void
{
    var circleX:Number = circle.x;
    var circleY:Number = circle.y;
    for each(var tSprite:Sprite in sprites) {
        if (Math.abs(tSprite.x - circleX) < 30 && Math.abs(tSprite.y - circleY) < 30)
            tSprite.filters = theGlowFilterArray;
        else 
            tSprite.filters = null;
    }
}

答案 2 :(得分:2)

我将整合The_asMan建议的所有改进。另外,这一行:

tSprite.filters = [new GlowFilter(0xFFFFFF)];

可能非常糟糕,因为你只是一遍又一遍地创建相同的GlowFilter,并且创建新对象总是很昂贵(并且每次mouse_move激发时你都会在for循环中执行此操作!)。而是在创建此类时将其创建一次并将其分配给变量:

var whiteGlow:GlowFilter = new GlowFilter(0xFFFFFF);

...

tSprite.filters = [whiteGlow];

如果您在此之后仍然遇到性能问题,请考虑每次调用glowNearbySprites时仅检查一半(或更少)对象(设置某种类型的标记,让它知道继续在哪里下一个调用(数组的前半部分或后半部分)。您可能不会在视觉上发现任何差异,并且您应该能够将性能提高一倍。

答案 3 :(得分:1)

您的问题是,在每次鼠标更改事件中进行至少线性O(n)的计算效率非常低。

减少计算次数的一个简单启发式方法是保存距离最近的精灵的距离,并且只有在鼠标移动该距离后才会重新计算潜在的崩溃。这可以在恒定时间O(1)中计算。

请注意,这只适用于一次运行一个精灵。