How to Jump Randomly to Frame Labels without Repeats
One of the more popular entries of this blog describes How to Jump to a Random Frame Label. The ActionScript 2.0 involved is very straightforward, weighing in at a mere 5 lines. Its sole purpose is to choose a random label once at the beginning, then go to it (then stop). In the Comments section, a visitor named Heather asked for a variation in which the movie starts at a random label, then proceeds to the remaining labels in order, looping around to the beginning, if necessary, to hit each label once. I offered some suggested code, and eventually a number of other visitors asked for yet another variation: how to jump randomly to a whole series of labels — without repeats. That takes a bit more code, but it’s certainly doable. Let’s take a look.
An answer, short and sweet
This bulk of this code goes in frame 1, then one of its functions gets called at the end of each span of frames represented by a label. Here’s the code for frame 1:
function shuffle(arr:Array):Void {
var len:Number = arr.length - 1;
for (var i:Number = len; i >= 0; i--) {
var p:Number = Math.floor(Math.random() * (i + 1));
var t:Object = arr[i];
arr[i] = arr[p];
arr[p] = t;
}
}
var labels:Array = ["a", "b", "c", "d", "e"];
shuffle(labels);
var currentLabel:Number = 0;
function gotoNextLabel():Void {
if (currentLabel < labels.length) {
gotoAndPlay(labels[currentLabel]);
currentLabel++;
} else {
stop();
}
}
gotoNextLabel();
Gulp! That’s a lot of code, huh? We’ll step through it in the “How it works” section, but for the time being, just know that the labels array holds your frame labels (here, “a”, “b”, “c”, and so on — but you’ll use meaningful labels) and that you’ll obviously need corresponding frame labels in the timeline that holds this ActionScript. If your equivalent of label “a” spans from frames 2 through 100, you’ll put a new keyframe in your scripts layer at frame 100 with the following small function call in it:
gotoNextLabel();
If your equivalent of label “b” follows, spanning from 101 to 150, you’ll put a new keyframe in your scripts layer at frame 150 and call that gotoNextLabel() function again. Do this at the end of each span. The end result is that the playhead will visit each and every span at random without repeats.
How it works
The ActionScript above is visually organized into three sections, just for clarity. The first section (the custom shuffle() function) isn’t my own code, but a time-tested algorithm made popular on the Adobe forums by long-time contributor kglad. There are a number of variations on it, and a complete discussion would probably make a good blog entry of its own, but for the time being, let the shuffle() function be a magic stone in your pocket: it shuffles whatever array is passed to it, and it works.
The second section is comprised of two lines:
var labels:Array = ["a", "b", "c", "d", "e"];
shuffle(labels);
In this variation on the “jump to a random frame” theme, the labels array is declared outside of any function, which makes it available to all code in this timeline. It’s pretty clear to see what these two lines do. The first declares an Array instance named labels and sets it to a series of arbitrary frame labels. The second shuffles that array. After shuffle() is called, the array might look like any one of the following sample shufflings …
b,e,a,d,c
a,e,b,c,d
b,c,e,d,a
d,c,a,e,b
e,a,c,d,b
… which is great, because that means the final section of code is pretty easy. First, an arbitrarily named variable, currentLabel, is declared and set to zero.
var currentLabel:Number = 0;
Why zero? Because arrays start their counting at zero rather than one. We’re going to be starting at the first element in the array, sending the playhead to that frame label, then stepping through the remaining array elements in turn. This is accomplished by a custom gotoNextLabel() function.
function gotoNextLabel():Void {
if (currentLabel < labels.length) {
So far, the currentLabel variable (currently 0) is checked against the Array.length property of the labels instance. Have we reached the end of the array? Not yet, so the following occurs:
gotoAndPlay(labels[currentLabel]);
currentLabel++;
The playhead is sent to the frame label represented by labels[0] (the first element), and currentLabel is incremented by one (the ++ operator). This will happen every time the gotoNextLabel() function is called, and eventually, the value of currentLabel will no longer be smaller than the value of labels.length. When that happens …
} else {
stop();
}
}
The playhead stops. All spans of the timeline will have been visited, and the movie will rest comfortable in the final frame of its final randomly chosen span. To start the whole thing off, this last line of code calls the very function just described:
gotoNextLabel();
As noted earlier, you’ll have to repeat this gotoNextLabel() call in the last frame of each span of frame labels.
Have fun with it!
July 2nd, 2007 at 4:41 am
Hi,
I found this really usefull - however, I need the whole thing to repeat (after another shuffle of the array) rather than coming to a stop at the final array label - is this possible?
Thanks
Steve
July 2nd, 2007 at 9:01 am
Steve,
Instead of that
stop(), perform a re-shuffle, setcurrentLabelback to zero and start again:July 2nd, 2007 at 9:53 am
Hi david,
Thanks for the fast response. That works great. The one thing I have found with this is that on odd occasions the same frame plays after one another. I guess because of the re-shuffle this could be a possibility.
How would you change the stop() to play the array again without reshuffling? So that way the same frame label would never play twice in sucession?
I have tried taking out the
shuffle(labels);
currentLabel = 0;
…sections as I thought this would just play the array again without a re-shuffle but this does not seem to work?
I have been looking at other entries on your blog by the way and they are all really helpfull!!
Thanks again
Steve
July 3rd, 2007 at 8:20 am
Steve,
Statistically speaking, you would indeed have the occasional duplication with re-shuffles, as whatever item happened to be last previously might be first next time. That sort of occurrence could be checked for too, but if you’re comfortable shuffling once and simply repeating that same shuffle, just leave out the second reference to the
shuffle()function. You do want to keep thecurrentLabel = 0line, though, otherwise the cycle doesn’t start again from the beginning.August 31st, 2007 at 4:14 pm
Thank You David,
this was the script I was looking for a whole day long, I tried to make it myself, but it was a bit too much for my beginner skills in working with flash.
You are great!
Thank You!
August 31st, 2007 at 4:32 pm
Christian,
Thanks!
September 14th, 2007 at 10:15 am
Hi David,
I’m starting off with Action Script and still using FlashMX (I’m waiting for my upgrade) and I’ve input your code in my flash document. I’ve changed the code to VAR labels:Array = [“01”, “02”, “03”, “04”, “05”, “06”, “07”, “08”] and placed it in frame 1 on a separate action’s layer. It seems to work but it’s playing the frames in sequence starting from a random frame. So it will start at frame 04 and play 05-08 and then play in sequence from a random number. Any idea what I’ve missed or have done incorrectly? The file in question is the one titled “Applications” on the bottom right of the web page below:
http://www.variosystem.net/home2.html
Any help is appreciated. Thanks.
September 14th, 2007 at 10:35 am
William,
If memory serves me, the Flash MX IDE doesn’t support ActionScript 2.0, even though later versions of Flash can indeed publish AS2 all the way back through Flash Player 6.
Try removing the post colon syntax wherever you see it, leaving everything else the same. In other words, change
to
and so on — always removing the
:Typesuffixes. Let me know if that does it for you.September 14th, 2007 at 11:17 am
Hi David,
I’m so sorry for not getting back to you right away. It’s been a little hectic for me as of late.
Yes your code did work for me and I was relived! I did have to make a small change and removed the stop (); command at the end of the script. I found that the movie would play and just stop after one or two cycles. It worked for me and it’s now up and running on my client site.
Thanks again for your help.
October 16th, 2007 at 9:51 pm
William,
Glad to hear it!
November 14th, 2007 at 2:34 pm
I have the code in the first frame of an 8 frame movie . Each frame has its own movie clip to play. (banner)
When I run the file I get random numbers in my output window but it does not play the frame number coinciding with the number in the output (trace) window. It consistently just plays the first movie it comes to on the timeline.
Any ideas? Thanks
November 14th, 2007 at 2:41 pm
Adding here…
Codes used:
function getRandomFrame():Number {
return Math.ceil(Math.random()*(this._totalframes-1))+1;
}
trace(getRandomFrame());
this.gotoAndPlay(getRandomLabel());
and…
function getRandomLabel():String {
var labels:Array = new Array(”cityNet”, “buffaloSpirit”, “deposits”, “cityTrust”, “cityAdvantage”, “happyBirthday”);
var index:Number = Math.floor(Math.random()*labels.length);
return labels[index];
}
this.gotoAndPlay(getRandomLabel());
trace(getRandomLabel());
… on the above code i replaced the frame labels a, b, c with the frame labels that refer to the movie that plays its frame.
Neither worked for me…
November 14th, 2007 at 7:59 pm
c-young,
Every time you run that
getRandomLabel()function, you’re going to get a random item from thelabelsarray. So if you use the function to send the playhead somewhere, then (in a new line) use the function again in atrace()statement, the return values will likely be different. Same goes for yourgetRandomFrame()function. In order to trouble shoot, you may want to do something like this:… which will ensure that the value sent to the Output panel matches the value sent to the
gotoAndPlay()method. That would be a good first step, because that will tell you exactly what that method is receiving as input.November 15th, 2007 at 10:03 am
Thanks for the reply. I wasn’t sure anyone still monitored this thing.
Ok… I implemented the code you listed above and it is not working upon first starting the movie. It does, however work when I use the > (next button) on the last frame and it sends the playhead back to frame 1.
After starting the movie over three times this is what displays in the output window:
3
8
6
8
7
6
8
2
6
2
The code I am using goes as follows:
function getRandomFrame() {
return Math.ceil(Math.random()*(this._totalframes-1))+1;
}
trace(getRandomFrame());
var num:Number = getRandomFrame();
this.gotoAndPlay(num);
trace(num);
So I guess I need to figure out how to intitiate the function upon start of the movie.
There are a total of 7 frames in the movie. Frame 1 is blank except for the code. Frame 2 has a movieClip that begins upon frame entry. Frame 3 is the same… etc. Frame 7 has another movieClip that contains a 5 second delay at the end that sends the playhead back to frame 2.
I just need the player to randomly select a frame to play when it is first opened.
You can view the banner here:
http://www.cnbok.com/
I initially had:
gotoAndPlay(random(7)+1) or something to that effect
This would cause it to just go between 2 frames over and over and sometimes freeze the movie entirely so I removed it.
I didn’t think randomly pickung a frame would be so tough. Thank you for any and all of your help.
November 15th, 2007 at 11:00 am
c-young,
This is my personal blog, so I answer questions when I can.
I do get overwhelmed sometimes, but for the most part — like in this particular article’s comments, for example — my replies happen within a few days. Always luck of the draw, though. Sometimes I’m away on business or at the park with my daughter.
That’s somewhat perplexing, and it makes me wonder if you have something in frame 1 that sends the playhead elsewhere. By rights, anything you put into frame 1 should execute. Perhaps you have a nested movie clip in frame 1 that tells the main timeline to jump ahead? You didn’t mention that, but then, troubleshooting often turns up all sorts of questions.
I also wonder if you’re publishing for ActionScript 2.0 or 1.0? If your document is set for 1.0, you’ll need to drop all the post colon suffixes (
:Number,:String, etc.).The
random()function is a bit older, in the annals of ActionScript. It accepts a parameter, whereMath.random()doesn’t.random()returns a number limited by the parameter;Math.random()returns a number between 0 and 1.The expression
random(7)+1returns a value between (and inclusive of) 1 and 7. If that expression was in frame 1, it might return 1 and send itself back to that same frame, but that shouldn’t lock up the SWF. If I start a brand new FLA and past the following into frame 1:gotoAndPlay(random(7) + 1);… then add enough frames to make the timeline span to 7, then test my SWF, I can indeed see the playhead jump around in a random fashion. By chance, it might jump to frame 3, for example, then play through the end (frame 7), then automatically loop to frame 1, in which case it might jump straight to 7, then back again … perhaps this time to 5, on through 7, then back again, and so on.
If that caused Flash to lock up for you, it may be helpful if you start a new FLA and build up slowly to the goal you’re aiming for. Start with nothing — no assets — and work through nothing more than code to see the timeline jump around. Figure out if you want to put code in frame 1 only, or perhaps in other frames too, depending on your needs. Make sure you understand how each small part plays its role in the instructions you’re giving Flash.
November 15th, 2007 at 4:45 pm
Ok… a bit of an update here.
I have the code working right now. There is a hitch though. The flash piece and code works just fine on an internal test server. The problem comes when I move the file to my live server for users to view. The play head goes to the 3rd frame (buffalo spirit) every single time. If it doesn’t go to the 3rd frame it freezes and shows nothing on the live site at all.
See it at www.cnbok.com
Keep hitting refresh and it will go to the “buffalo spirit” movie every time and eventually freeze up.
I wondered if you might have an idea as to why this would happen. If you dont know much about the server side of things I understand. Just thought I’d give it a shot.
Thanks so much!
December 5th, 2007 at 12:13 pm
Dave,
thanks much for going over this script. im merely a beginner/intermediate actionscript user, but this was pretty easy to implement, and understand. Arrays are not my thing (not yet anyway) so i needed a bit of help.
Im sure you know, but i dont see it mentioned, that this script will find the frame labels in other scenes as well. I was afraid i may have had to restructure my project, but was thrilled to find this case wasnt so. I had to make a couple of small changes to your scripts, and even had to write a bit outside your script to get what i wanted… but everything went very well and worked out great.
Thanks again.
December 5th, 2007 at 12:23 pm
[Follow up on c-young …]
I was able to get c-young’s file to respond as expected on my own machine, but the size of the SWF itself was large enough that this was likely a preloader issue — that is, the playhead was being asked to jump to frames that hadn’t yet loaded.
To Batgra …
You’re absolutely right, as long as frame labels are unique across all Scenes, both
gotoAndPlay()andgotoAndStop()are smart enough to find the relevant Scene at runtime.I think it’s great that you changed my suggestions and re-wrote certain aspects to meet your own needs! That’s honestly my best hope with this blog, that visitors can leave with something that helps them create their own solutions.
December 11th, 2007 at 10:04 pm
thanks mate, this is exactly what I was looking for.
worked like a charm!
December 12th, 2007 at 9:30 am
Aaron,
Sure thing.
January 18th, 2008 at 10:12 am
Hello
This is very useful but I seem to be having some trouble. I have entered the code as follows into Flash8 but for some reason it is recognising some kind of syntax error on line 14, and I can’t seem to work out why. Would you know?
function shuffle(arr:Array):Void {
var len:Number = arr.length - 1;
for (var i:Number = len; i >= 0; i–) {
var p:Number = Math.floor(Math.random() * (i + 1));
var t:Object = arr[i];
arr[i] = arr[p];
arr[p] = t;
}
}
var labels:Array = [”photo1″,”photo2″,”photo3″,”photo4″,”photo5″,”photo6″]; shuffle(labels);
var currentLabel:Number = 0;
function gotoNextLabel();Void {
if (currentLabel
January 27th, 2008 at 8:50 pm
rebecca,
For the life if me, I can’t remember now if I followed up with you via email. I think I did — and if so, it was probably to ask for the rest of your code sample, which seems to have been mangled by WordPress. If not, I hope you see my reply here and give it another shot.
If you need to type <, use
<(stands for “less than”); for >, use>(greater than) — that should let your code through.March 9th, 2008 at 8:10 pm
Dear David,
I have a problem using the code, my situation is that i want to random the frame from frame 3 to 12 without repetition and after it random all the 10 frames it will go to from 13 (end)
using your code example should i frame label it from frame 3 to 12 or should i change the “a”, “b”, to frame numbers?
Thanks!
March 9th, 2008 at 8:47 pm
Hey David,
I just found the solution to my problem
The thing is that i need to change the array into my frame number in order to make the function work.. Yay!!! you really a life-saver for me.. I would like thank you for the code you shared to other people.
Thanks and you rock!!!
March 10th, 2008 at 12:50 pm
Zai,
Glad to hear that! Good for you!
May 11th, 2008 at 1:40 pm
Hi Dave,
I really apreciate the clarity of your tutorial and am impressed with the control over randomness using the shuffle method. I’m working on a project at the moment which would have to react in the very same way, except instead of jumping to different labels, I’d like to load external movies. How would I achieve this? I am not too sure of how to customise your code to what I’d like to happen.
-Guillaume
May 12th, 2008 at 7:30 pm
Hi David,
I’m looking to add some functionality to this where I can restrict the array that gets shuffled to certain frame numbers.
For instance, there are up to 20 frames that could populate the array. The user selects the min and max and an inclusive array is generated appropriately to use with shuffle(labels); and gotoNextLabel(); commands.
May 15th, 2008 at 4:22 pm
Hi David,
Wanted to send you an update about my question, as I found a solution. Comments included in the code for other users to understand what’s going on.
First, I changed the array “labels” to “labels1″, including the list of frame titles/numbers the same as your example. This creates an array with all possibilities. I removed the following code from the list, as it will be incorporated later:
shuffle(labels);
Then added:
var j.Number = new Number(); //creates a variable that the user chooses later
var k.Number = new Number(); // dito
var labels:Array = new Array(); // creates a new array, which is used below.
Then to a start button I tied the below code, using the array slice function:
labels=labels1.slice(j-1,k);
shuffle(labels);
gotoNextLabel();
// .slice populates the “labels” array with a portion of the full “labels1″ array.
//”j-1″ dictates the beginning slice position in the array, adjusted one position to account for arrays starting thier count at 0.
// “k” dictates the end of the slice, AND IS EXCLUSIVE so doesn’t need to be adjusted.
// search “.slice” in Flash help for a more thorough discussion/understanding
// NOTE: the variables “j” and “k” need to match the position IN THE ARRAY that you want to pull from, not the frame numbers on the timeline (which you can include in the array “labels1″.
The other 2 lines are just initiating the code you had; shuffling the sliced array, and sending the user down their path. then “Next” buttons after that have “gotoNextLabel();” attached, which cycles through the sliced array. Note that I changed the full array to “labels1″ so I didn’t have to worry about the references to “labels” in the “gotoNextLabel();” function section.
May 29th, 2008 at 9:40 am
To Guillaume …
There are numerous ways to load content in Flash. In AS2, my personal favorite is the
MovieClipLoaderclass, because it dispatches events to let you know when content has loaded. Assuming the sameshuffle()function already in place, you could replace thegotoNextLabel()function with something like this (note the addition of a variable for theMovieClipLoaderclass, and the changes to some of the variable names to suite the new usage):Check out the
MovieClipLoaderclass entry in the ActionScript 2.0 Language Reference for details — but you’ll see that it features aloadClip()method, which accepts the two parameters shown: a) a file to load (here taken from the shuffledmoviesarray) and b) a movie clip container to load it into. In my sample code, the movie clip container’s instance name iscontainerClip, but that would be whatever you want. In fact, you could load SWFs or images directly into the main timeline by replacingcontainerClipwiththis.To Ezra …
Cool approach! Thanks for sharing your solution, too.
This is exactly the sort of thing that makes ActionScript (actually, programming in general) so much fun to me — figuring out handy solutions to problems as they crop up.
September 11th, 2008 at 12:58 am
This method worked great for a game I’m making based on the animal kingdom for children. Perhaps I’ll post a link when I’m finished. Thanks for taking time to share your knowledge, you saved my butt and I learned something.
September 11th, 2008 at 7:20 am
Thad,
Glad to hear that!
October 23rd, 2008 at 9:45 am
Hiya David,
Great script.
I’ve got a question. I’m creating a screenasver that scrolls some text panels across the screen. Is there a way to get multipul panels scrolling across, but in a random order.
6 types of text, need to scroll seamlessly
Cheers
t
October 30th, 2008 at 5:04 pm
Thanks so much for sharing this code, it works a treat!!