2

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

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

4 回答 4

3

好吧,根据您拥有的精灵数量的大小,这可能是微不足道的。但是,如果您要处理超过 1k 个精灵——使用数据结构来帮助您减少检查量。看看这个QuadTree 演示

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

虽然这看起来更麻烦,但如果你有更多的项目,它实际上会更有效率——即使你添加更多的精灵,检查的数量也保持一致。使用这种方法,我假设您可以运行 10k 个精灵而不会出现任何问题。

其他性能优化将是:

  • 使用精灵的向量/数组而不是 getChildAt
  • 预增量 i (++i)
  • 存储一个静态单实例发光过滤器,因此它只是一个数组,而不是为所有精灵创建一个单独的过滤器。
  • GlowFilter 是相当 CPU 密集型的。一次将所有精灵绘制在一起,然后对它应用一次 GlowFilter 可能是有意义的——(这当然取决于你如何设置东西——对你自己的位图进行 blit 可能更麻烦)。
  • 使你的变量声明 var sprite:Sprite = .... 如果你不是很难键入它,它必须通过字符串而不是通过更快的 getlex 操作码来进行“过滤器”变量查找。
于 2012-06-22T23:06:40.017 回答
2

尝试根据您的原始代码将其他人的建议编译成解决方案,到目前为止,我只创建了一次 GlowFilter 并重新使用,其次我将循环更改为使用 for each 而不是基于迭代的循环,第三,我已经更新为使用 ENTER_FRAME 事件而不是 MOUSE_MOVE。到目前为止,我看到的唯一建议是使用 Vector,我的知识几乎为零,所以在我进行一些自学之前我不会建议或尝试它。另一个编辑

只是将精灵的声明更改为 Vector 类型,这里没有代码来说明它是如何填充的,但下面的文章说你基本上可以像数组一样对待,因为它实现了所有相同的方法,但有一些你应该注意的警告,即你向量中不能有空点,因此如果有可能,您必须用大小声明它。鉴于它知道对象的类型,这可能会从能够在恒定时间内计算数组中任何元素的确切位置(sizeOfObject*index + baseOffset = item 的偏移量)中获得性能提升。确切的性能影响并不完全清楚,但似乎这总是会导致至少与 Array 时间一样好,如果不是更好的话。 http://www.mikechambers。

//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;
    }
}
于 2012-06-22T23:07:12.430 回答
2

我会合并 The_asMan 建议的所有改进。此外,这一行:

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

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

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

...

tSprite.filters = [whiteGlow];

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

于 2012-06-22T23:11:06.750 回答
1

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

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

请注意,这仅在一个精灵一次移动时才有效。

于 2012-06-22T21:25:38.867 回答