ActionScript 3, handling mouse event cases - actionscript-3

I'm learning ActionScript 3 and at the moment, following a tutorial here http://www.senocular.com/flash/tutorials/as3withmxmlc/ . It describes a simple application which put a ball on the stage and lets the user drag it around. But it has bugs, especially because it doesn't handle the case that the user drags the pointer off the stage. This has gotten me thinking about a better way to handle cases. For starters, I'm considering how to handle MOUSE_UP events. I'd like to write something like this:
public class Test extends Sprite
{
public function Test(stage:Stage)
{
mySprite = SomeSpriteClass()
stage.addEventListener(MouseEvent.MOUSE_UP, handleStageMouseUp);
mySprite.addEventListener(MouseEvent.MOUSE_UP, handleSpriteMouseUp);
}
private function handleStageMouseUp(event:MouseEvent):void {
// how do I determine here if there was also a MOUSE_UP on the Sprite?
// Perhaps I could check the click coordinates of 'event' to see if
// they are superimposed on the Sprite.
}
private function handleSpriteMouseUp(event:MouseEvent):void {
// I don't need this method if I can handle all cases in the
// method above.
}
}
This issue that arises is that with the event model ActionScript3 uses, I don't know how to look for cases that involve combination of events. Alternatively as I wrote in the comment above in handleStageMouseUp() I could check to see if a mouse event occurs over 'mySprite' (how would I do that?)
What I'd really like to do is be able to combine all my case logic in something like this:
private function handleAllCases(...):void {
if (..mouse up on stage but not sprite..) {
.. handle case ..;
} else if ( .. mouse up on both stage and sprite .. ) {
.. handle case .. ;
}
}
Is there a way to do this, or perhaps a better way to think about it?

You check Event.target and Event.currentTarget. target is the object that originally dispatched the event and currentTarget is the latest object to dispatch it (generally the object your listener is attached to).
Quick overview of that. Assume obj2 is wrapped by obj1 which is on the stage. Quick hierarchy:
-Stage
--obj1
---obj2
If you were to trigger an event (that bubbles, that is important as it allows an event to go through its parents until it reaches an event handler. I believe all MouseEvents do this ) in obj2 and had a listener attached to Stage, obj2 would be the target and Stage would be the currentTarget.
So how does this work for you? Well, you just need to check what target and currentTarget are to determine where the event started and where it currently is at.
Plug this into Flash and click on the object and release in various locations and take a look at your console (I did test this):
import flash.display.*;
var mySprite:Sprite = new Sprite();
mySprite.graphics.beginFill(0x000000);
mySprite.graphics.drawRect(0,0,100,100);
mySprite.graphics.endFill();
addChild( mySprite);
stage.addEventListener(MouseEvent.MOUSE_UP, handleStageMouseUp);
function handleStageMouseUp(e:MouseEvent):void {
trace( "handleStageMouseUp - target == stage: " + (e.target == this.stage) );
trace( "handleStageMouseUp - target == mySprite: " + (e.target == mySprite) );
trace( "handleStageMouseUp - currentTarget == stage: " + (e.currentTarget == this.stage) );
trace( "handleStageMouseUp - currentTarget == mySprite: " + (e.currentTarget == mySprite) );
trace( "--------" );
}
For the most part, in your stageUp handler, you can check if e.target == mySprite to determine if it also happened to mySprite.
There is a caveat here, though. If your Sprite has children, one of those children will be the target. However, if you set mySprite.mouseChildren = false, it will not register mouse events on children which means it will behave as it does in the above example.
Hopefully that helps

Related

How to detect what class an instance is from in flash(as3)

I am relatively new to Flash and I am trying to make a little game.
For that I need to detect, if the player clicked on a plane or a bird.
I am spawning them with addChild and the name of each instance is generated.
The eventlistener is attached to the instance.
I tried detecting it like that, but it doesn't seam to work.
It detects the clicking (it prints out the shot: instance but not the trace commands in the if), but not was was clicked on.
function shoot(e: MouseEvent): void {
trace("shot: "+ e.target.name);
if (e.target is Plane) {
trace("shot plane");
e.target.parent.removeChild(e.target);
gotoAndStop(3);
}
if (e.target == Bird) {
trace("shot bird");
score += 1;
e.target.parent.removeChild();
}
}
Does anybody have a tip?
Try using e.currentTarget rather than e.target.
if (e.currentTarget is Plane) {
...
}
if (e.currentTarget is Bird) {
...
}
The current target of an event is a reference to the item you added the event listener to. The target, on the other hand, is the item actually clicked (which could be the same as current target, or a descendant/child object of it)
You can use getQualifiedClassName to check the object type:
trace(flash.utils.getQualifiedClassName(e.currentTarget));

Actionscript 3 - Passing one variable from one object to another in the same project

I'm fairly new at ActionScript 3 but I am working on it and trying to learn by reading and modifying ActionScript source codes.
So far so good, however I stumbled upon one problem which I seemingly can't solve by myself. It should be faily simple for you guys tho.
The situation:
I got one "object" which is clickable and gives a random value which is also being saved in a variable.
I got another "object" which does the same thing but only has a different name.
I want the variable from the first object to be passed to the second one, how can I do it?
One way to pass values relies on using references to objects...
// mc1 and mc2 exist as movieclips on the stage
mc1.addEventListener( MouseEvent.CLICK, onClick );
mc2.addEventListener( MouseEvent.CLICK, onClick );
function onClick( event:MouseEvent ):void
{
// reference clicked movieclip through click target
var mc:MovieClip = event.target as MovieClip;
// if our clip matches one, assign the other clip the value.
if ( mc === mc1 )
{
mc2.value = mc.value;
}
else if ( mc === mc2 )
{
mc1.value = mc.value;
}
}
There's a thousand ways to pass references around, and this is just one.
OK guys let me show you the important parts of the code (cant show it all since I paid for it and I don't know whether the author woudl be OK with me publishing all of it).
I got these two objects there. When I click on one of the objects (which is a DICE), it gives me this code basically
var faceValue:int = 6;
// Add mouse click functionality to roll the die
addEventListener(MouseEvent.CLICK, onClickDie, false, 0, true);
mouseChildren = false;
buttonMode = true;
function onClickDie(e:MouseEvent):void {
removeEventListener(MouseEvent.CLICK, onClickDie);
buttonMode = false;
// Initiate the roll-out sequence
if(faceValue == 6) {
gotoAndPlay("rollout6");
etc...
Then somewhere in the frame where it is SPINNING the dice it is randomizing a number and save it into facevalue
// Calculate a random face value between 1 and 6
faceValue = 1 + Math.floor(Math.random()*6);
// Initiate the roll-in sequence
if(faceValue == 6) {
gotoAndPlay("rollin6");
...etc
Now how can I get the randomized facevalue from the spinning the dice frame to pass it to the other dice?

AS3 MouseEvent and weakReference

Ok, here's a weird thing:
I have a class, which is a MovieClip that has 2 children, MovieClips also.
I add the children to him and base MovieClip to stage.One of the children is animated.
All is perfect.
Now when I add MouseEvent.MOUSE_UP on the children, all works fine.
Yet if I set useWeakReference to true (the 5th parameter) mouse event does not fire anymore,but the items are on stage. Basically, somehow, they are not in the memory.
Of course if I add a simple onEnterFrame that does nothing to base MovieClip, it traces the MovieClip, yet the MouseEvents does not trigger. That means the object is still there, but somehow for flash is not
Now, this is a simplified concept, that is easy to clean, but my code is very big and a simple removeEventListener is not a solution. At least not a simple one.
What are your suggestions to work around this?
I'm not sure how complex your code is, but if each movieclip has MOUSE_UP event handler - some function, you could indeed use removeEventListener MOUSE_UP function. For instance:
var mc:MovieClip = new MovieClip();
mc.addEventListener( MouseEvent.MOUSE_UP, onMU );
function onMU(e:MouseEvent){
var target = MovieClip(e.currentTarget);
target.removeEventListener( MouseEvent.MOUSE_UP, onMU );
}
This way you can have multiple movieclips and remove listeners without knowing object name.
Alternatively you could modify your code to add aray of all added events and then listen to REMOVE_FROM_STAGE event. Something like this:
var mc:MovieClip = new MovieClip();
mc.events = [];
mc.events.push( { evt: MouseEvent.MOUSE_UP, fn: onMU } );
mc.addEventListener( MouseEvent.MOUSE_UP, onMU } )
//or use events array reference to keep events and functions in one place.
//when object is removed you can iterate through events array and automatically remove
//all listeners
Another alternative would be to create Class that extends MovieClip - but since your code is huge you probably don't want to do that.
You can also look on Robert Penner's Signals library, which is interesting alternative to AS3 events. (https://github.com/robertpenner/as3-signals)

Dispatch event on every frame for MovieClip

I have a Movie Clip, and I need to stop the movie when it's reaches a certain frame. To do this, I need to add an event listener that will dispatch when that specific movie clip enters a new frame (as opposed to the whole animation ENTER_FRAME).
bar.addEventListener(Event.ENTER_FRAME, function(e:Event) {
var b:MovieClip = MovieClip(e.currentTarget);
if(b.currentFrame == mp.getPopularity())
b.stop();
});
Any ideas?
Thanks.
Try this;
A simple function:
function playUntil(target:MovieClip, frame:int):void
{
target.stopFrame = frame; // Works because MovieClip is dynamic.
target.addEventListener(Event.ENTER_FRAME, _checkFrame);
}
With the listening function:
function _checkFrame(e:Event):void
{
var m:MovieClip = e.target as MovieClip;
if(m.currentFrame == m.stopFrame)
{
m.stop();
m.removeEventListener(Event.ENTER_FRAME, _checkFrame);
}
}
Apply it to all your instances:
playUntil(instance1, 15);
playUntil(instance2, 27);
playUntil(instance3, 113);
You should remove the event listener when it's no longer needed, otherwise you risk memory leaks. An anonymous function can make this difficult, though you might be able to do it with arguments.callee.
bar.addEventListener(Event.ENTER_FRAME, function(e:Event) {
var b:MovieClip = MovieClip(e.currentTarget);
if(b.currentFrame == mp.getPopularity()){
b.stop();
b.removeEventListener(e.type, arguments.callee);
// ^^ this may work to remove your anonymous listener.
}
});
There's another way to go about this, though. Does mp.getPopularity() change frequently? If it does not change after bar is told to play() then you could use addFrameScript. Just remember that addFrameScript is 0-indexed, so adding a script to frame 1 means you have to pass 0... so if an action is to happen on mp.getPopularity() then you'll have to pass mp.getPopularity() - 1.
var framescript:Function = function():void{
bar.stop();
bar.addFrameScript(bar.currentFrame-1, null);
// ^^ nulling the framescript after
// ^^ it is no longer needed.
}
bar.stop(); // Generally a good idea to call stop before calling addFrameScript
bar.addFrameScript(mp.getPopularity() - 1, framescript);
bar.gotoAndPlay(1); // or wherever it needs to start from.
This is a more precise solution, but you do have to remember to clean up your framescript after you're done, if you plan to use this same bar instance later with a different mp.getPopularity() value.

Emulating Mouse Event AS3

I have a project that involves polling a hardware switch each update and converting its states to a custom events:
ToggleSwitchEvent.BackWardButtonDown
ToggleSwitchEvent.BackWardButtonUp
ToggleSwitchEvent.Click
etc ....
The hardware switch acts similar to a two button mouse. So I would like to be able to "inject" the custom ToggleSwitchEvent events into the Display List and have it act just as a MouseEvent does, bubbling up from the InteractionObject that the click intersects with. This would allow me to addEventListener to the display list just as I do with MouseEvents.
The trouble, as far as I understand it, is that I would need a reference to the leaf nodes of the DisplayList to insert my ToggleSwitchEvent manually. I would like to say that the switch was clicked at a certain position on screen, and have the DisplayList figure out what InteractionObject the click intersects and dispatch my ToggleSwitchEvent instead of a MouseEvent.
Is there a method to emulate the MouseEvent in AS3 but using a custom event?
i do not quite understand ... do you want a custom event to bubble, or to fire MouseEvents on your own?
any event will bubble as expected, if its bubbles property is set to true ...
if you dispatch mouse events manually, they will also bubble, and even their stageX and stageY is calculated appropriately ...
so good luck then ... ;)
edit:
ok, well the display list will not really take care of this for you ... actually, you have two options ... start at the stage, and make your way through it, always keeping under the mouse ... or, start at the object under the mouse, get its path in the tree, and find the right InteractiveObject in its parent chain ... the latter can be done using DisplayObjectContainer::getObjectsUnderPoint ... here's the code ...
public static function dispatchEventUnderMouse(event:Event, stage:Stage, point:Point):Boolean {
//return if stage is disabled
if (!stage.mouseEnabled) return false;
//find the topmost object
var cur:DisplayObject = stage.getObjectsUnderPoint(point).pop();
if (cur == null) cur = stage;
//build the path
var path:Array = [];
while (cur != null) {
path.push(cur);
cur = cur.parent;
}
//traverse the path from the root to find the right InteractiveObject
while (path.length > 0) {
cur = path.pop();
if (cur is InteractiveObject) {
if (!(cur as InteractiveObject).mouseEnabled) {
cur = cur.parent;
break;
}
if (cur is DisplayObjectContainer) {
if (!(cur as DisplayObjectContainer).mouseChildren) break;
}
}
else {
cur = cur.parent;
break;
}
}
return cur.dispatchEvent(event);
}
you might actually want to inject the right coordinates into the event, but i guess you'll figure that out ... ;)