防止透明Bitmap Sprite的边界框触发鼠标事件

时间:2022-04-18 15:21:09

I thought I had solved my mouseEvent problem for Sprites containing a Bitmap with an alpha channel but I've encountered a new issue shown in the image below: the bounding box of the "Eurasia" map Sprite is triggering a `MouseEvent.Roll_Out" for the "Africa" Sprite.

我以为我已经解决了包含带有alpha通道的Bitmap的Sprites的mouseEvent问题,但我遇到了下图中显示的新问题:“Eurasia”地图的边界框Sprite正在触发`MouseEvent.Roll_Out“ “非洲”雪碧。

防止透明Bitmap Sprite的边界框触发鼠标事件

My setup: Each map piece is a Sprite with a child Bitmap (PNG with alpha) and a "hitArea" Sprite derived from the Bitmap. The relevant code is below. This works great – except in the case where there are bounding box overlaps. The eventListeners I attach to each Sprite use MouseEvent.ROLL_OVER and MouseEvent.ROLL_OUT but I have also tried MouseEvent.MOUSE_OVER and MouseEvent.MOUSE_OUT.

我的设置:每个地图块都是一个带有子位图的Sprite(带有alpha的PNG)和一个从Bitmap派生的“hitArea”Sprite。相关代码如下。这很有效 - 除了有边界框重叠的情况。我附加到每个Sprite的eventListeners使用MouseEvent.ROLL_OVER和MouseEvent.ROLL_OUT,但我也尝试过MouseEvent.MOUSE_OVER和MouseEvent.MOUSE_OUT。

I've tried attaching eventlisteners to the "hitArea" Sprite and various other things but I can't get the bounding box to be ignored. Are there any settings I may have missed – or a workaround?

我已经尝试将eventlisteners附加到“hitArea”Sprite和其他各种东西,但我不能让边界框被忽略。有没有我可能错过的设置 - 或解决方法?


Code:

buttonImage = new Bitmap(upImageData);
buttonImage.smoothing = true;
this.addChild(buttonImage);
hitSprite = createHitArea(upImageData, 4);
this.addChild(hitSprite);
hitSprite.visible = false;
hitSprite.mouseEnabled = false;
this.hitArea = hitSprite;

    public function createHitArea(bitmapData:BitmapData, grainSize:uint=1):Sprite
    {   
        var _hitarea:Sprite = new Sprite();
        _hitarea.graphics.beginFill(0x000000, 1.0);         
        for(var x:uint=0;x<bitmapData.width;x+=grainSize) {
            for(var y:uint=grainSize;y<bitmapData.height;y+=grainSize) {                    
                if(x<=bitmapData.width && y<=bitmapData.height && bitmapData.getPixel(x,y)!=0) {
                    _hitarea.graphics.drawRect(x,y,grainSize,grainSize);
                }                   
            }
        }           
        _hitarea.graphics.endFill();
        _hitarea.cacheAsBitmap = true;
        return _hitarea;
    }

1 个解决方案

#1


1  

If using a vector mask is not a viable option (it should work if you changed your hitSpite into a Shape and then made it the mask of the map piece sprite - also you'd have to add it as a sibling of the map piece and not a child), then the way most people do this is checking if the pixel under the mouse is transparent or not.

如果使用矢量蒙版不是一个可行的选项(如果你将你的hitSpite更改为一个Shape然后使它成为地图片段精灵的面具,那么它应该有效 - 你也必须将它添加为地图片段的兄弟和不是孩子),那么大多数人这样做的方式是检查鼠标下的像素是否透明。

Here is an example:

这是一个例子:

Let's say all your map pieces are the sole children of a Sprite referenced in a var called container. Let's also make the assumption for the example, that your map pieces are all Sprites that have the png Bitmap as the bottom most child.

假设你所有的地图片段都是一个名为容器的var中引用的Sprite的唯一子元素。让我们假设这个例子,你的地图片段都是将png Bitmap作为最底层孩子的Sprite。

You need to add the click listener to the container (instead of each individual map piece):

您需要将click侦听器添加到容器(而不是每个单独的map片段):

container.addEventListener(MouseEvent.CLICK,click);

function click(e:MouseEvent):void {
    var child:Sprite; //a helper var to store the current iterations child below in the while loop
    var target:Sprite; //the item that we determined was clicked

    //iterate through all children of container (backwards, so we process the top most layer first)
    var i:int = container.numChildren;
    while(i--){
        child = container.getChildAt(i) as Sprite; //populate the child var

        //now we check if the mouse is over this child by a simple hit test point
        //we also then check if the pixel under the mouse is transparent (a value of 0)
        if(child.hitTestPoint(e.stageX, e.stageY) && Bitmap(child.getChildAt(0)).bitmapData.getPixel32(child.x + e.localX,child.y + e.localY)){
            target = child;
            break; //break out of the loop since we found a child that meets the criteria
        }
    }

    trace(target);

    //now do something with target
}

#1


1  

If using a vector mask is not a viable option (it should work if you changed your hitSpite into a Shape and then made it the mask of the map piece sprite - also you'd have to add it as a sibling of the map piece and not a child), then the way most people do this is checking if the pixel under the mouse is transparent or not.

如果使用矢量蒙版不是一个可行的选项(如果你将你的hitSpite更改为一个Shape然后使它成为地图片段精灵的面具,那么它应该有效 - 你也必须将它添加为地图片段的兄弟和不是孩子),那么大多数人这样做的方式是检查鼠标下的像素是否透明。

Here is an example:

这是一个例子:

Let's say all your map pieces are the sole children of a Sprite referenced in a var called container. Let's also make the assumption for the example, that your map pieces are all Sprites that have the png Bitmap as the bottom most child.

假设你所有的地图片段都是一个名为容器的var中引用的Sprite的唯一子元素。让我们假设这个例子,你的地图片段都是将png Bitmap作为最底层孩子的Sprite。

You need to add the click listener to the container (instead of each individual map piece):

您需要将click侦听器添加到容器(而不是每个单独的map片段):

container.addEventListener(MouseEvent.CLICK,click);

function click(e:MouseEvent):void {
    var child:Sprite; //a helper var to store the current iterations child below in the while loop
    var target:Sprite; //the item that we determined was clicked

    //iterate through all children of container (backwards, so we process the top most layer first)
    var i:int = container.numChildren;
    while(i--){
        child = container.getChildAt(i) as Sprite; //populate the child var

        //now we check if the mouse is over this child by a simple hit test point
        //we also then check if the pixel under the mouse is transparent (a value of 0)
        if(child.hitTestPoint(e.stageX, e.stageY) && Bitmap(child.getChildAt(0)).bitmapData.getPixel32(child.x + e.localX,child.y + e.localY)){
            target = child;
            break; //break out of the loop since we found a child that meets the criteria
        }
    }

    trace(target);

    //now do something with target
}