Child MovieClip is triggering events with mouseChildren and mouseEnabled set to false - actionscript-3

I have thumbnails loaded into a container. Each thumb has a MOUSE_OVER and _OUT listener. I have each thumbs mouseChildren set to false.
The popup that gets created behind the thumb is triggering the MOUSE_OVER events. I have mouseEnabled set to false on the popup.
I'm not sure why this is happening. I'd appreciate any clues.
for (var i:uint = 0; i < numOfThumbs; i++) {
// add thumb
thumb = new Thumb
thumb.buttonMode = true;
thumb.mouseChildren = false;
container.addChild(thumb);
// add listener
thumb.addEventListener(MouseEvent.MOUSE_OVER, rollOverHandler, false, 0, true);
thumb.addEventListener(MouseEvent.MOUSE_OUT, rollOutHandler, false, 0, true);
// add popup
popup = new Popup;
popup.mouseEnabled = false;
thumb.addChildAt(popup, 0);
}
private function rollOverHandler(e:MouseEvent):void {
// Hey popup.... stop triggering this. I just want the thumb to trigger this. jerk.
}

It's working as expected. Child objects affect the bounds of the parent (which is what is recieving the mouseOver/Out).
Your best bet is to either:
A: Put your popup in a different displayObject container (not as a child of thumb)
B: temporarily disable mouseInput on the Thumb while the popup is visible,
C: on the popup, set mouseEnabled = true (keep mouseChildren=false) and check in your rollOverHandler if the event target (e.target) is the popup and return if true:
if(e.target == popup) return;

Set mouseChildren to false for the main MovieClip.

Related

Actionscript 3.0 - Mouse_OVER, Mouse_OUT to show / hide items...?

Making a interactive tour guide where you mouse over an icon and information pops up (Images, text, etc...) I can get my test image to become visible on MOUSE_OVER but it does not become invisible when I MOUSE_OUT my icon/button.
Here is what I have so far....
sthelens1 is an image
butt1 is my icon for the mouseto mouse over and out
sthelens1.visible = false
butt1.addEventListener(MouseEvent.MOUSE_OVER,showImage);
butt1.addEventListener(MouseEvent.MOUSE_OUT,showImage);
function showImage(MouseEvent){
if(MouseEvent == "MOUSE_OUT"){
sthelens1.visible = false;
}
if(MouseEvent = "MOUSE_OVER"){
sthelens1.visible = true;
}
}
Any guidance or help would be appreciated..
use (e:MouseEvent) and then you can get more info on the e variable.
You can also use a separate listener function on MOUSE_OUT
sthelens1.visible = false
butt1.addEventListener(MouseEvent.MOUSE_OVER, showImage);
butt1.addEventListener(MouseEvent.MOUSE_OUT, hideImage);
function showImage(e:MouseEvent){
sthelens1.visible = false;
}
function hideImage(e:MouseEvent){
sthelens1.visible = true;
}

Double Click - Fullscreen

I'm working on a video player and I though that it could be usefull to include the fullscreen's function when the user double click on the stage.
I've done some research but I'm stuck right now because of this line:
player.display.mouseChildren = false;
I read somewhere that I have to include this before those:
player.display.doubleClickEnabled = true;
player.display.addEventListener(MouseEvent.DOUBLE_CLICK, doubleClickFS, false, 0, true);
However, if mouseChildren is false, the children are not working ^^'.
Do you have an idea to fix this ?
Thank you,
Lea.
Here is a cheat solution.
You just make a full size sprite over all layers, and set it to alpha 0.
Add double click listener to the sprite and set mouseChildren to false, this won't effect your player.
I just screwed up the event flow in AS3. Here is also a cheat solution.
Make your own DOUBLE_CLICK event use CLICK event.
var lastClickTime:Number = 0;
mc.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void
{
var curTime:Number = getTimer();
if( curTime - lastClickTime < 300 )
{
trace("Double Click");
}
else
{
trace("Single Click");
}
lastClickTime = curTime;
});

movie clip on click at mouse coordinates prevent mouse click

I want to add at each click a movie clip (it's smoke, as the pointer is a gun).
I've managed to do that, BUT now I can't clik on anything (buttons,objects..etc). The smoke appears juste fine but seems to "block" the click.
Do you know how I can resolve this issue ?
Here's my code :
private var fumee:MovieClip;
stage.addEventListener(MouseEvent.MOUSE_DOWN, tire, false, 0, true);
public function tire(e:MouseEvent):void{
fumee = new fumeeFusil;
addChild(fumee);
fumee.x = mouseX;
fumee.y = mouseY;
}
Thank you,
You need to make sure your fummeFusil instances are not mouse-accessible:
public function tire(e:MouseEvent):void{
fumee = new fumeeFusil;
// setting both mouseEnabled and mouseChildren to false will ensure
// that you cannot click on the MovieClip
fumee.mouseEnabled = fumee.mouseChildren = false;
addChild(fumee);
fumee.x = mouseX;
fumee.y = mouseY;
}

AS3 - MouseEvent.CLICK fires off for mouse click that happened before the listener was added

I'm new to AS3 and have made a simple "asteroids" game with a game over screen and a resetButton that lets the user play again. When the user clicks on the reset button, the game over screen and the reset button are removed from the stage, and the game proper is added to the stage, along with eventListeners. One of these is a MouseEvent.CLICK listener added to the stage, which calls a fireBullet function. This function draws a bullet and adds it to the stage (other parts of the code then make the bullet move on the screen).
The issue that I am having is that when the user clicks on the reset button, the gameover screen is removed correctly, and the game proper (player, asteroids, eventListeners) are added to the stage correctly, but also at the same time a bullet fires even though the user has not clicked after clicking on the reset button.
My gameOver() function is like this:
stage.removeChild() all objects
stage.removeEventListener() all listeners
null out all objects
draw and add to the stage the game over text and resetButton
addEventListener(MouseEvent.CLICK, onReset) to the resetButton
Then, the onReset() function looks like this:
stage.removeChild() the gameover text and the resetButton
call gameStart();
The gameStart() function looks like this:
initialize variables
draw and add player and asteroids on the screen
add eventListeners including MouseEvent.Click, fireBullet
I've added traces at each function to see what's going on, and this is the flow:
added fireBullet listener //this is gameStart() function being called from Main() and adding everything to the stage the first time
fired bullet //shooting at the asteroids
fired bullet
fired bullet
fireBullet listener should have been removed //this is gameOver() being called that removes everything from the stage and adds the resetButton
clicked on reset
added fireBullet listener //gameStart() being called again from onReset() function
fired bullet //I did not click a second time after clicking on reset
I've read somewhere that events are dispatched all the time regardless if any listeners are actually listening for them, so my suspicion is that my MouseEvent.CLICK listener is picking up the mouse button click from the time when the reset button is clicked, even though this listener is added to the stage afterwards.
I don't have enough experience with AS3 or programming to figure out if this is really the case and what can I do to make sure that the MouseEvent.CLICK listener does not respond to any clicks that happened before it was added to the stage, so any help with this would be greatly appreciated.
====
EDIT
I was assuming I had a logic problem or didn't know something about AS3 and flash, so I just used pseudo code above. Below is a link to the full .as file including the generated .swf
And below that are the relevant functions in full
https://www.dropbox.com/sh/a4rlasq8o0taw82/wP3rB6KPKS
private function startGame():void this is called from Main
{
//initialize variables
bulletArray = [];
cleanupBullets = [];
bulletSpeed = 10;
score = 0;
asteroid1Speed = 0;
asteroid2Speed = 0;
asteroid3Speed = 0;
asteroid4Speed = 0;
//draw player and asteroids
ship = drawPlayer();
asteroid1 = drawAsteroid();
asteroid2 = drawAsteroid();
asteroid3 = drawAsteroid();
asteroid4 = drawAsteroid();
//embarrasing and inefficient code to get random number between -5 and 5 without a 0
asteroid1Speed = Math.ceil(Math.random() * 10 -5);
if (asteroid1Speed == 0)
asteroid1Speed = returnNonZero(asteroid1Speed);
asteroid2Speed = Math.ceil(Math.random() * 10 -5);
if (asteroid2Speed == 0)
asteroid2Speed = returnNonZero(asteroid2Speed);
asteroid3Speed = Math.ceil(Math.random() * 10 -5);
if (asteroid3Speed == 0)
asteroid3Speed = returnNonZero(asteroid3Speed);
asteroid4Speed = Math.ceil(Math.random() * 10 -5);
if (asteroid4Speed == 0)
asteroid4Speed = returnNonZero(asteroid4Speed);
//trace(asteroid1Speed, asteroid2Speed, asteroid3Speed, asteroid4Speed);
//add asteroids to stage
stage.addChild(asteroid1);
stage.addChild(asteroid2);
stage.addChild(asteroid3);
stage.addChild(asteroid4);
//position player and add to stage
ship.x = 40;
ship.y = 40;
stage.addChild(ship);
//add event listeners
stage.addEventListener(Event.ENTER_FRAME, onFrame);
stage.addEventListener(MouseEvent.CLICK, fireBullet);
trace("added fireBullet listener");
}
private function gameOver():void this is called from an onFrame(called every frame) function that I am not including (it's too big and not exactly relevant). it's called when all asteroids are removed.
{
//remove any remaining bullets off the screen
for each (var item:Sprite in bulletArray)
{
stage.removeChild(item);
}
//null out objects and remove listeners
bulletArray = null;
stage.removeEventListener(Event.ENTER_FRAME, onFrame);
stage.removeEventListener(MouseEvent.CLICK, fireBullet);
//check if the listener has actually been removed
if (!(stage.hasEventListener(MouseEvent.CLICK))) {
trace("fireBullet listener should have been removed");
}
stage.removeChild(ship);
ship = null
//graphic for resetButton
resetButton = new Sprite();
resetButton.graphics.beginFill(0xFFFFFF);
resetButton.graphics.drawRect(0, 0, 100, 50);
resetButton.graphics.endFill();
//position for resetButton
resetButton.x = 250;
resetButton.y = 300;
//text for resetButton
resetTextField = new TextField();
var resetTextFormat:TextFormat = new TextFormat();
resetTextFormat.size = 30;
resetTextFormat.color = 0x000000;
resetTextField.defaultTextFormat = resetTextFormat;
resetTextField.selectable = false;
resetTextField.text = "RESET";
resetButton.addChild(resetTextField);
//add resetButton and listener
stage.addChild(resetButton);
resetButton.addEventListener(MouseEvent.CLICK, onReset);
//gameover text
gameOverTxtField = new TextField();
gameOverFormat = new TextFormat();
gameOverFormat.size = 50;
gameOverFormat.color = 0xFFFFFF;
gameOverFormat.align = "center";
gameOverTxtField.defaultTextFormat = gameOverFormat;
gameOverTxtField.selectable = false;
gameOverTxtField.text = "GAME OVER";
gameOverTxtField.width = 660;
gameOverTxtField.height = 200;
gameOverTxtField.x = -10;
gameOverTxtField.y = 20;
stage.addChild(gameOverTxtField);
}
private function onReset(e:MouseEvent):void
{
trace("clicked on reset");
//remove gameover objects and null them
resetButton.removeEventListener(MouseEvent.CLICK, onReset);
stage.removeChild(gameOverTxtField);
stage.removeChild(resetButton);
resetButton = null;
gameOverTxtField = null;
//restart the game
startGame();
}
What's happening is that MouseEvent.CLICK is a bubbling event. In Flash, events have three phases: the "capture phase", the "at target" phase, and "bubbling phase". You can read about it in this Adobe article.
Your reset button's click event handler happens in the "at target" phase. If you trace out the event's phase in the reset button click handler, it will show that event.phase is 2. Per the docs, 1 = "capture phase", 2 = "at target", 3 = "bubbling phase".
After the reset button click handler does its work, the event then bubbles back up through the display list. Since the stage is at the top of the display list, the click event "bubbles up" to the stage. And by that time, you've started the game again and added the stage's click event handler. So the stage's click handler is also triggered.
You can confirm this by tracing out the value of event.phase in your bulletFired() method:
private function fireBullet(e:MouseEvent):void
{
// most of this time it will trace out 2 for the phase
// except when you click on an asteroid when firing or
// click the reset button
trace("fired bullet, event phase: " + e.eventPhase);
bullet = drawBullet();
bullet.y = ship.y;
bullet.x = ship.x + (ship.width / 2);
bulletArray.push(bullet);
stage.addChild(bullet);
}
To fix the problem, you can stop the event from bubbling in your onReset() method:
private function onReset(e:MouseEvent):void
{
// prevent this event from bubbling
e.stopPropagation();
trace("clicked on reset");
//remove gameover objects and null them
resetButton.removeEventListener(MouseEvent.CLICK, onReset);
stage.removeChild(gameOverTxtField);
stage.removeChild(resetButton);
resetButton = null;
gameOverTxtField = null;
//restart the game
startGame();
}
It sounds to me like the previous iteration of your game has not had the MOUSE.CLICK event listener removed. Even if the game is removed, the MOUSE.CLICK event will continue triggering whatever handler you added to it, eg; addEventListener(MOUSE.ClICK, ). When the game is removed you also need to removeEventListener(MOUSE.CLICK, ).

Simulate Mouse Click in AS3

I am working on an AS3 project and I am struggling with one particularly fragile part which will need a lot of refactoring in the near future. Just unit testing separate classes in isolation does not catch all issues we are running into. For example, we might forget to disable mouse events on a transparent overlay and thereby block all clicks on a button. Therefore, I am trying to write a test that simulates real user input.
I have tried to manually send a MouseEvent to the stage at the correct position:
stage.dispatchEvent(new MouseEvent(MouseEvent.CLICK, true, true, 380, 490, stage));
Since the stage has no click event handler, I expected the event to propagate through the hierarchy to the button that will actually handle it (as it does when I physically click the mouse). However, it doesn't.
I know that I could just dispatch the event on the button, but that will not detect if the object is somehow obstructed. Is there some way to simulate mouse events, such that they will properly propagate through the hierarchy?
Edit:
I managed to do it by re-implementing the propagation behavior of Flash:
Edit 2:
My previous solution didn't work if there was a partly transparent overlay with a click handler, like a Sprite with a few Shapes in it. The problem is that the hitTestPoint method returns true even if the object in question is completely transparent at that point. Therefore, I modified it to check the actual pixel value:
private function clickObject(obj:DisplayObject) : void
{
var relPos:Point = new Point(obj.width / 2, obj.height / 2);
var globalPos:Point = obj.localToGlobal(relPos);
simulateClick(obj.stage, globalPos);
}
private function simulateClick(obj:InteractiveObject, globalPos:Point) : Boolean
{
// first, check if we have any children that would rather handle the event
var container:DisplayObjectContainer = obj as DisplayObjectContainer;
if (container != null)
{
if (container.mouseChildren)
{
for (var i:int = 0; i < container.numChildren; ++i)
{
var child:DisplayObject = container.getChildAt(i);
var interactive:InteractiveObject = child as InteractiveObject;
if (interactive != null)
{
if (simulateClick(interactive, globalPos))
{
// if we have found a handler in the children, we are done
return true;
}
}
}
}
}
if (!obj.mouseEnabled) {
return false;
}
if (obj.hitTestPoint(globalPos.x, globalPos.y))
{
var localPos:Point = obj.globalToLocal(globalPos);
// check if object is visible at the clicked location
var pixel:BitmapData = new BitmapData(1, 1);
pixel.draw(obj, new Matrix(1, 0, 0, 1, -localPos.x, -localPos.y));
var color:uint = pixel.getPixel32(0, 0);
if ((pixel.getPixel32(0, 0) & 0xff000000) != 0)
{
// if yes, dispatch the click event
var e:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, true, localPos.x, localPos.y, obj);
obj.dispatchEvent(e);
return true;
}
}
return false;
}
Unfortunately, there is still at least one case not covered: If the object is a mask for another object. I have no idea how to check for this, since it could be mask anywhere in the display hierarchy. I would have to traverse the whole tree and check every single display object to find this out.
So, my question remains: Isn't there an easier way to do this?
I've had issues with events in AS3 as well. I've found that the best way is to have the eventListeners added to the same object that's dispatching the events. In your case, adding the .addEventListener to the stage and sending the function as a function on a child clip. eg:
stage.addEventListener(MouseEvent.CLICK, object.object.clicked);
I hope this may help. I've used this method with success in the past.
You can use stage.getObjectsUnderPoint(new Point(pointerX , pointerY )); function , that will return You array with objects . Than remove overlay object and last instance in array should be deepest DisplayObject.
note: last instance can be graphic or such thing , so You should loop through parent objects to find nearest InteractiveObject .
Also , dont forget that parent objects can have mouseChildren = false or mouseEnabled = false