Search code examples
actionscriptdispatchevent

Simulating mouse events in Actionscript 3


Given stage coordinates (x,y), I want to make my flash app behave just as if the user clicked at position (x,y). That is, something like

function simulateClick(x:Number, y:Number):void{
    var e:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, false, x, y)
    stage.dispatchEvent(e);
}

I've found a bunch of pages talking about this kind of thing, and they all give solutions similar to the above. However, this isn't equivalent to the user clicking at (x,y). There are two problems:

The first is that e.stageX and e.stageY are both 0. I can't set them directly. The documentation says they are calculated when e.localX and e.localY are set, but this isn't happening when I set e.localX before dispatchEvent, nor in the event listener.

I could rewrite all of my event listeners with something like this:

var p:Point = e.target.localToGlobal(new Point(e.localX, e.localY));

Is this the only option?

The second problem is that my event listeners are registered with children of stage, not stage itself. So I need to find out what target to call dispatchEvent on. Clearly Flash is capable of determining what the target should be, ie which object owns the topmost pixel visible at position (x,y), because it does so when the user actually clicks. Is there an easy way to get at this information, or should I just write my own recursive function to do the same thing? I'm using DisplayObjectContainer.getObjectsUnderPoint at the moment, but it's not quite right.

I'm writing in FlashDevelop, if that makes any difference.


Solution

  • e.stageX/Y is populated correctly for me... also getObjectsUnderPoint() seems to work fine. I'm assuming that the x/y values passed to simulateClick are global coordinates?

    edit: as pointed out in the comments, the mouse event must be dispatched on InteractiveObject instances... modified the code accordingly.

    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.InteractiveObject;
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    
    public function simulateClick(x:Number, y:Number):void
    {
        var objects:Array = stage.getObjectsUnderPoint(new Point(x, y));
    
        var target:DisplayObject;
        while(target = objects.pop())
        {
            if(target is InteractiveObject)
            {
                break;
            }
        }
    
        if(target !== null)
        {
            var local:Point = target.globalToLocal(new Point(x, y));
    
            var e:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, false, local.x, local.y);
            target.dispatchEvent(e);
        }
    }
    
    public function addedToStage():void
    {
        var parent:Sprite = new Sprite();
        stage.addChild(parent);
    
        var child:Sprite = new Sprite();
        child.name = 'child 1';
        child.graphics.beginFill(0xff0000, 1);
        child.graphics.drawRect(0, 0, 200, 200);
        child.graphics.endFill();
    
        var child2:Sprite = new Sprite();
        child2.name = 'child 2';
        child2.graphics.beginFill(0xff00ff, 1);
        child2.graphics.drawRect(0, 0, 100, 100);
        child2.graphics.endFill();
        child2.x = 150;
        child2.y = 150;
    
        var bmpData:BitmapData = new BitmapData(80, 80, false, 0x00ff00);
        var bmp:Bitmap = new Bitmap(bmpData);
        bmp.name = 'bitmap';
    
        child2.addChild(bmp);
    
        parent.addChild(child);
        parent.addChild(child2);
    
        child2.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void
        {
            trace('target: ' + e.target.name);
            trace('localX: ' + e.localX);
            trace('localY: ' + e.localY);
            trace('stageX: ' + e.stageX);
            trace('stageY: ' + e.stageY);
        });
    
        simulateClick(190, 190);
    }
    

    Output:

    target: child 2
    localX: 40
    localY: 40
    stageX: 190
    stageY: 190