2

スプライトの1つがドラッグ(移動)されているとき、キャンバス上の他のスプライトを循環して、範囲内にあるかどうかを確認し、範囲内にある場合は、背景にグローを設定します。これが私が今それをしている方法です:

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

Well, depending on the size of the amount of sprites you have, it may be trivial. However, if you're dealing with over 1k sprites -- use a data structure to help you reduce the amount of checks. Look at this QuadTree Demo

Basically you have to create indexes for all the sprites, so that you're not checking against ALL of them. Since your threshold is 30, when a sprite moves, you could place it into a row/column index of int(x / 30), int(y / 30). Then you can check just the sprites that exist in 9 columns around the row/column index of the mouse position.

While this would seem more cumbersome, it actually it way more efficient if you have more items -- the number of checks stays consistent even as you add more sprites. With this method I'm assuming you could run 10k sprites without any hiccup.

Other performance optimizations would be:

  • use an vector/array of sprites rather than getChildAt
  • preincrement i (++i)
  • store a static single instance glowfilter, so it's only one array, rather creating a separate filter for all the sprites.
  • GlowFilter is pretty CPU intensive. Might make sense to draw all the sprites together in one shot, and then apply GlowFilter once to it -- (this of course depends on how you have things set up -- might even be more cumbersome to blit your own bitmap).
  • Make your variable declaration var sprite:Sprite = .... If you're not hard typing it, it has to do the "filters" variable lookup by string, and not by the much faster getlex opcode.
于 2012-06-22T23:06:40.017 に答える
2

Attempting to compile the suggestions by others into a solution based on your original code, so far I've created the GlowFilter only once and re-used, secondly I've changed the loop to use a for each instead of the iterant based loop, third I've updated to use ENTER_FRAME event instead of MOUSE_MOVE. The only thing I've left out that's been suggested so far that I see is using a Vector, my knowledge there is pretty much nil so I'm not going to suggest it or attempt until I do some self education. Another Edit

Just changed the declaration of sprites to type Vector no code here for how it's populated but article below says you can basically treat like an Array as it has all the same method implemented but has a couple of caveats you should be aware of, namely that you cannot have empty spots in a Vector and so if that is a possibility you have to declare it with a size. Given it knows the type of the object this probably gets a performance gain from being able to compute the exact position of any element in the array in constant time (sizeOfObject*index + baseOffset = offset of item). The exact performance implications aren't entirely clear but it would seem this will always result in at least as good as Array times if not better. 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;
    }
}
于 2012-06-22T23:07:12.430 に答える
2

I'd incorporate all the improvements that The_asMan suggested. Additionally, this line:

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

is probably really bad, since you're just creating the same GlowFilter over and over again, and creating new objects is always expensive (and you're doing this in a for loop every time a mouse_move fires!). Instead create it once when you create this class and assign it to a variable:

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

...

tSprite.filters = [whiteGlow];

If you're still having performance issues after this, consider only checking half (or even less) of the objects every time you call glowNearbySprites (set some type of flag that will let it know where to continue on the next call (first half of array or second half). You probably won't notice any difference visually, and you should be able to almost double performance.

于 2012-06-22T23:11:06.750 に答える
1

You problem is that making calculations that are at least linear O(n) on every mouse change event is terribly inefficient.

One simple heuristic to bring down the amount of times that you make your calculations is to save the distance to the closest sprite and only after mouse moved that distance would you recalculate the potential crash. This can be calculated in constant time O(1).

Notice that this works only when one sprite moves at a time.

于 2012-06-22T21:25:38.867 に答える