Overlapping hit areas for sibling sprites
I have a recurring problem with buttons. And when I mean buttons I mean anything that has a mouse handler and inherits from InteractiveObject, which includes Sprite and SimpleButton. The problem is when two or more of these objects are siblings (i.e. in the same container, not nested) and they overlap, then they block each other. The top-most one will always receive mouse interaction and anything beneath is blocked.

What I would like is both Sprites to dispatch mouse events when the user interacts. Sometimes you can get round it by nesting buttons and using ROLL_OVER and MOUSE_OVER events, but this isn’t always practical, especially if there are a lot of overlapping buttons. So I’ve come up with another solution.
The idea is to create a ‘fake’ hit area using the Rectangle object. Rectangle has a method called containsPoint, which you pass a Point object and it returns true or false depending on whether the coordinates fall inside the specified dimensions. So if you pass the mouse coordinates you can determine whether the mouse is inside the ‘fake’ hit area. Also, because Rectangle isn’t a display object it can’t block mouse interaction, and so you can have multiple overlapping hit areas that all trigger when clicked.
The containsPoint method has to be continually polled, so it needs to be called everytime the mouse moves. I’ve written some code that wraps this functionality up for ease of use. Take a look at the class below to see how the insides work.
Download: HitArea.as
In the example below I’ve set up two hit areas and overlap them by 100px. The hit areas are invisible, so they won’t show up on the stage, but when you click on the overlapping section the CLICK event will fire twice, once for each hit area. There are also HitArea.ROLL_OVER and HitArea.ROLL_OUT events, which can be useful.
addChild(hitArea1);
hitArea1.addEventListener(HitArea.CLICK, hitAreaClick);
hitArea2 = new HitArea(200, 200);
addChild(hitArea2);
hitArea2.x = 100; // overlap the first hit area by 100 px
hitArea2.addEventListener(HitArea.CLICK, hitAreaClick);
private function hitAreaClick(event:Event):void
{
// will fire twice, even on overlapping area
trace(“click: “ + event.target.name);
}
Unfortunately there are two downsides to this method: firstly it’s slightly more CPU intensive (it needs to constantly run code whenever the mouse moves). Secondly you can’t use the hand cursor with the hit area. This is the most problematic from a user-experience point of view. Unfortunately to get a hand cursor you need to set buttonMode=true on the Sprite, and the moment you do that it turns it into a real button and we come back to the original problem.
Posted: December 1st, 2009 under Actionscript, Flash.