Actionscript 3: Back History Button - actionscript-3

Sorry in advance. I am new in Actionscript and trying to implement a history back button, not a go to previous frame button; in a flash movie, not in a flash web page). My approach is to use an array and push the currentframe to it on any visited frame. My code is not working and it is giving back an error #1502.
Please help me to fix the code I have by now:
var back:Array = new Array(); //global variable
stop();
back.push(MovieClip(this.root).currentFrame); // in every visited frame
back.addEventListener(MouseEvent.CLICK, backGoTo);
function backGoTo(event:MouseEvent):void
{
switch (currentLabel)
{
case "framelabel":
for (var i:int = 0; i < back.length; i++)
{
gotoAndStop(back[i]); // expected to go to the last visited frame.
}
break;
}
}
thanks a lot in advance.

To get the last element of an array, use back[back.length-1], or if you want to remove the last element and get it back at the same time then back.pop();. So in your case, that for cycle has no use, it's all wrong.. nonsense to check all the elements of an array in this case.
function Softkey3GoTo(event:MouseEvent):void
{
switch (currentLabel)
{
case "m_1":
case "m_2":
case "reply":
case "messages":
gotoAndStop(back[back.length-2]); // -2 because the current one is -1, the previous frame is -2.
break;
}
}

After exploring the Array .pop method I could find the solution to my back button. here is my code for a global back history button. in every frame you must push it to the array. call the last element of the array and delete it at the same time:
var back:Array = new Array(); //global variable
stop();
back.push(MovieClip(this.root).currentFrame); // in every visited frame
back.addEventListener(MouseEvent.CLICK, backGoTo);
function backGoTo(event:MouseEvent):void
{
switch (currentLabel)
{
case "framelabel":
back.pop();
gotoAndStop(back.pop());
break;
}
}

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.

Add event listener to the items in an array

I have an array with movieclips (r1, r2 etc) which are placed on stage dynamically every time there is a hittestobject. I want to add event listener to those movieclips in the array so that every time there is a click on them the last item to be removed. I have this code but it seems to be wrong because only the first element of the array is remove. Can you please help me?
var counter:int = 0;
function releaseToDrop(e:MouseEvent):void
{
Star(e.target).stopDrag();
if (Star(e.target).hitTestObject(target))
{
removeChild(Star(e.target));
var replace:Array = [r1,r2,r3,r4,r5,r6,r7,r8]
addChild(replace[counter]);
counter = counter+1;
}
else
{
trace("No collision.");
removeChild(Star(e.target));
}
}
replace[counter].addEventListener("click", bindClick(o));
function bindClick(o)
{
replace.splice(replace.indexOf(replace[counter]),1);
trace(counter);
}
For one, your bindClick function is inside the releaseToDrop function. This way that function will not be called. Also, try to use the static refence for the click event, so MouseEvent.Click instead of "click".
For your convenience, trace the current state of "counter" in the bindClick function, so you know what element will be removed. You will probably then find out where the flaw is in your logic.
A wise thing to to is also to check counter is not outside the scope of your array. So in bindClick, check for counter < replace.length. (for that the replace array of course has to be defined outside of the function scope of releaseToDrop.
Hope this gets you working in the right direction.
For future references adding addEventListener to the elements of an array so that every time there is a click on them the last item to be removed can be done by this code:
import flash.display.Sprite;
var replace:Array = [r1,r2,r3,r4,r5,r6,r7,r8]
var counter:int = 0;
// Every time there is a hittestobject an element of the array is placed on stage
function releaseToDrop(e:MouseEvent):void
{
Star(e.target).stopDrag();
if (Star(e.target).hitTestObject(target))
{
removeChild(Star(e.target));
addChild(replace[counter]);
counter = counter+1;
trace(counter);
}
}
// The first element of the array has an eventListener and when is clicked the last item of that array shown on stage is removed
var yourSprite:Sprite=new Sprite();
replace.push(yourSprite);
replace[0].addEventListener(MouseEvent.CLICK, myClickHandler, false, 0, true);
function myClickHandler (e:MouseEvent):void
{
counter =counter-1;
if (contains(replace[counter]))
removeChild(replace[counter]);
trace(counter);
}
Can you add an event listener to every movie clip that you spawn, then in each event handler, add a conditional to check if the clicked movie clip is the last item, then remove it (or do whatever you need to to it).
You should also accept Chicken's advice.

AS3 - Recording a click with playHeadTime and using the time

I am fairly new to AS3 so any help would be appriciated.
Basically I am trying to make something similar to the Hazard Perception test, where you click and it records weather you clicked at the right time or not.
What I have so far is this:
import flash.events.Event;
videoOverlay.addEventListener(MouseEvent.CLICK,doClick)
function doClick(e:Event):void
{
trace(myVideo.playheadTime)
}
I have managed to make a clickable area and then display click times, what I now need to do is to be able to tell if a click was in a certain time frame then add 1 point, and then at the end of the video clip I want to display a score.
I am not after just code, if anyone could maybe suggest a way of doing this it would be appriciated.
You can store the 'right moments' in an array, xml, whatever. Let's say something like this:
var moments:Array = [{start: "1:01", end: "1:16"}, {start: "1:25", end: "1:26"}, {start: "1:39", end: "1:51"}];
//time is in minutes, so you need to convert it to seconds
function doClick(e:Event):void
{
for (var i:int = 0; i < moments.lenght; i++)
{
var moment:Object = moments[i];
if (myVideo.playheadTime => toSeconds(moment.start) && myVideo.playheadTime <= toSeconds(moment.end))
{
trace("that's the right moment");
break; //we do not need to check further
}
}
}
The best way I can think of to do this is a timer. Setup a timer class and on the click event display rather or not the click is within a certain range of time - if so then success if not so then failure.
Try something like this:
counter:Number = 0;
videoOverlay.addEventListener(MouseEvent.CLICK,doClick)
public function doClick(e:Event):void
{
if (counter < 10)
trace("Success, your quick!");
else
{
trace("Failure!");
}
}
addEventListener(Event.ENTER_FRAME, timed_event);
public function timed_event(event:Event) : void
{
counter++;
}
Of course, the time rate depends upon the frame rate of the application as well.

Removing Children in AS3

My flash game exists of a timeline with multiple frames (I know I should avoid the timeline)
The point of the game is a point and click adventure. The object that you are able to pick up get spawned and destroyed accordingly as you enter and leave the room. now my problem is when entering frame 14 (accessibel from frame 12) it creates a piece of paper which you are able to pick up if you have another item. Now my problem is when you can't or don't pick up the paper and go back to frame 12 (only exit is to frame 12), you can't click on any other object and you are basicly stuck on frame 12. When leaving and entering other rooms it works properly but for some reason it doesn't for on the paper on frame 14.
My code to remove objects works as following
In my Main.as Documentclass I have a function that called as soon as the game starts which does the following
if (lastframe == 14)
{
trace (prop.numChildren);
while (prop.numChildren )
{
prop.removeChildAt(0);
}
}
The lastframe variable is established when moving from frames
this function is found on the frame itself (each exit function on it's own respective frame)
function exitKantine(event:MouseEvent):void
{
Main.lastframe = 14;
gotoAndStop(12);
}
The function to remove the prop actually removes it but then causes all other clickable objects to be unusable.
Thanks for looking at my question and thanks in advance for your suggestions
I would say instead of removing children, add it once in the beginning, add all the listeners in the beginning, and toggle the visibility instead of trying to addChild and removeChild every time you want to hide it. Use an array so you can have a few happening at the same time.
something like this:
private function init():void
{
assignVars();
addListeners();
stage.addChild // make sure this is in document class or you are passing stage to the class using it
}
for (var i = 0; i < _thingsAry.length; i++)
{
if (_thingsAry[i] == 14)
{
_thingsAry[i].visible = false;
trace("the visibility of _thingsAry[" + i + "] is " + _thingsAry[i].visible
}
}

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
}
}