using an array function for click events - actionscript-3

I have created symbols which contain animations (mc1 - mc25). I would like to play these animations if I click on the symbols (click on mc1 -> play mc1, click on mc2 -> play mc2 etc.).
I created an array to address all my symbols in one go. It works ok.
var A:Array = [mc1, mc2, mc3, mc4,...mc25] // create array
car aClip:MovieClip;
for each (aClip in A) // stop all symbols
{aClip.stop();}
How can I get to the result below for all my symbols using an array function?
mc1.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_4);
function fl_MouseClickHandler_4(event:MouseEvent):void
{
mc1.play();
}
I tried something like this but I couldn't get it work:
aClip.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler);
function fl_MouseClickHandler(event:MouseEvent):void {
aClip.play();
}
Thank you!

The simple way about it is to algorithmically figure which one was clicked. The script below is short and does not contain various checks, it assumes all the elements of A are really MovieClips.
// Assume, you filled the Array with these clips already.
var A:Array;
// Iterate all the clips.
for each (var aClip:MovieClip in A)
{
// Subscribe to each of the clips for the CLICK event.
aClip.addEventListener(MouseEvent.CLICK, clickPlay);
}
// Click event handler function. The point is, you can subscribe it
// to the multiple objects and use the function argument to figure out
// which one of them all is actually the source of the event.
// Furthermore, you can subscribe it to handle multiple events too,
// the argument also carries the id of the event. Sometimes it is
// really simpler to compose a single event-processing gate
// rather then process each one event in a separate handler.
function clickPlay(e:MouseEvent):void
{
// Use e.currentTarget because the initial e.target
// could be not the MovieClip itself, but some interactive
// element deep inside the MovieClip's hierarchy.
// Read about bubbling events (MouseEvent is bubbling)
// to get the whole picture of it.
// Typecasting, because e.target and e.currentTarget
// are of base Object type.
var aClip:MovieClip = e.currentTarget as MovieClip;
// Well, the only thing that is left.
aClip.play();
}

Related

AS3: how do i stop two of the same function from playing at once?

i am using AS3 to create a function that will automatically play a movieclip all the way through and then remove it. my project is going to have a lot of animated cutscenes, so id like to be able to call this function, use the cutscene id like as a parameter, and then move on to the next. the problem is, im trying to use the function multiple times in a row to play clips sequentially, but they're all playing at the same time. is there a fix, or a better way to do this altogether?
playClip(new a_walk); //find a way to make these stop playing at the same time
playClip(new a_door);
//a_walk and a_door are the AS linkage class names for the movieclips im referring to
function playClip (clip:MovieClip):void {
addChildAt(clip, 0);
clip.mask = myMask;
clip.x=412.4;
clip.y=244.5;
clip.addEventListener(Event.ENTER_FRAME, checkframes);
function checkframes(event:Event) {
if (clip.currentFrame == clip.totalFrames) {
//trace("wow! youre an idiot!");
if (clip.parent) {
clip.parent.removeChild(clip);
trace (100);
return;
}
}
}
}
Sounds like you want a mechanism to play a queue of MovieClips? If so, here is a way you can accomplish this:
//create an array with the clips you want to play (in order), in my example here, the items can be a MovieClip derived Class, or a MovieClip instance
var playQueue:Array = [a_walk, a_door];
//create a var to store the currently playing clip
var currentClip:MovieClip;
playNext(); //call this when you want the queue of clips to start playing
function playNext():void {
//if there was an item previously playing (currentClip has a value), stop it and remove it/dispose of it
if(currentClip){
currentClip.stop(); //stop it from playing
currentClip.addFrameScript(currentClip.totalFrames-1, null); //remove the frame script that was added
currentClip.parent.removeChild(currentClip); //remove it from the display
currentClip = null;
}
//check if there's anything left to play
if(playQueue.length < 1) return;
var nextItem:* = playQueue.shift(); //shift retrieves and removes the first item in the array;
if(nextItem is Class){
//if it's a class, instantiate it
currentClip = new nextItem();
}else{
currentClip = MovieClip(nextItem);
}
//initialize the movie clip
addChildAt(currentClip, 0);
currentClip.gotoAndPlay(1);
//this is just what you were doing before:
currentClip.mask = myMask;
currentClip.x=412.4;
currentClip.y=244.5;
//add a command on the last frame of the movie clip to play the next item in the queue
currentClip.addFrameScript(currentClip.totalFrames-1, playNext);
//addFrameScript is 0 based, so 0 would refer to the first frame. This is why we subtract 1 to get the last frame
}
I should note, that addFrameScript is an undocumented function. It serves as a nice shortcut so you don't have to have an ENTER_FRAME listener checking currentFrame vs. totalFrames. Being undocumented however, one can not count on it's continued existence in future versions of the Flash/AIR runtimes (though it's been around for a long long time)
note
This answer is a work in progress. I'm waiting on a response from the OP.
// playClip(new a_door); don't call this yet, or they will just both play.
var clipData:CustomClass = new CustomClass(); // add an instance of a custom class to hold the value of the movie
//clip being played (so that you can use this value later in the event handler.)
// it will also hold a value of the next clip
clipData._currentClip = a_walk;
clipData._nextClip = a_door;
playClip(new a_walk);
function playClip (clip:MovieClip):void {
addChildAt(clip, 0);
clip.mask = myMask;
clip.x=412.4;
clip.y=244.5;
clip.addEventListener(Event.ENTER_FRAME, checkframes);
}
function checkframes(event:Event) {
if (clipData._currentClip.currentFrame == clipData._currentClip.totalFrames) {
//trace("wow! youre an idiot!");
if (clipData._currentClip.parent) {
playClip(clipData._nextClip);
clipData._currentClip.parent.removeChild(clipData._currentClip);
clipData._currentClip = clipData._nextClip; // moves the clips down
//clipData._nextClip = here we have
//a problem. Do these clips play in a set
//order, first to last? Or do the play out of
//order jumping back and forth? If so, how
//are you planning on controlling which clip
//plays next?
trace (100);
return;
}
}
}
I haven't checked this in Flash yet to see if it works, but I noticed that you are defining a function inside another function, which I don't think is good practice, so this might clean things up for you. Give it a try and let us know.
I'll try to fix my code above when I get a chance. In the meantime, you answered my question about playing the clips in order, so a simple solution would be to put all the clips in an array and then play them by playClip(clipArray[i]) and then when the clip ends and gets removed, do i++ and call the same function playClip(clipArray[i]) which will play the next clip in the array.

Turn image array images to buttons AS3

I'm creating an app with a search function. I display the images by loading from the array the one's which match the search criteria. All the images are loaded from the library. I want to be able to click on an image as though it were a button. Once I click I want to goto frame 3 and change a variable integer to say which image was clicked on so that I can display the information about the photo in frame 3. Can I do this using an event listener say
imagesArray[i].addEventListener(MouseEvent.CLICK, imageClick);
function imageClick(event:MouseEvent):void
{
gotoAndStop(3);
current = i;
}
or similar,
Thanks
Yes, but it won't be as easy. First, Bitmaps do not process events, so you can't assign a listener directly to a Bitmap object. Next, there is no "i" available in such a construction, you have to determine that "i" by yourself. To do that, you parse event.target property, which is the object that's been clicked. You wrap each Bitmap object into a separate Sprite object, assign listeners to these sprites, then you parse event.target to get the relevant object reference out of it, grab the index via indexOf() call, and assign it to global current variable.
for (i=0;i<imageArray.length;i++) {
var sp:Sprite=new Sprite();
sp.addChild(imageArray[i]);
// position "sp" correctly here
addChild(sp);
sp.addEventListener(MouseEvent.CLICK, imageClick);
}
function imageClick(e:Event):void {
var content=e.target.getChildAt(0); // the object that was wrapped
var i:int=imageArray.indexOf(content);
if (i==-1) return; // OW, out of array
current=i;
gotoAndStop(3);
}
I'm not so sure that setting "current" to "i" would work as the function wouldn't be running at the same time as the for loop.
I'm assuming that because you're using the [i] that you've added the event listeners in a for loop?
What you could do is:
for (var i:int = 0; i < (howeverManyTimesToRun); i++)
{
imagesArray[i].addEventListener(MouseEvent.CLICK, imageClick);
imagesArray[i].myIndex = i;
}
And then in your function (outside of the for loop)
function imageClick (e:MouseEvent) {
gotoAndStop(3);
current = e.currentTarget.myIndex;
}
Also in your for loop you might want to add
imagesArray[i].buttonMode = true;
To change the mouse cursor to a hand when their mouse goes over your image.

How to apply action to mulitple flash layers

I have 5 layers with symbols on each: a, b, c, d and e.
I am trying to work out how to apply the action bellow to a, c, d and e when you hover over b.
Also is there another action similar to ' gotoAndStop(0); ' that instead of going immediately to frame 0 it goes back the way it came?
Link to .Fla http://www.fileden.com/files/2012/11/27/3370853/Untitled-2.fla
stop();
stage.addEventListener(MouseEvent.MOUSE_OVER, playMovie); function playMovie(event) { play(); }
stage.addEventListener(MouseEvent.MOUSE_OUT, stopMovie); function stopMovie(event) { gotoAndStop(0); }
stop();
Thanks
EDIT
After looking at your .fla, here is what is missing/misplaced:
Layers in flash don't mean anything other than z-order/depth. You cannot manipulate a layer in code. All your animations are on the same timeline, so they will always play together. If you want an individual item to animate without the others, you'll have to do the animation on it's own timeline (not just it's only layer). You access your symbols own timeline by double clicking it - do your animation in there.
To reference items that are on the stage, you need to give them an instance name. You do that by clicking on the item that's on the stage, then in properties panel, there is field where you can put in an instance name. For the code below to work, you'd need to give them an instance name of "a","b","c","d","e" respectively. This is different than the symbol name in your library (though it can be the same name).
One way you could do this:
var btns:Vector.<MovieClip> = new Vector.<MovieClip>(); //create an array of all your buttons
btns.push(a,b,c,d,e); //add your buttons to the array
for each(var btn:MovieClip in btns){
btn.addEventListener(MouseEvent.MOUSE_OVER, btnMouseOver); // listen for mouse over on each of the buttons
btn.addEventListener(MouseEvent.MOUSE_OUT, btnMouseOut);
}
function btnMouseOver(e:Event):void {
for each(var btn:MovieClip in btns){ //loop through all your buttons
if(btn != e.currentTarget){ //if the current one in the loop isn't the one that was clicked
btn.play();
try{
btn.removeEventListener(Event.ENTER_FRAME,moveBackwards); //this will stop the backwards animation if running. it's in a try block because it will error if not running
}catch(err:Error){};
}
}
}
function btnMouseOut(e:Event):void {
for each(var btn:MovieClip in btns){ //loop through all your buttons
if(btn != e.currentTarget){ //if the current one in the loop isn't the one that was clicked
goBackwards(btn);
}
}
}
There is no nice way to play a timeline backwards, but there are ways to do it. One such way:
//a function you can call and pass in the item/timeline you want played backwards
function goBackwards(item:MovieClip):void {
item.stop(); //make sure the item isn't playing before starting frame handler below
item.addEventListener(Event.ENTER_FRAME, moveBackwards); //add a frame handler that will run the moveBackwards function once every frame
}
//this function will move something one frame back everytime it's called
function moveBackwards(e:Event):void {
var m:MovieClip = e.currentTarget as MovieClip; //get the movie clip that fired the event
if(m.currentFrame > 1){ //check to see if it's already back to the start
m.prevFrame(); //if not move it one frame back
}else{
m.removeEventListener(Event.ENTER_FRAME,moveBackwards); //if it is (at the start), remove the enter frame listener so this function doesn't run anymore
}
}

How can I recover an object who fires an eventListener event in AS3?

How can I access to an object who fires an eventListener event?
Let's say I have a mc:
var element = new MovieClip();
which has an eventlistener:
element.addEventListener(MouseEvent.CLICK, elementEventHandler);
And then, in the event handler, I want to add something to my mc:
function elementEventHandler(event:MouseEvent):void
{
var b1:balloon = new balloon("ballon1"); //this is another class.
event.target.addChild(b1);//this doesn't work.
}
So that is what I want to achieve... Recover the object who fired the event and then do crazy things with it (in this example, add another object in it).
If anybody has any idea, thanks in advance!
pd: yes, I know I can directly use the var element in this snippet, but in the real code I'm generating the mcs in a loop, according to a xml file.
function elementEventHandler(event:MouseEvent):void
{
// use the as-operator to cast the target into the class you need
var element:DisplayObjectContainer = e.target as DisplayObjectContainer;
// if the cast fails, element will be null, then we bail
if(!element) return;
// then, create your child and add it
var b1:balloon = new balloon("ballon1");
element.addChild(b1);
}
The reason you're getting an error is probably that the event is not coming directly from element but instead from one of its descendant objects.
"click" is a bubbling event.
Check out event flow in the DOM Level 3 Events spec to understand how the capture, target, and bubbling phases work:
http://www.w3.org/TR/DOM-Level-3-Events/#dom-event-architecture
So here's what I would do:
function elementEventHandler(event:MouseEvent):void
{
if (event.target != event.currentTarget)
// If event is not from "element", ignore it.
return;
...
}

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.