I have created a custom Sprite object which is not dispatching the MouseEvent.MOUSE_DOWN or MouseEvent.MOUSE_UP events. It is properly dispatching MouseEvent.MOUSE_MOVE events.
All event listeners are verified to be registered.
I apologize if this is a common or excessively simple question, but I have spent the last hour Googling and reading StackOverflow for questions or answers that fit my situation and simply have not found one. As far as I know, Sprite is an InteractiveObject and therefore should be dispatching all three of these events rather than only the MouseEvent.MOUSE_MOVE events.
Class Declaration:
import flash.display.*;
import flash.events.*;
import flash.geom.*;
public class CustomSprite extends Sprite {
Object Initialization in main.as
var circle:CustomSprite = new CustomSprite();
circle.graphics.lineStyle(3,0x00FF00);
circle.graphics.beginFill(0x0000FF,.5);
circle.graphics.drawCircle(0,0,50);
circle.graphics.endFill();
circle.x = 100;
circle.y = 100;
Event Listener Registration in Constructor:
addEventListener(MouseEvent.MOUSE_DOWN,mouseDownListener);
if (hasEventListener(MouseEvent.MOUSE_DOWN)) trace("MOUSE_DOWN listener exists");
addEventListener(MouseEvent.MOUSE_UP,mouseUpListener);
if (hasEventListener(MouseEvent.MOUSE_UP)) trace("MOUSE_UP listener exists");
addEventListener(MouseEvent.MOUSE_MOVE,mouseMoveListener);
if (hasEventListener(MouseEvent.MOUSE_MOVE)) trace("MOUSE_MOVE listener exists");
Event Listener Registration Output:
MOUSE_DOWN listener exists
MOUSE_UP listener exists
MOUSE_MOVE listener exists
Listener Functions as Part of CustomSprite Class:
private function mouseDownListener(e:MouseEvent):void {
trace("mouseDownListener");
}
private function mouseUpListener(e:MouseEvent):void {
trace("mouseUpListener");
}
private function mouseMoveListener(e:MouseEvent):void {
trace("mouseMoveListener");
}
Output after hovering over object and clicking several times:
mouseMoveListener
mouseMoveListener
mouseMoveListener
mouseMoveListener
Thanks in advance for any help you can offer - it is greatly appreciated!
What is looks like from the source you have provided is that you aren't adding the event listeners to the circle itself. This snippet I tested worked, provided I don't have your CustomSprite, but just the Sprite.
var circle:CustomSprite = new CustomSprite();
circle.graphics.lineStyle(3,0x00FF00);
circle.graphics.beginFill(0x0000FF,.5);
circle.graphics.drawCircle(0,0,50);
circle.graphics.endFill();
circle.x = 100;
circle.y = 100;
circle.addEventListener(MouseEvent.MOUSE_DOWN, function(event:MouseEvent):void{
trace("mouseDown");
});
circle.addEventListener(MouseEvent.MOUSE_UP, function(event:MouseEvent):void{
trace("mouseUp");
});
circle.addEventListener(MouseEvent.MOUSE_MOVE, function(event:MouseEvent):void{
trace("mouseMove");
});
Related
I know the different between event capturing and bubbling and how stopPropagation work.
So why capturing not worked in my simple test? There is no stopPropagation in the code.
import flash.display.Sprite;
import flash.events.MouseEvent;
public class CapturingNotWork extends Sprite {
public function CapturingNotWork() {
var rect:Sprite = new Sprite();
rect.graphics.beginFill(0x000000);
rect.graphics.drawRect(0, 0, 100, 100);
rect.graphics.endFill();
// CAPTURING NOT WORKED
rect.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown, true);
// BUT THE BUBBLING WORKED
// rect.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
stage.addChild(rect);
}
function mouseDown(e:MouseEvent): void {
trace("It's worked");
}
}
The event starts with the topmost parent (stage) and works down the display object hierarchy until reaching the original target.
When the useCapture parameter is set to true, the listener processes the event only during the capture phase and not in the target or bubbling phase. Your event never reaches the target (rect) as you can see on this image:
I know there's a lot of questions on this but I'm really having troubles getting this to work.
I only have in the first frame this code:
var game = new Game(this);
In the game class I have a lot of stuff
package {
import flash.display.*;
import flash.ui.*;
import flash.events.*;
public class Game extends MovieClip {
public function Game(esc) {
var camp = new Camp(); //camp és l'escenari, el conjunt de celles
var player = new Player();
esc.addEventListener(KeyboardEvent.KEY_UP, controlTeclat);
camp.mostraInterficie(esc);
player.situaPlayer(esc);
}
public function controlTeclat(ev){
switch(ev.keyCode){
/*case 37: player.moveLeft();break;
case 38: player.moveUp();break;
case 39: player.moveRight();break;
case 40: player.moveDown();break;
case 32: player.dropBomb();break;*/
}
trace ("hi");
}
}
}
The problem is that the controlaTeclat() function's never called, the trace is no printed. No error displayed, dough.
The mc will need to be on the displayList to receive keyboard events.
var game = new Game(this);
addChild( game );
Without more code it's hard to say exactly what's going wrong here, however if the esc object doesn't have focus (hasn't been clicked by the mouse) then keyboard events won't propagate through it and so the handler won't fire.
You could just add the keyboard listener to the stage itself. You can also set the focus with 'stage.focus' so it will receive events without having to click on the stage first.
stage.addEventListener( KeyboardEvent.KEY_UP, keyupHandler );
//if you want to, you can set focus like this:
stage.focus = stage; //or some other object
private function keyupHandler(e:KeyboardEvent):void
{
trace("keyupHandler()");
}
if (esc.stage) esc.stage.addEventListener(KeyboardEvent.KEY_UP, controlTeclat);
else trace("Stage is inaccessible!");
The best practice is allocating your keyboard listeners to stage, so that they will always react to keyboard events. "esc" is your Document class seemingly, but it's not the stage, so we use "stage" property of "esc" to get access there.
I'm making a game where if the enemy's bullet hits the user, the bullet disappears. Everything works fine except that I keep getting,
'Error #1009: Cannot access a property or method of a null object
reference'
Once the bullet hits the user (bullet disappears though). It confuses me because I've used almost exact the same code in another class where it works perfect.
package classes.enemy
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.display.Stage;
import classes.Main;
public class Bullet extends MovieClip
{
var speed:Number;
public function initialize()
{
var stageReff:Stage = this.stage as Stage;
stage.addEventListener("enterFrame", enterFrame);
}
//code
function enterFrame(e:Event):void
{
this.x += speed;
if(this.hitTestObject(Main.user))
{
removeEventListener("enterFrame", enterFrame);
this.parent.removeChild(this);
// line above gives the error.
}
}
}
}
I have no clue what could be wrong.
Thanks in advance.
My guess is it has to do with the fact that you're adding an enter frame event listener to the stage, yet try to remove it from the listening object itself.
Try changing
var stageReff:Stage = this.stage as Stage;
stage.addEventListener("enterFrame", enterFrame);
to
addEventListener("enterFrame", enterFrame);
you're not removing the event listener from the stage, but the object itself :)
I would not recommend doing it like this, create one listener in your main game class, and call an update function on all objects!
The reason why it fails is because you're removing your Bullet from it's parent. So when you're referencing this.parent it returns null because there simply isn't a parent anymore. You're trying to remove the ENTER_FRAME event but because you set it on the stage the original ENTER_FRAME event keeps running. You can simply fix it like so:
if(this.x > 30)
{
stage.removeEventListener("enterFrame", enterFrame);
this.parent.removeChild(this);
}
But like #RasmusWriedtLarsen already pointed out, it's better to handle these events more globally. And also let the parent handle the removing of the Bullet.
Is there any way to detect if the user click outside a MovieClip?
For instance, I need to detect it to close a previously opened menu (like Menu bar style: File, Edition, Tools, Help, etc).
How can I detect this kind of event? Thanks!
Add a listener to stage and check if stage is the target of the event.
Example of code here:
http://wonderfl.net/c/eFao
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class FlashTest extends Sprite
{
private var _menu : Sprite;
public function FlashTest()
{
_menu = new Sprite();
_menu.x = 100;
_menu.y = 100;
_menu.alpha = 0.5;
with(_menu.graphics)
{
beginFill(0xFF0000, 1);
drawRect(0, 0, 300, 300);
endFill();
}
addChild(_menu);
_menu.addEventListener(MouseEvent.CLICK, onClickHandler);
stage.addEventListener(MouseEvent.CLICK, onClickHandler);
}
private function onClickHandler(event : MouseEvent) : void
{
switch(event.target)
{
case _menu:
_menu.alpha = 0.5;
break;
case stage:
_menu.alpha = 1;
break;
}
}
}
}
You can add a listener to the click event of the root element:
MovieClip(root).addEventListener(MouseEvent.CLICK, clickObject);
then in the function clickObject, you can check to see what you are clicking.
function clickObject(e:Event):void
{
var hoverArray:Array = MovieClip(root).getObjectsUnderPoint(new Point(stage.mouseX, stage.mouseY));
var hoverOverObject:* = hoverArray[hoverArray.length - 1];
}
hoverOverObject references the element that you are clicking on. Often this will be the shape within the movie clip, so you'll need to look at it's parent then compare it to your movie clip. If the click wasn't on the drop down movie clip, trigger the close.
var container:MovieClip = new MovieClip();
var mc:MovieClip = new MovieClip();
with(mc.graphics){
beginFill(0xff0000,1);
drawCircle(0,0,30);
endFill();
}
mc.name = "my_mc";
container.addChild(mc);
addChild(container);
stage.addEventListener(MouseEvent.CLICK, action);
function action (e:MouseEvent):void
{
if(e.target.name != "my_mc"){
if(container.numChildren != 0)
{
container.removeChild(container.getChildByName("my_mc"));
}
}
}
Use capture phase:
button.addEventListener(MouseEvent.CLICK, button_mouseClickHandler);
button.stage.addEventListener(MouseEvent.CLICK, stage_mouseClickHandler, true);
//...
private function button_mouseClickHandler(event:MouseEvent):void
{
trace("Button CLICK");
}
private function stage_mouseClickHandler(event:MouseEvent):void
{
if (event.target == button)
return;
trace("Button CLICK_OUTSIDE");
}
Note that using stopPropagation() is good for one object, but failed for several. This approach works good for me.
Use a stage and a sprite (menu) click listener with the sprite listener executing first and apply the stopPropagation() method to the click handler of the sprite. Like this:
menu.addEventListener(MouseEvent.CLICK, handleMenuClick);
stage.addEventListener(MouseEvent.CLICK, handleStageClick);
private function handleMenuClick(e:MouseEvent):void{
// stop the event from propagating up to the stage
// so handleStageClick is never executed.
e.stopPropagation();
// note that stopPropagation() still allows the event
// to propagate to all children so if there are children
// within the menu overlay that need to respond to click
// events that still works.
}
private function handleStageClick(e:MouseEvent):void{
// put hide or destroy code here
}
The idea is that a mouse click anywhere creates a single MouseEvent.CLICK event that bubbles from the stage, down through all children to the target, then back up through the parents of the target to the stage. Here we interrupt this cycle when the target is the menu overlay by not allowing the event to propagate back up to the parent stage, ensuring that the handleStageClick() method is never invoked. The nice thing about this approach is that it is completely general. The stage can have many children underneath the overlay and the overlay can have its own children that can respond to clicks and it all works.
I'm developing a drag'n'clone feature for my flash app (AS3 and Flash CS5). The clones are created perfectly, but when I try to drag the clone recently created, the app creates a new clone (and allow me to drag it).
I want to remove this behaviour: clone only should be drag and dropped, not cloned. My code is:
public class Car extends MovieClip
{
// imports...
public function Car()
{
addListeners();
}
private function addListeners():void
{
this.addEventListener(MouseEvent.MOUSE_DOWN,clone);
}
private function clone(e:MouseEvent):void
{
// Clone the object
var newcar = new e.target.constructor;
newcar.graphics.copyFrom(this.graphics);
newcar.x = this.x;
newcar.y = this.y;
this.parent.addChild(newcar);
newcar.addEventListener(MouseEvent.MOUSE_MOVE,dragCar);
newcar.addEventListener(MouseEvent.MOUSE_UP,dropCar);
}
private function dragCar(e:MouseEvent):void
{
e.target.startDrag();
}
private function dropCar(e:MouseEvent):void
{
e.target.stopDrag();
// This line doesn't remove the event, and I don't know why
e.currentTarget.removeEventListener(MouseEvent.MOUSE_DOWN,clone);
e.target.removeEventListener(MouseEvent.MOUSE_MOVE, dragCar);
e.target.removeEventListener(MouseEvent.MOUSE_UP,dropCar);
}
}
I hope someone can help me. Thanks!
Here as you are creating a new instance of Car in the clone function, the constructor of that new Car object is called which in turn calls addListeners() and the clone has a MOUSE_DOWN event listener that again clones the clone. That is why you have the problem you describe.
To avoid this you need to add the following line in you clone function (this does not work see below edit)
newcar.removeEventListener(MouseEvent.MOUSE_DOWN,clone);
this removes the clone event listener from the cloned (new) Car instance and avoids cloning again.
Also to start drag you should do it in MOUSE_DOWN instead of MOUSE_MOVE.
Update
OK I see, MOUSE_DOWN is not called Again for the clone, so you need to use MOUSE_MOVE. Thus in this case I would remove the MOUSE_MOVE listener in the listener's body so that it is only called once.
Update
This seems to remove the event listener.
newcar.removeEventListener(MouseEvent.MOUSE_DOWN, newcar.clone);
You have to refer to the newcar instance's clone method newcar.clone to remove the event listener.
Working Code
The following code is working just fine
package{
import flash.display.MovieClip;
import flash.events.*;
public class Car extends MovieClip
{
public function Car()
{
addListeners();
}
private function addListeners():void
{
this.addEventListener(MouseEvent.MOUSE_DOWN,clone);
}
private function clone(e:MouseEvent):void
{
// Clone the object
var newcar = new e.target.constructor;
newcar.graphics.copyFrom(this.graphics);
newcar.x = this.x;
newcar.y = this.y;
this.parent.addChild(newcar);
// remove the clone listener
newcar.removeEventListener(MouseEvent.MOUSE_DOWN, newcar.clone);
// add dragging listeners
newcar.addEventListener(MouseEvent.MOUSE_MOVE, dragCar);
newcar.addEventListener(MouseEvent.MOUSE_UP, dropCar);
// this is used for dragging the clone
newcar.addEventListener(MouseEvent.MOUSE_DOWN, dragCar);
}
private function dragCar(e:MouseEvent):void
{
// if a MOUSE_MOVE event listener is registered remove it
if(e.target.hasEventListener(MouseEvent.MOUSE_MOVE))
e.target.removeEventListener(MouseEvent.MOUSE_MOVE, dragCar);
e.target.startDrag();
}
private function dropCar(e:MouseEvent):void
{
e.target.stopDrag();
// do not remove the listener if you want the clone to be dragable
// e.target.removeEventListener(MouseEvent.MOUSE_UP,dropCar);
}
}
}
Keep clone dragable
For this in the above code I have added a MOUSE_DOWN event listener for dragging the clone
newcar.addEventListener(MouseEvent.MOUSE_DOWN, dragCar);
And commented out the e.target.removeEventListener(MouseEvent.MOUSE_UP,dropCar); in dropCar
Also added a Check before removing MOUSE_MOVE listener as to whether it is there or not, as this function will be called later by MOUSE_DOWN also.