当我的一个精灵被拖动(四处移动)时,我正在画布上的其他精灵骑行,检查它们是否在范围内,如果它们是,我会在它们上面设置背景光。我现在就是这样做的:
//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事件时都会遍历每个精灵。这工作正常,但拖动精灵周围的滞后是非常明显的。有没有办法做到这一点更有效率,没有或更少滞后?
答案 0 :(得分:3)
嗯,根据你所拥有的精灵量的大小,它可能是微不足道的。但是,如果您正在处理超过1k的精灵 - 使用数据结构来帮助您减少检查量。看看这个QuadTree Demo
基本上你必须为所有精灵创建索引,这样你就不会检查所有精灵。由于阈值为30,当精灵移动时,您可以将其放入int(x / 30),int(y / 30)的行/列索引中。然后,您可以只检查鼠标位置的行/列索引周围9列中存在的精灵。
虽然这看起来更麻烦,但实际上如果你有更多的项目它会更有效率 - 即使你添加更多的精灵,检查的数量也保持一致。通过这种方法,我假设您可以在没有任何打嗝的情况下运行10k精灵。
其他性能优化将是:
答案 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)中计算。
请注意,这只适用于一次运行一个精灵。