4

I have a DisplayObject docked at the top of my interface that displays debug information (frames per second, etc.) and is translucent with an alpha of 60%.

I would like to interact with items under this surface, such that when the mouse rolls over it, it dims to 10% alpha, and mouse events pass through it to the underlying objects.

Normally, I have this debug info panel's mouseEnabled and mouseChildren properties set to false, so objects under it receive mouse events.

The problem is that in order to hide it when the mouse rolls over it, it needs to have mouseEnabled set to true. However, if mouseEnabled is true, the mouse events are not picked up by objects underneath it.

As far as I know, I can't selectively enable mouseEvents, so it's either going to receive them all or none of them. That means that I'd have to handle and forward ALL events, if I took that approach.

I really wish the mouseEnabled property had a "peek" mode or something, so that it could receive the events if it is on top, but also allow them to pass through to objects underneath.

Triynko
  • 18,766
  • 21
  • 107
  • 173

6 Answers6

1

If a DisplayObject has mouseEnabled=true it means that its events will be sent to its container not to whateve is underneath the object. So this solution will not work. The best solution would be to reroute events from it manually using getObjectsUnderPoint as described here.

I've been using this approach for years in multi-touch apps. With multiple touch points I don't see any processor overhead. And you got only one cursor.

Community
  • 1
  • 1
Valentin Simonov
  • 1,768
  • 10
  • 14
  • 1
    You're describing a slightly different and much simpler problem. Simply identifying the object under any particular point is easy. It's also easy to forward a mouse up/down/click event to any object stacked under a particular point. With a "passthrough" problem however, dealing with even a single point (such as a mouse cursor) is more complex, because mouse-movement-related events must be manually detecting and generated. The over/out/rollover/rollout events involve detecting transitions between hit regions in an unlimited number of sub-layers (at least 1). – Triynko Mar 13 '12 at 15:50
  • Also, there is more logic necessary to locate the actual target from the list of objects returned by getObjectsUnderPoint than one is led to believe. Often, that method returns DisplayObjects rather than InteractiveObjects, such that you must choose its parent. In fact, you may need to choose an ancestor of the node, because you must traverse the parent chain up the tree, all the way to the stage, to identify the highest parent, if any, that has mouseChildren set to false, because the highest parent object to have mouseChildren set to false will become the actual target of any mouse events. – Triynko Mar 13 '12 at 16:05
  • Finally, whatever that final target turns out to be, whether it's a parent, ancestor, or the original object... it must have its mouseEnabled property set to true... otherwise it is fully transparent to the mouse, and the next object in the array returned by getObjectsUnderPoint must be considered. Also with this kind of hierarchy, a static exclusion list must be maintained, because once a layer is processed, it must be excluded from further processing, interference, or interception of events. – Triynko Mar 13 '12 at 16:07
  • Having said all that, I agree that the solution ultimately involves rerouting events from each layer manually using getObjectsUnderPoint as a stepping stone. I'm going to post a full, general solution shortly. – Triynko Mar 13 '12 at 16:09
  • You are mostly right. Sorry I can't show you how I do the same thing. But it's not that complicated if you think about it. Also as I said if you got `mouseEnabled=false` you shouldn't go to the next target in the array. The target should be the parent of the object which got an event. – Valentin Simonov Mar 13 '12 at 16:40
  • Not true. Case in point: create a method that generates a new sprite and draws a solid square background. Call it four times to generate four of these sprites, and put three of them inside the one, so all the graphics overlap. stage.getObjectsUnderPoint will return an array with the objects ordered back to front, starting with the container sprite, then the three children. If you set the last child's (i.e. the foreground sprite's) mouseEnabled property to false, the PARENT DOES NOT become the target.. the next child sprite does. – Triynko Mar 13 '12 at 21:41
  • You're definitely not doing what I'm doing, because what I'm doing is significantly more complicated. First of all, just to drag three sibling sprite objects simultaneously from a single point requires a specialized drag routine, since you can only call startDrag on one object at at time. Implementing that is simple, but that's only a small part. Have two or more overlapping siblings, which you can drag simultaneously from any point where they all overlap, is actually quite complicated when you throw in the need to handle simultaneous mouse over/out events regardless of their depth. – Triynko Mar 13 '12 at 21:45
  • The other important point is that getObjectsUnderPoint doesn't return all possible targets in order. For example, in the previous example, if you drew no graphics in the container sprite, it wouldn't show up in getObjectsUnderPoint... EVEN THOUGH, if you set all it's childObjects' mouseEnabled to false, the container would become the target of a mouse event. getObjectsUnderPoint is actually inadequate for this task as a result. – Triynko Mar 13 '12 at 21:59
  • Yes, you are right. It seems more complicated than I thought. I'll have to rethink my code. – Valentin Simonov Mar 14 '12 at 12:59
0

I had this same problem.. i made function to check is mouse over certain object:

    public function isMouseOverObject(mPos: Point, pObject:DisplayObject, pContainer:DisplayObjectContainer) {

        var under_objects:Array = pContainer.getObjectsUnderPoint(mPos);
        var is_under:Boolean = false;

        for (var i:int = 0; i < under_objects.length; i++) {
            if (under_objects[i] == pObject) {
                is_under = true;
                break;
            }
        }

        return is_under;
    }
Hardy
  • 5,590
  • 2
  • 18
  • 27
0

I believe you are looking for mouseEnabled = false
But another last ditch attempt you can do is on mouse over move it to the other side of the screen.

The_asMan
  • 6,364
  • 4
  • 23
  • 34
  • 2
    mouseEnabled = false would allow events to pass through the parent (possibly to children), and also setting mouseChildren = false would allow events to pass through the object entirely. However, neither of those is what I want. I DO want it to receive the event, so mouseEnabled MUST be true; however, I then also want the event to pass through to the next object that would have otherwise received it (possibly with the option of excluding child objects). – Triynko Mar 09 '12 at 00:01
0

I feel your pain. Unfortunately, I don't know of a way to enable/disable specific mouse events. You could get creative with the solution though. For instance, maybe try adding a MOUSE_MOVE listener to your stage and track the coordinates of the mouse. Then, if the stageX,stageY of the mouse is in the area of your panel, set the visibility. You might also be able to use getObjectsUnderPoint() to determine which objects are under the mouse. But, my guess is that it would get a little intense on the processor to run that on each frame iteration.

Corey
  • 5,818
  • 2
  • 24
  • 37
  • I'm working on a solution now (it's done, I'm testing it), that will call getObjectsUnderPoint, and then forward the event to the appropriate item in the list, depending on a "pass through mode". I'll post it shortly. – Triynko Mar 08 '12 at 23:54
0

One approach you can take, although not ideal, is to add an enter frame listener and check the mouse position every frame. something along the lines of:

stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);


private function onEnterFrame(e:Event):void {
    if(mouseX > width || mouseY > height){
        //hide stats
    }
}
mbaker3
  • 895
  • 6
  • 9
  • The correct way to implement that kind of solution would be to add a MouseEvent.MOUSE_MOVE listener to the top-level container (the stage) in the capture phase, so you would be processing the mouse move event only when it occurs. Processing the mouse position every frame whether it moved or not would be unnecessary and inefficient, but thanks for the suggestion. – Triynko Mar 08 '12 at 23:59
  • While true I've found in some cases MOUSE_MOVE events are not as responsive as checking every frame. I suppose it depends on how accurate you want to be. – mbaker3 Mar 10 '12 at 00:04
0

I'm assuming you have this display hierarchy:

Debug Window
    Debug Control 1
    Debug Control 2
    ...
    Overlay

Why not make the overlay a mask on DebugWindow and have your mouseEvents attached to DebugWindow itself? See this page for some inspiration: http://blog.shaperstudio.com/2010/11/as3-inverse-mask/

Glenn
  • 5,334
  • 4
  • 28
  • 31
  • That actually has nothing to do with this problem. I'm looking for a way to have not the topmost 1, but the topmost X objects under the mouse receive events, where X is going to be 1 + the number of those objects that are allowing mouse passthrough to occur. It's not as simple as you think. I just got the craziest longest single error (1947 lines long) that I've ever seen from AS3 at runtime with my first attempted solution, including stuff like "MERGE FIRST B513" "530:pushbyte -1, 532: jump 570" "VerifyError: Error #1030: Stack depth is unbalanced. 1 != 0." LOL – Triynko Mar 09 '12 at 00:26
  • Maybe there's a misunderstanding, but I'm saying you shouldn't have a "surface" object. Merely create the appearance of a surface by using a mask on the top layer DisplayObject. Then you can interact with the objects there were under the surface normally. When the mouse over/out happens, you just change the opacity of the mask. – Glenn Mar 09 '12 at 00:47
  • Don't get me wrong: I haven't tested my idea either. Might not work. Just throwing out suggestions. – Glenn Mar 09 '12 at 00:49
  • There's definitely a misunderstanding, and that wouldn't work. I am currently changing the opacity of the surface on mouse over/ mouse out. The problem is that in order to do that, you have to actually receive the mouse over, and mouse out events, and only ONE object in the display list can ultimately be the target of such an event. To allow objects under the debug display to receive mouse events, I have to disable mouse events on the overlay, such that it cannot respond to mouse events at all. I actually need to let the debug display receive the event, and then forward it. – Triynko Mar 09 '12 at 00:55
  • Also, apparently that error was coming from invalid ActionScript that the compiler missed. I guess putting a "break;" inside a finally block is a bad idea, oops! 0:) – Triynko Mar 09 '12 at 00:56
  • Maybe this will help then: http://stackoverflow.com/questions/6840691/making-a-movieclip-which-is-under-a-mask-clickable-and-respond-to-mouseevents – Glenn Mar 09 '12 at 01:54
  • I am actually doing something like that. I wrote my own getObjectsUnderPoint, but it's going to actually return interactive objects under point... and not all interactive objects, but the ones that would receive mouse events in the absence of the ones that precede it :). Such a list works well for event passthrough purposes. The method considers hitTestPoint with shapeFlag, mouseEnabled, mouseChildren, and other mechanisms to quickly build the list from the top-down, starting at (but not including) the root (because it seems that the stage is always substituted for root as an event target). – Triynko Mar 14 '12 at 07:02