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

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.

Related

AS3 tween object not working with .hitTestObject()

I am having a major problem in my new browser app.
Okay so I made game where different cubes (squares) spawn at the top of the screen and I use the Tween class to make them go down the screen and then disappear.
However I want to detect a collision when a cube hits the player (that is also a flying cube).
I tried everything, truly everything but it does not seem to work. The problematic thing is that when I remove the "Tween" function it does detect collision with the hitTestObject method but when I add the "Tween" line collision won't be detected anymore.
It looks like this:
function enemiesTimer (e:TimerEvent):void
{
newEnemy = new Enemy1();
layer2.addChild(newEnemy);
newEnemy.x = Math.random() * 700;
newEnemy.y = 10;
if (enemiesThere == 0)
{
enemiesThere = true;
player.addEventListener(Event.ENTER_FRAME, collisionDetection)
}
var Tween1:Tween = new Tween(newEnemy, "y", null, newEnemy.y, newEnemy.y+distance, movingTime, true);
}
And the collision detection part:
private function collisionDetection (e:Event):void
{
if (player.hitTestObject(newEnemy))
{
trace("aaa");
}
}
I am desperate for some information/help on the topic, it's been bugging me for days.
Thanks for your time, I would be very happy if someone could help me out^^
First, make sure the "newEnemy" instance and the "player" instance are within the same container. If they are not, their coordinate systems might not match up and could be the source of your problem.
Otherwise, you need to keep a reference to each enemy instance you create. It looks like you are only checking against a single "newEnemy" variable which is being overwritten every time you create a new enemy. This might be why you can successfully detect collision between the player and the most recent "enemy" instance.
So... you need a list of the enemies, you can use an Array for that.
private var enemyList:Array = [];
Every time you create an enemy, push it to the Array.
enemyList.push(newEnemy);
In your "collisionDetection" function, you need to loop through all of the enemies and check if the player is touching any of them.
for(var i:int = 0; i < enemyList.length; i++)
{
var enemy = enemies[i];
if (player.hitTestObject(enemy))
{
trace("Collision Detected!");
enemy.parent.removeChild(enemy); // remove the enemy from the stage
enemies.splice(i, 1); // remove the enemy from the list
}
}
I'd suggest that you move to TweenMax, it just might solve your problem, and in my experience it's much better in every possible way.
Scroll down the following page to see a few variations of this library, I myself use TweenNano, they're completely free of charge:
https://greensock.com/gsap-as
I think some plugins cost money, but I doubt you'll ever need them.

Can i create a function to tell a button to open a movie clip of the same name

I am new to the actionscript side of flash,
I am working on a map that has say 20 popups(movieclips) and the countries are the buttons, i have just been informed i need to add 60 more.
Below is an example of the code i have been using
english_movie.visible=french_movie.visible=turkish_movie.visible=false
english_btn.addEventListener(MouseEvent.CLICK, englishButtonClick);
french_btn.addEventListener(MouseEvent.CLICK, frenchButtonClick);
turkish_btn.addEventListener(MouseEvent.CLICK, turkishButtonClick)
function englishButtonClick(event:MouseEvent):void {
english_movie.visible=true;
english_movie.play();
french_movie.visible=turkish_movie.visible=false
}
function frenchButtonClick(event:MouseEvent):void {
french_movie.visible=true;
french_movie.play();
english_movie.visible=turkish_movie.visible=false
}
function turkishButtonClick(event:MouseEvent):void {
turkish_movie.visible=true;
turkish_movie.play();
english_movie.visible=french_movie.visible=false
}
Im thinking there must be an easier way to do this than replicating the code over and over.
Any help would be much appreciated.
Here's how to simplify the whole thing with code: Each btn object is
related to one movie object. This can be achieved with a Dictionary.
var btnToMovieAssociation:Dictionary = new Dictionary();
btnToMovieAssociation[english_btn] = english_movie; // repeat this line for every btn/movie pair
Now you have to generalise your click handler. The key difference
between each function (apart from making one certain movie visible)
is that they all make certain other movies invisible. But actually,
it's sufficient to only make the previously visible movie invisble.
To do this, create a variable that keeps track of the current visible
movie.
var currentMovie:MovieClip = english_movie;
Initialising the variable with english_movie has no effect on the
program. you can pick any other of the movies. It will make things
easier in the following code if this variable is initialised.
Now your function does effectively this:
make movie of clicked button visible
play this movie
make last movie invisible
Here's the cool part. You only add one listener. Look up if something
is in the dictionary for the clicked thing and consider that the
movie you want to show next.
addEventListener(MouseEvent.CLICK, buttonClick);
function buttonClick(event:MouseEvent):void
{
var movie:MovieClip = btnToMovieAssociation[event.target]
if (movie == null)
return; // nothing in the dictionary, it wasn't a button that was clicked.
movie.visible=true;
movie.play();
currentMovie.visible = false;
currentMovie = movie;
}
There are problems with this solution:
You still have to declare every pair, which is still tedious and prone to erro. (you have to type every name twice)
If your buttons are made up of several objects, event.target might point to them instead of the button as a whole. But with only
the btns in the dictionary and not all their individual parts,
nothing would be found in the dictionary. This can be circumvented by
setting mouseChildren = false; on every btn.
i posted this question else where and got this response
var tl:MovieClip=this;
var mc:MovieClip;
var i:int;
var buttonA:Array=[english_btn,french_btn,turkish_btn];
for(i=0;i<buttonA.length;i++){
buttonA[i].addEventListener(MouseEvent.CLICK,buttonF);
tl[buttonA[i].name.split("_")[0]+"_movie"].visible = false;
}
function buttonF(e:MouseEvent):void{
for(i=0;i<buttonA.length;i++){
tl[buttonA[i].name.split("_")[0]+"_movie"].visible = false;
}
tl[e.currentTarget.name.split("_")[0]+"_movie"].visible=true;
}
Which works great.

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?

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