I have made a simple drag and match game for kids.
I used setchildindex for movie clips to be dragged but when I click next button and go to another frame but movie clips are remaining in the same stage. What should i do?
Here is my code I used: (drag_1, this.numChildren0);.
When I reload it's not working.
drag_1.buttonMode = true;
drag_1.addEventListener(MouseEvent.MOUSE_UP, dropMe_1);
drag_1.addEventListener(MouseEvent.MOUSE_DOWN, dragMe_1);
var back_1X:Number = back_1.x;
var back_1Y:Number = back_1.y;
var hit_2X:Number = hit_2.x;
var hit_2Y:Number = hit_2.y;
function dragMe_1(event:MouseEvent)
{
drag_1.startDrag()
setChildIndex(drag_1, this.numChildren-1);
}
function dropMe_1(event:MouseEvent)
{
drag_1.stopDrag();
if(drag_1.hitTestObject(drop_2))
{
TweenMax.to(drag_1, 0.5, {
x:hit_2X,
y:hit_2Y,
ease:Cubic.easeOut
});
drag_1.mouseEnabled = false;
SoundMixer.stopAll();
}
else
{
TweenMax.to(drag_1, 0.5,
{
x:back_1X,
y:back_1Y,
ease:Bounce.easeOut
});
}
}
You need to remove the MovieClips using removeChild().
Now, why do you need to do that here? Well, this is one of those odd problems you get when you mix the timeline with code. When you place a symbol on the timeline keyframe, the Flash Player will instantiate that symbol when it reaches that frame. After that, any frame on the timeline that updates the symbol (tweens, effects, etc) will do just that, and any frame that lacks the symbol will remove it. However, the Flash Player is very picky about identifying that symbol on each frame of the timeline. When you move it using setChildIndex you are basically breaking the timeline link, and the Flash Player no longer identifies it and removes it based on the keyframes. You'll also find that if you revisit a keyframe that had that symbol, the Flash Player will instantiate a second one regardless if the one you moved is still there. As you can see, it can get pretty messy.
Related
Sorry this is so specific but I have combed through so many pages and videos and tutorials and can't figure this out.
I have all of my animations within a MovieClip. In the movie clip is also a stage sized white square button with the instance name "btn". Back on the main stage I have a second layer called "actions" with the following code applied to the first (and only) frame. It's not working. At all. (HUGE) tia
stop(); // this will stop the movie from playing at the start
btn.addEventListener((MouseEvent.ROLL_OVER, playMovie);
btn.addEventListener((MouseEvent.ROLL_OUT, stopMovie);
function playMovie(evt:MouseEvent):void {
play();
}
function stopMovie(evt:MouseEvent):void {
stop();
}
The problem is when you say play(); or stop(); which object are you really commanding? Your playMovie function could be in theory used to control many MovieClips at once, in different ways, so be specific with your commands...
btn.play(); //start the playback of "btn" MC
btn.stop(); //stop the playback of "btn" MC
Also consider using MOUSE_OVER/OUT instead ROLL_OVER/OUT etc but whatever works for you.
For reversing you will use btn.prevFrame(); together with an ENTER_FRAME event function. This function reads your Document settings for the FPS. For example, if you set 30 frames-per-sec then whatever instructions you put inside the event function will be processed 30 times per second.
See this other Answer for advice about reversing the playback of a MovieClip.
#VC.One is correct in how you should implement a solve to your issue, however in response to your comment on their answer, I thought I would demonstrate how to implement this fully for you - incase they don't.
var removeUpdate = false;
btn.addEventListener(MouseEvent.MOUSE_OVER, playMovie);
btn.addEventListener(MouseEvent.MOUSE_OUT, stopMovie);
function playMovie(evt:MouseEvent):void {
// Stop rewinding the movie clip and play it
if(removeUpdate){
stage.removeEventListener(Event.ENTER_FRAME, update);
removeUpdate = false;
}
// play our button
btn.play();
}
function stopMovie(evt:MouseEvent):void {
// stop our button
btn.stop();
// ... and rewind it
stage.addEventListener(Event.ENTER_FRAME, update);
removeUpdate = true;
}
function update(evt: Event){
// moves the button movie clip backwards one frame.
btn.prevFrame();
// If we have finished rewinding the movie clip, then stop
if(btn.currentFrame == 1){
stage.removeEventListener(Event.ENTER_FRAME, update);
removeUpdate = false;
}
}
It is important that you remove the update event because if you don't, the movie will never play again, because it will go one frame forward and then back again every frame due to; btn.play(); btn.prevFrame();
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.
Ok, so I'm a beginner at AS3 and Flash and I managed to put this code together for an animation. A Button called start_btn is supposed to start and stop a movieclip called main_mc. On the first click of the Button, the Movieclip is supposed to play (which it does), however on the second click, the movie stops in the middle of its animation (which I don't want). My question is, when you click the Button a second time, how can i get the Movieclip to finish playing its animation then stop on the last frame?
I thought about using if (main_mc.currentFrame == main_mc.totalFrames); {main_mc.stop(); but the Movieclip still does not stop on the last frame. The Movieclip itself also has a gotoAndPlay(2); command on the last frame so that the animation repeats before the Button is clicked a second time.
here is the code i have:
`start_btn.addEventListener(MouseEvent.CLICK, mainaniS);
function mainaniS(event:MouseEvent):void
{
main_mc.play();
start_btn.removeEventListener(MouseEvent.CLICK, mainaniS);
start_btn.addEventListener(MouseEvent.CLICK, mainaniSt);
}
function mainaniSt(event:MouseEvent):void
{
if (main_mc.currentFrame == main_mc.totalFrames);
{main_mc.stop();}
start_btn.removeEventListener(MouseEvent.CLICK, mainaniSt);
start_btn.addEventListener(MouseEvent.CLICK, mainaniS);
}`
Try main_mc.gotoAndStop(main_mc.totalFrames).
I was going to provide a quick and dirty solution, but decided instead to try and explain a few of the issues with your current implementation and attempt to refactor and explain and better one. Unfortunately I don't have access to Flash right now, so the code is untested.
You're adding and removing event listeners often, which is generally a bad idea. Instead, since you're using a single button to perform multiple functions it would make sense to track the button state in a separate variable. In this case, a boolean for whether or not the movieclip is currently playing.
var playing:Boolean;
Now we can combine the mainaniS and mainaniSt into one and perform a different action based on whether or not the movieclip is playing, and just keep the one eventlistener on the button. I've also taken the liberty of naming the method something more meaningful:
start_btn.addEventListener(MouseEvent.CLICK, onStartClick);
function onStartClick(event:MouseEvent):void
{
if(playing) {
playing = false;
}
else {
playing = true;
main_mc.play();
}
}
You may be wondering why we don't call main_mc.stop() in the first block: the reason is that you don't want to stop the movieclip as soon as you click the button, but after the movieclip has finished playing if the button has been clicked. Therefore, we just set playing to false to indicate that we want it to stop later.
Finally, we need to make sure the movieclip stops upon completion, but only if playing is false. To do this we add a listener to movieclip that is called every frame, and checks whether playing is false, and if it's on the last frame. Note that the last frame is actually totalFrames - 1: this is because the frame numbers start from zero rather than one (i.e. if totalFrames is 3, the frame numbers will be 0, 1, 2).
main_mc.addEventListener(Event.ENTER_FRAME, animate);
function animate(event:Event):void {
if(!playing && main_mc.currentFrame == main_mc.totalFrames - 1) {
main_mc.stop();
}
}
All the refactored code together:
var playing:Boolean;
start_btn.addEventListener(MouseEvent.CLICK, onStartClick);
main_mc.addEventListener(Event.ENTER_FRAME, animate);
function onStartClick(event:MouseEvent):void
{
if(playing) {
playing = false;
}
else {
playing = true;
main_mc.play();
}
}
function animate(event:Event):void {
if(!playing && main_mc.currentFrame == main_mc.totalFrames - 1) {
main_mc.stop();
}
}
I've got a movieclip that is composed of 3 separate symbols. 2 of the symbols have their alpha tweened over 60 frames. 1 of the symbols is not tweened at all. All symbols are in separate layers, and there is a 4th, empty layer with a keyframe on frame 60 for actionscript.
The actionscript on frame 60 is simply "stop();" .
I am adding an instance of the movieclip to the stage dynamically from the document class. When I have "stop();" in there, the movieclip appears on the stage and skips straight to frame 60, where it succesfully stops.
Without "stop();" in there, the movieclip plays the alpha tweens perfectly, but obviously continuously loops.
Manually dispatching an Event.COMPLETE and listening for it does not work either and I would prefer not doing it that way anyway.
Here is the code that adds the movieclip to the stage:
//initialize the gameplay, remove title screen.
private function initialize_gameplay():void
{
//remove the title screen
initialize_title_screen(true);
this.screen_transition_obj = new tide_out_video();
this.addChild(this.screen_transition_obj);
this.game_board = new tidepool_gameboard();
this.screen_transition_obj.addEventListener(Event.COMPLETE,swap_transition_video_for_screen);
}
//replace the current transition video with the screen it was transitioning to
private function swap_transition_video_for_screen(e:Event){
this.addChild(this.game_board);
if(this.screen_transition_obj != null){
if(this.getChildByName(this.screen_transition_obj.name)){
this.removeChild(this.screen_transition_obj);
}
this.screen_transition_obj.removeEventListener(Event.COMPLETE, swap_transition_video_for_screen);
this.screen_transition_obj = null;
}
}
The movieclip's class is tidepool_gameboard and the property of the document class that stores the reference to it is game_board.
Any idea why putting stop(); on frame 60 of the movie clip is causing it to skip to the end without tweening?
UPDATE:
Adding the movieclip to the stage instantly instead of as the result of an event listener works properly, the problem only occurs when the movieclip is added in the event listner.
I can't believe I overlooked this, as it appears to be fairly obvious to me now.
In the code posted in the question, I've initialized a new instance of this.game_board, then added it to the stage after a delay based on an event listener for a video clip. The animation is playing, but it's playing before the clip ever is added to the stage.
Thanks go to alecmce who answered this question.
I did his Event.ADDED_TO_STAGE event listener, and it worked, which led me to realize that the MovieClip does not wait until it is added to the stage to start playing its own timeline, it simply starts the second you instantiate the object.
This is the new, fully functional code:
//initialize the gameplay, remove title screen.
private function initialize_gameplay():void
{
//remove the title screen
initialize_title_screen(true);
this.screen_transition_obj = new tide_out_video();
this.addChild(this.screen_transition_obj);
this.screen_transition_obj.addEventListener(Event.COMPLETE,swap_transition_video_for_screen);
}
//replace the current transition video with the screen it was transitioning to
private function swap_transition_video_for_screen(e:Event)
{
this.game_board = new tidepool_gameboard();
this.addChild(this.game_board);
if (this.screen_transition_obj != null)
{
if (this.getChildByName(this.screen_transition_obj.name))
{
this.removeChild(this.screen_transition_obj);
}
this.screen_transition_obj.removeEventListener(Event.COMPLETE, swap_transition_video_for_screen);
this.screen_transition_obj = null;
}
}
It's been a while since I've had to write Actionscript that really needs to integrate with the timeline (in this case, controlling a series of frames that must happen in a certain sequence) and I am trying to figure out what to do.
In the first few frames, I have a button "next_1".
At frame 10, I need to have another button "next_2". I really really need this button to not be on frame one (I could possibly just make it invisible, but that's going to create a clickable area that I don't want).
The problem is, anything I don't put on "frame_1" renders as null in my Document class.
Is there any solution to this? I would rather not have to write my script on the timeline if possible (it seems easier in the long run to keep it in a document class)...
Items on the timeline are created on the fly, so if the playhead has not reached frame 10, next_2 is not created.
Easiest Document-class solution:
Create an array of frame labels like ["label1", "label2"]
Create sectionIndex var and set it to 0
Create a next button on its own layer so it is always showing.
When the next button is clicked, increment sectionIndex, then gotoAndPlay(myLabels[sectionIndex])
Okay, directly lifted from "Real World Flash Game Development":
/**************************************************
* FRAME LABELS *
**************************************************/
private function enumerateFrameLabels():void {
for each (var label:FrameLabel in currentLabels) {
addFrameScript(label.frame-1, dispatchFrameEvent);
}
}
private function dispatchFrameEvent():void {
dispatchEvent(new Event(currentLabel, true));
}
This dispatches an event at each frame label on the timeline.
Then you can just add event listeners for each frame:
addEventListener("name_of_my_framelabel", frameHandler);
addEventListener("another_framelabel", frameHandler);
And write a switch statement to add event listeners for the buttons when they actually show up on the timeline.
private function frameHandler(e:Event):void {
switch(e.type) {
case 'screen_2':
stop();
next_2.addEventListener(MouseEvent.CLICK, click2, false, 0, true)
break;
}
}