Putting buttons in an array is such a cool technique, that I can kind of guarantee that once you get to using it, you'll want to use it for everything!
Let's start with a simple example so that I can really clue you in to just how useful this is. Suppose that you have a fla file for a website. In your fla file, you've created five buttons. Each button is actually a MovieClip instance. Whether they are all instances of the same MovieClip symbol is irrelevant. What's relevant is that they have all been created in the authoring tool, and that they all exist on the stage. If the code is going to be on frame 1 (we'll assume it is), then the buttons should all exist on frame 1 too, otherwise you will get null object reference errors.
Anyway, let's say that your buttons are named:
The point is that the names could be anything at all (they don't have to be numbered in sequence like the former btn0, btn1, btn2, etc). We will put them in an array, and after we do, it will be easy to treat them as a group instead of a bunch of unrelated buttons. So let's create an array and add our buttons to the array:
var clipArray:Array = [home_mc, about_mc, products_mc, services_mc, contact_mc];
One mistake beginning programmers make is to assume that arrays can only store string values (no offense, I used to think of arrays that way, too!). So they proceed to give all their MC buttons names so that they can store the names in the array, then they use perhaps getChildByName("theName") to get references to their buttons. But arrays can store any and all kinds of values. So why not store the MovieClip instances themselves, rather than string values? In fact, I don't so much think of the array as "storing" the values--rather I think of it as giving all the values a new set of names. Here's what I mean. After that above line of code is run:
So, I want you to get this mental picture: putting all of these buttons into an array didn't cause them to get scooped up and placed somewhere else, as if they had moved or something. Let's take home_mc for example. I can still use the name home_mc even after I have put home_mc in the array:
home_mc.addEventListener(MouseEvent.CLICK, clickHandler); function clickHandler(event:MouseEvent):void { this.gotoAndStop("home"); }
But now I can also use home_mc's new alternate name to do the same thing:
clipArray[0].addEventListener(MouseEvent.CLICK, clickHandler); function clickHandler(event:MouseEvent):void { this.gotoAndStop("home"); }
This is the part I want you to get: clipArray[0], for all practical purposes, is home_mc. Everything that you could do with home_mc, using all of the properties and methods of MovieClips, you can now also do using clipArray[0] instead! So to move home_mc twenty pixels further down, you can do this:
clipArray[0].y += 20;
I could go on and on with more examples of properties and methods you can use (the whole list of MovieClip properties and methods!), but I think by now you get the central idea. Each mc button can now be programmed using the array name and index number instead.
Where this really comes in handy is when you combine arrays and loops. Arrays have a length property that you can use to know how many times to loop. This makes for dynamic code that need only be written once. For example:
var clipArray:Array = [home_mc, about_mc, products_mc, services_mc, contact_mc]; for(var i:int = 0; i < clipArray.length; i++) { clipArray[i].buttonMode = true; clipArray[i].addEventListener(MouseEvent.CLICK, clickHandler); } function clickHandler(event:MouseEvent):void { trace("you clicked " + event.target.name); }
In the above code, the loop is run five times. Each time through the loop, the loop counter "i" is incremented by one. So the first time through the loop, we are dealing with clipArray[0], which corresponds to home_mc. So home_mc's buttonMode property is set to true, giving it a hand cursor, and an event listener for a mouse click (clickHander) is added to home_mc. The next time through the loop, i will equal 1, and the same actions will be performed for about_mc. And so on for the rest of the loop, until all of the buttons have been programmed in the same way.
Suppose that later on, you add another mc button called links_mc. Now all you would have to do is add links_mc to the end of the array. There would be no need to change the for loop at all. Since the loop is based on the array's length, it will now automatically include links_mc, which will get the same programming as all the other buttons (but we won't add links_mc right now).
I am aware that the same event listener has been added to all of the buttons, and you may be wondering how to make individualized actions happen inside the listener, based on which button was clicked. One answer is to either use if statements, or a switch statement, that compares each button to event.currentTarget (you could also use event.target, if you are sure that your buttons don't contain other display objects, but currentTarget will always be safe in this case, because the event listener was added to the button itself. See my event flow tutorial for details):
var clipArray:Array = [home_mc, about_mc, products_mc, services_mc, contact_mc]; for (var i:int = 0; i < clipArray.length; i++) { clipArray[i].buttonMode = true; clipArray[i].addEventListener(MouseEvent.CLICK, clickHandler); } function clickHandler(event:MouseEvent):void { switch (event.currentTarget) { case home_mc : gotoAndStop("home"); break; case about_mc : gotoAndStop("about"); break; case products_mc : gotoAndStop("products"); break; case services_mc : gotoAndStop("services"); break; case contact_mc : gotoAndStop("contact"); break; } }
Notice that this switch statement uses the actual objects themselves, and compares them to event.currentTarget. I realize that you could also use strings, and compare them to event.currentTarget.name. Either way is fine, it's just that this way seems more direct and intuitive to me.
Still, this code is more verbose than it needs to be. Notice that there are two sets of information: the buttons themselves, and the destination frame on the main timeline that each button is intended to take you to. The buttons are already in an array, so why not put the destination frames in an array also? This technique is called using parallel arrays:
stop(); var clipArray:Array = [home_mc, about_mc, products_mc, services_mc, contact_mc]; var destArray:Array = ["home", "about", "products", "services", "contact"]; for (var i:int = 0; i < clipArray.length; i++) { clipArray[i].buttonMode = true; clipArray[i].addEventListener(MouseEvent.CLICK, clickHandler); } function clickHandler(event:MouseEvent):void { for (var i:int = 0; i < clipArray.length; i++) { if (event.currentTarget == clipArray[i]) { this.gotoAndStop(destArray[i]); } } }
Notice that each element of the first array has a corresponding, or parallel element in the other array. Now, when you click, there is another for loop inside the clickHandler that cycles through the clipArray and compares each button to the event.currentTarget. When it finds a match, it has effectively figured out which button was clicked. At that point, while we still have the proper value for "i," we can tell the main timeline to gotoAndStop() at the frame that's labelled with the value from the corresponding array.
Here's this simple website example, followed by a link for you to download the fla file:
Although the website example is simple, it makes a good illustration of the techniques outlined. So even if your buttons don't belong to a website application, the principles are the same. On the next page we'll explore how to give your buttons "disabling" behavior, using the same website example.
Cool indeed
... just what I was looking for. Very efficient when working with many movie clips.
Nice one!
I'm so glad I found this
I am new at flash but have a pretty good understanding of the logic behind this. I knew linking arrays had to be possible but I couldn't find how to do it anywhere. After days of searching, here it is. Thank you!
I'm trying to link btns in my array to pics & captions in an XML list. Any suggestions on how to adapt this?
*EDIT*
Nevermind, figured it out. Your code is so clean it was easy to write my own additions & adapt it to my needs. Awesome.
www.PulseStudios.com
this is cool but I need help
so I have completed an array and it runs as I want:
I have a map of the united states, on it are several dots(buttons) these are listed in my array. When the button is clicked on a movie clip plays revealing information and hiding all the other dots. I want to be able to place a button in the movie clip information to return to the dots on the maps, but I keep getting stuck. any suggestions?
here is my code./////
///////
var myMenuArray = [alaska, washington, california, hawaii, louisiana, alabama, florida, virginia, maine, alaskaUF, floridaUF, michiganUF, nhUF, nyUF, oregonUF, texasUF, virginiaUF, ncUF];
for each (var btn in myMenuArray) {
btn.addEventListener(MouseEvent.CLICK, onBtnClick);
}
function onBtnClick (event:MouseEvent):void {
pages.gotoAndStop(event.target.name);
}
///
/////where would I put new code? How do I get the button in the movieclip to reverse itself.
I am a complete newbie trying to teach myself!! Any help would be much appreciated!!
buttons in Array
I like your method but I was woundering, is there a way that you can add a child and open an external swf, and to remove child when button is clicked?
I keep getting errors with this method!!
Thanks for the tutorial...very straightforward and did exactly what I expected it to do....
But I still have a major prolem - it doesn't work for me!
I am not using any script within the FLA, mine is all external within a Document.as file that extends the movie clip (being the swf the FLA exports)
Every time I do the loop I keep getting the movie clip names as they appear in the library with _8 or _13 next to them (I'm running a trace because if I do anything else it throws up errors), I know these are references to the objects themselves. I can get the instance name by adding ".name" like such:
trace(myArray[i].name);
Is there something I'm not doing here?? I'm declaring the array as a public static variable up the top of my class
"public class Document extends MovieClip{declaring my array in here}"
I call up the for loop and add event listeners to the buttons in the initialise function - I keep getting errors no matter what I do and it's really bugging me!
Here is my full code below, please forgive it's messy state...
package com.eq.rarc
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
import flash.events.MouseEvent;
import flash.text.*;
import fl.transitions.Tween;
import fl.transitions.TweenEvent;
import fl.transitions.easing.*;
import flash.display.SimpleButton;
import flash.display.Sprite;
public class Document extends MovieClip
{
public var controlBtns:Array = [submit_btn,zoomMinus,zoomPlus];
public function Document()
{
addEventListener(Event.ADDED_TO_STAGE, init, false, 0, true);
}
public function init(e:Event):void
{
//createSlideNum();
// initiateSlider();
initBtns();
}
public function initBtns():void
{
for(var i:int = 0; i < controlBtns.length; i++)
{
trace(controlBtns[i]);
controlBtns[i].addEventListener(MouseEvent.ROLL_OVER, function(){controlBtns[i].nextFrame()});
controlBtns[i].addEventListener(MouseEvent.ROLL_OUT, function(){controlBtns[i].prevFrame});
controlBtns[i].mouseChildren = true;
controlBtns[i].buttonMode = true;
}
}
}
}
I've edited the code a bit to exclude a bunch of other functions that were running on the script.
It's all good, found my
It's all good, found my solution. Using inline/anonymous functions was not a good idea!
I just got them shown to me by my senior developer as a way of writing neater code for one operation functions....well now I know.
This tutorial has helped me
This tutorial has helped me so much,
but how would i go to the scene name, not the frame???????
function autoGoTohandle(event:MouseEvent):void
{
for (var i:int = 0; i < arrayItem.length; i++) {
if (event.currentTarget == arrayItem[i]) {
gotoAndStop(destArray[i]);
}
}
i know its something to do with the quotation marks missing from the gotoAndStop(destArray[i]); but im unsure how to put them there
ah done it!!! was missing
ah done it!!! was missing "1,"
Nice work!
nolimit (I like that username),
I appreciate your comment! Many thanks. I am also glad that you seem to have solved your own problem! Nice work!
great code, helped me allot
great code, helped me allot
Dynamically assigning instance names
Hi Jody,
I used your technique here to avoid redundant code. My problem is I can quite figure out how to dynamically put an instance name into the array. The following code works fine to position the instance name of bases345:
var clipArray:Array = [bases345];
clipArray[0].x += 200;
clipArray[0].y += 200;
However, I can't figure out how to call a variable to form the instance inside the array. This returns an array about not being able to assign property X to a string, which makes sense, because i just made an array that has a string in it, not an instance.
var i:String = "123456";
var clipArray:Array = ["bases"+i.substr(2,3)];
clipArray[0].x += 200;
clipArray[0].y += 200;
Any help is appreciated.
Thanks, Chi
The nature of variables and "instance names"
Chi,
The Flash IDE (that is, the Flash authoring tool) can give programmers the false impression that there is a "one object/one instance name" relationship when there is not. An instance name that's been set in the properties panel is converted to a variable by Flash "behind the scenes."
A string is just a string, yes. But a variable name is different than an ordinary string. When you use an instance name that you've set in the properties panel, you are still not using an ordinary string. Flash has taken the string of text you typed in, and made a variable out of it for you. The variable serves as a "pointer" to the actual object. But there can be any number of these "pointers," not just one.
To answer your question, here's how to make the variable bases345 the manual (that is, dynamic) way:
var bases345 = new Bases(); //or whatever the name of your class is, and it must be set for Export
Then to place it into an array (for example, on the very next line):
var clipArray:Array = [bases345];
Now there are two ways to refer to the new instance of Bases that was just instantiated:
bases345
--AND--
clipArray[0];
Both of those monikers now point to the same object. So you can see that basically objects are anonymous and can be pointed to by any variable name you want. It's the variable name that makes the object meaningful to us humans, as people can write some pretty descriptive variable names.
There is another way to get a reference, and it does use a string:
this["bases345"];
Personally, I hate this particular way of getting a reference, always avoid it myself, and it seems like just a crutch for those who just want to hang on to instance names and strings at all costs because they haven't yet figured out the true nature of variables, that they are just pointers to objects and not the actual objects themselves.
You have to be able to just picture in your mind an object floating around out there and it's not wearing a name tag. I like to picture objects as bubbles, myself. And then by "out there" I might mean floating around in the computer's memory somewhere. When you take an object like this, and assign a variable to it (what we like to call a "reference") you basically give yourself a way to "talk" to the object with programming, and access its properties, methods, and events. In other words, you get a handle on it.
But to really show you the true nature of the instance names that flash uses in the properties panel, let's suppose that I made bases345 by dragging an instance from the library and then giving it that name in the properties panel. I could actually change it's "name" by doing this:
var foo = bases345;
bases345 = null;
Now the object's "name" is no longer bases345, now it's foo!
Further, I could do this:
var bar = foo;
Now the object has two "names," either one of which is equally valid.
I hope this explanation helps. If not, maybe I didn't fully understand your problem, and you are welcome to send me your file if you want.
Jody
Hi Jody, Let me explain a
Hi Jody,
Let me explain a little more. I have 30+ movieclip instances that i have dropped into a MC, but offstage. depending on the situation of my game, i want to pick a specific movieclip and move it into view. The game goes out to an sql db and fetches some information, which includes the name of the instance i want to move into view. So,
trace(clipname); // a string that returns "bases234", which is what i retrieved from the sql database and stored it in clipname variable.
so in this case, bases234 is the MC i want to manipulate. but next time through, it might be bases123 MC that i want to manipulate. it all depends on a complex algorithm that runs through PHP on the mysql dbase and selects the next clip to show.
I just want to put whatever happens to be in my clipname variable at the time, into the clipArray[0] so i can manipulate it.
var clipArray:Array = [??????];
clipArray[0].x = 435;
clipArray[0].y = 65;
Does this make more sense?
Chi
Figured it out...
duh...this was a simple one.
var myclip:String = "bases234";
var clipArray:Array = [this[myclip]];
clipArray[0].x = 435;
clipArray[0].y = 65;
thanks,
Chi
Do you really need an array?
You wrote "I just want to put whatever happens to be in my clipname variable at the time, into the clipArray[0] so i can manipulate it."
I hope you are speaking in general terms. If there is always giong to be only one item, you have no need for an array, just a variable.
yes...
Jody,
Yes, I still want an array because i have a half dozen different types of clips that i am moving around, all with their own collection of possibles. I also need to move them on and off the stage, so i am populating with prior and current mc's. I was simplifying the question by just showing you one piece of the array. BTW...love your method of using arrays for clips and buttons. makes the code so much lighter.
One other question. Right now i am using x and y position to move them on and off stage. is it better to just put them in the location and use visible function to turn them on or off? or is it 6 of one half dozen of the other?
Thanks, Chi
visible = false is better
I've used both methods you describe. I feel like the visible property is better and cleaner. If you move something off the stage, I believe the player still has to render it. And then there's the nagging thought of something hanging around "out there," and how far offstage is far enough, etc, etc. It may be that if you move something offstage and it's not far enough out there, it might be revealed if someone resizes the window. Setting visible = false not only hides the object, it prevents the user from interacting with it (which is not the case with alpha = 0). And I've read it's a lot faster than addChild / removeChild. Here is a nice article on the whole issue of hiding objects, by Colin Moock: http://www.developria.com/2008/11/visible-false-versus-removechi.html However, he doesn't even mention repositioning the object offstage as a possible way, and the fact that he doesn't must mean that either (a) he didn't think of it, or (b) he didn't consider it worth mentioning because he considers it a bad hack. (a) is possible but probably unlikely, as Moock is very, very smart, and (b) is also possible, but I am just guessing at his opinion of it... LOL.
makes sense!
Jody,
Great advice. I had not thought about the rendering, but no reason to make the engine work harder than necessary. I was going to ask about addchild removechild, but i was kind of thinking the same thing you mentioned about speed. if add them all up front and simply turn on and off, that has to be faster. I will go read the article now.
As usual, you have great advice that is backed up with logic.
Chi
I'm wrong again for the first time in a row...
I think I'm wrong about off screen objects being rendered by the player. That's why I kind of qualified it with "I believe..." I had a feeling.
I just did a Google search and found this: http://help.adobe.com/en_US/as3/mobile/WS948100b6829bd5a66f2268f12913a0c...
Even though off-stage objects do not continue to be rendered, note that the above link also communicates Adobe's recommendation for NOT placing objects off-stage. What do you know? I learn something every day.
One other disadvantage I thought of: Off-screen objects can be a pain when the swf they're in is loaded externally by another (main, or host) swf, because now the former swf is not the "stage owner" anymore. Objects that originally were off-screen when the swf owned the stage, are now just making the swf physically bigger in width and height and will have to be masked out with a mask the size of the original stage. I've had to do this before. If I'm lucky I might still remember how!
BTW, thank you for your continued support, Chi. I really appreciate it.