I have a swf file loaded into a main movie. When the child is finished playing, ie reaches its final frame, I would like the swf to unload. Can anyone tell me what bits I can add to this code?
//start button
start_button_aboriginal.addEventListener(MouseEvent.CLICK, fl_ClickToLoadUnloadSWF_3,false,0,true);
import fl.display.ProLoader;
var fl_ProLoader_3:ProLoader;
var fl_ToLoad_3:Boolean = true;
function fl_ClickToLoadUnloadSWF_3(event:MouseEvent):void
{
//ADDED APR02 START
fl_ProLoader_3.contentLoaderInfo.addEventListener(Event.INIT, childInitHandler);
fl_ProLoader_3.load(new URLRequest("myths/myth_aboriginal.swf"));
//ADDED APR02 END
if(fl_ToLoad_3)
{
fl_ProLoader_3 = new ProLoader();
fl_ProLoader_3.load(new URLRequest("myths/myth_aboriginal.swf"));
addChild(fl_ProLoader_3);
fl_ProLoader_3.x = 114;
fl_ProLoader_3.y = 41;
}
else
{
removeChild(fl_ProLoader_3);
fl_ProLoader_3.unloadAndStop();
fl_ProLoader_3 = null;
}
// Toggle whether you want to load or unload the SWF
fl_ToLoad_3 = !fl_ToLoad_3;
}
You could
1) Use the undocumented addFrameScript function defined in the MovieClip class to place a callback on the last frame of the child SWF. Useful if you don't have control over the code in your loaded SWF.
addFrameScript() has the following signature:
addFrameScript(frameNumber, callback); //frameNumber is zero-based (unlike in the Flash authoring suite, here you would enter 0 to refer to the first frame, and 1 for the second.)
In the parent SWF, add the following:
function fl_ClickToLoadUnloadSWF_3(event:MouseEvent):void {
...
fl_ProLoader_3.contentLoaderInfo.addEventListener(Event.INIT, childInitHandler);
fl_ProLoader_3.load(new URLRequest("myths/myth_aboriginal.swf"));
...
}
function childInitHandler(event:Event):void {
MovieClip(fl_ProLoader_3.content).addFrameScript(MovieClip(fl_ProLoader_3.content).totalFrames-1, unloadChild);
}
function unloadChild() {
fl_ProLoader_3.unloadAndStop();
}
If you're worried about addFrameScript going away - don't be. When you put code on the timeline, all that code is actually compiled into functions in the document class, with frame listeners added via addFrameScript.
Remember to define function unloadChild().
function unloadChild():void {
fl_ProLoader_3.unloadAndStop();
}
-OR-
2) Dispatch an event from your loaded SWF when it reaches the final frame.
In last frame of child SWF:
this.dispatchEvent(new Event("lastFrameReached"));
In parent SWF, add the following:
fl_ProLoader_3.content.addEventListener("lastFrameReached", unloadChild);
-OR-
3) Subscribe to the ENTER_FRAME event of the child and check if the child is on its last frame.
In parent SWF, add the following:
fl_ProLoader_3.content.addEventListener(Event.ENTER_FRAME, checkIfEnded);
function checkIfEnded(event:Event):void {
if (fl_ProLoader_3.content.currentFrame == fl_ProLoader_3.content.totalFrames) {
unloadChild();
}
}
I personally prefer addFrameScript - seems to me a cleaner solution. Callback runs once, you don't have to poll and you don't need to modify the child SWF.
There are a number of ways to solve this, and it has been asked many times...
Unload child swf on last frame of child swf
When External SWF has reached Frame X, how do I unload it?
That said, one way you could resolve this without creating event listeners on the child swf, is by periodically checking the child swf for the current frame. If it's the last one on the swf, call the unload method.
// once every second, indefinitely.
var tick:Timer = new Timer(1000, 0);
tick.addEventListener(TimerEvent.TIMER, checkSWF);
tick.start();
//Load something to the stage.
var loader:Loader = new Loader();
loader.load(new URLRequest("child.swf"));
addChild(loader);
function checkSWF(e:Event):void {
if (loader.content.currentFrame == loader.content.totalFrames) {
tick.stop(); // stop the timer
loader.unloadAndStop();
}
}
Related
The js code generated in the files output in Adobe Animation for HTML5 Canvas contains an object for the movie clip that include the code that is to be run in each frame.
(lib.cdseacc02_07 = function(mode,startPosition,loop) {
// timeline functions:
this.frame_0 = function() {
if(typeof showNarration !== "undefined"){ showNarration(1); }
}
this.frame_644 = function() {
playSound("02_07_01wav");
}
this.frame_652 = function() {
this.closeBtn_1.addEventListener("click", closepopup_1.bind(this));
}
this.frame_738 = function() {
this.stop();
}
}).prototype = p = new cjs.MovieClip();
If gotoAndPlay is called on the movieclip from this code the code on the frame function is not run on the frames as they are passing by, this frame code only works the first time the timeline is played. Is there any way to make it so that the code on the frames can run each time the playhead passes that frame even if it was through a gotoAndPlay call in the code?
Frame actions should absolutely be played each time the timeline passes that frame, whether during gotoAndPlay or gotoAndStop. In fact, this sometimes causes issues where things event listeners are added multiple times (each time the framehead passes the script, like on your frame 652).
Can you post a simplified example of this not working?
my problem is that the close button (removechild) which is placed in a swf works perfectly alone but when the swf is loaded from another one the button no longer works.
Here's the external swf code:
eti_scroll.scrollTarget = box_eti ;
hab_scroll.scrollTarget = box_hab ;
com_scroll.scrollTarget = box_com ;
descr_scroll.scrollTarget = box_descr ;
exit.addEventListener(MouseEvent.CLICK, exitBtn_clickHandler);
function exitBtn_clickHandler(event:MouseEvent):void {
if(this.parent) this.parent.removeChild(this);
}
And here's the button code from the main swf:
menu_button_2.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler2);
function fl_MouseClickHandler2(event:MouseEvent):void
{
var myLoader:Loader = new Loader();
var url:URLRequest = new URLRequest("pages/page_template.swf");
myLoader.load(url);
addChild(myLoader);
}
In the main swf there is no packages imported or loaderclass
Here is the link to the fla version and the example version:
http://www.mediafire.com/file/5dzqnq3kth6n6dt/examples.rar
Error thrown when I use trace code by #organis and loaded from an external SWF file:
The error thrown:
I am here! Exit Button: [object SimpleButton] object Event Handler: function
Function() {} function Exit.addEventListener: function Function() {} function
MouseEvent.CLICK This: [object MainTimeline] object Parent: [object Loader]
object Parent.removeChild: function Function() {} function Error: Error #2069:
La clase Loader no implementa este método(The Loader Class doesn't implement
this method). at Error$/throwError() at flash.display::Loader/removeChild() at
page_template_fla::MainTimeline/onClick()[page_template_fla.MainTimeline::frame1
:65] –
Let me explain how to deal with this kind of problem. Once something that is supposed to work does not, the first thing to do is to pinpoint where the problem starts exactly, so you can probably diagnose what the problem is rather than have a vague understanding there is a problem somewhere.
So you read all the traces from standalone run, then from loaded run and there certainly must be a difference. Upon finding it, you act with the regard to what that difference is.
// If this does not work, that means scripts do not work in the loaded SWF at all.
trace("I am here!");
// If this doesn't work the same as standalone, that means
// something breaks while constructing the loaded content.
trace("Exit Button:", exit, typeof(exit));
// Lets check id the event handler is doing fine.
trace("Event Handler:", onClick, typeof(onClick));
// If the method is not present on the object,
// something is deeply wrong with the whole thing.
trace("Exit.addEventListener:", exit.addEventListener, typeof(exit.addEventListener));
exit.addEventListener(MouseEvent.CLICK, onClick);
function onClick(event:MouseEvent):void
{
// If it doesn't work that means there's no mouse event.
trace("MouseEvent.CLICK");
// Just to check things out.
trace("This:", this, typeof(this));
trace("Parent:", this.parent, typeof(this.parent));
trace("Parent.removeChild:", this.parent.removeChild, typeof(this.parent.removeChild));
if(this.parent) this.parent.removeChild(this);
}
UPD: Now, you're getting the error (you should have mentioned it in your question in the first place, actually)
Error: Error #2069: La clase Loader no implementa este método(_The Loader Class doesn't implement tihs method_). at Error$/throwError() at flash.display::Loader/removeChild() at page_template_fla::MainTimeline/onClick()[page_template_fla.MainTimeline::frame1:65]
Now take a look here (I like this link as it explains quite a lot): https://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118a9b90204-7e26.html
Your script is on the main timeline. So, when you run it in a standalone mode, this.parent points to stage which can add and remove children, no problem.
Then, when you run it in the loaded mode, the hierarchy goes main SWF stage -> main SWF root -> ... -> Loader -> loaded SWF root. As you can see, when you address this.parent you get the Loader instance, the very one you use in main SWF to load the other one. Loader is a DisplayObjectContainer but is not intended for adding/removing children, thus it throws the exception mentioned above.
With all this I advise another way of removing content out of sight:
exit.addEventListener(MouseEvent.CLICK, onClick);
function onClick(event:MouseEvent):void
{
// Unsubscribe to help Garbage Collector do its job.
exit.removeEventListener(MouseEvent.CLICK, onClick);
// Hide the content.
visible = false;
// Remove all of its children.
removeChildren();
}
Or you can figure the case your script is running in and act accordingly:
exit.addEventListener(MouseEvent.CLICK, onClick);
function onClick(event:MouseEvent):void
{
// Unsubscribe to help Garbage Collector do its job.
exit.removeEventListener(MouseEvent.CLICK, onClick);
// Checking for
// if (parent == null)
// is unnecessary here because if that was the case
// there won't be a mouse event in the first place.
var aParent:DisplayObjectContainer = parent;
if (aParent is Loader)
{
// Loaded case.
aParent.parent.removeChild(aParent);
}
else
{
// Standalone case.
parent.removeChild(this);
}
}
However, that figuring will only work in the simple cases and will not in more complicated setups (cross-domain content, security sandboxes, etc).
New to AS 3.0 and I seem to have an issue where an external AS file is run when I test an individual scene in Flash Pro but not when I test the entire movie or when I test from Flash Builder. Anyone know what the problem might be?
Here's the code from the AS file:
package {
import flash.display.MovieClip;
public class Level1 extends MovieClip {
public var myplayer:MyPlayer;
public function Level1() {
super();
myplayer.x = 516;
myplayer.y = 371;
if (myplayer.x == 516)
{
trace("player x is 516");
}
else if (myplayer.y == 371)
{
trace("player y is 371");
}
}
}
}
Any ideas?
EDIT
I think I figured out the problem. The swf contained two scenes, and the external AS file started running at the start of Scene 1, but the myPlayer movie clip was not instantiated until Scene2, which, I think was causing the problem I was having, in addition to giving a 1009 null object error.
So I simply deleted the first scene, and now everything works fine. Maybe I will put that first scene in a separate SWF? Or, is there some way to delay a script's execution until a certain scene?
Your problem:
When the constructor of your doucment class runs, myPlayer doesn't yet exist so it throws a 1009 runtime error and exits the constructor at the first reference to myPlayer.
Solutions:
Put all the myPlayer code on the first frame of the MyPlayer timeline. OR use your current document class as the class file for MyPlayer (instead of documentClass). Change all references to myPlayer to this.
Listen for frame changes and check until myPlayer is populated, then run your code.
this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
function enterFrameHandler(e):void {
if(myPlayer){
//run the myPlayer code
this.removeEventListener(Event.ENTER_FRAME,enterFrameHandler);
}
}
If your frame rate is 24fps, this code though will run 24 times every second (until it finds myPlayer), so it's not the most performant way to go.
Use events. Add an event to the first frame of myPlayer (or to a class file for MyPLayer) that tells the document class that it exists now.
stage.dispatchEvent(new Event("myPlayerReady"));
Then listen for that event on the document class:
stage.addEventListener("myPlayerReady",playerReadyHandler);
playerReadyHandler(e:Event):void {
//your player code
var myPlayer = MyPlayer(e.target); //you can even get the reference from the event object
}
Thanks for your constructive, helpful responses, LDMS. I thought I had found a solution when I hadn't. Your advice worked. What I did was add the following code to the timeline of MyPlayer
this.x = 516;
this.y = 371;
if (this.x == 516)
{
trace("player x is 516");
}
if (this.y == 371)
{
trace("player y is 371");
}
and I removed the code from the document class. Everything seems to be working fine now. Thanks again for your help!
I have some code in a frame. It's basically
this.addEventListener(Event.ENTER_FRAME, handleUpdate);
function handleUpdate(e:Event):void
{...}
I want the code to be executed only when on that frame. But the handleUpdate function keeps getting called even when I'm out of that frame.
The timeline is stopped on this frame, and I want the handleUpdate to run continuously until the timeline moves off the frame.
If you're set on having the code for this on the frame in question, then you could do this:
var tmpCurFrame:int = currentFrame; //store the current frame
this.addEventListener(Event.ENTER_FRAME, handleUpdate)
function handleUpdate(e:Event):void {
if (tmpCurFrame != currentFrame) { //if the frame has changed, stop the frame handler
this.removeEventListener(Event.ENTER_FRAME, handleUpdate);
return;
}
//do your code
}
handleUpdate(null);
As an aside, it's much cleaner to have a document class and other class files that manage this sort of thing instead of using frame scripts. But if you all you're looking for is a quick and dirty tweak to your existing code, this should do the trick.
Haven't you heard about addFrameScript ?
It's perfect for your needs.
var desiredFrame = 25; // Timeline frame (starts from 1)
this.addFrameScript(desiredFrame-1, onFrame25); // 1st param is zero-based
function onFrame25():void
{
trace("I'm on frame", desiredFrame);
}
There's a few things you should consider with your approach:
Adding an ENTER_FRAME listener on the frame you care about happens after you enter that frame, so if the movieclip is playing you won't get an ENTER_FRAME event until the next frame (at which time it may have moved off that frame).
Be aware that code on a frame executes every time the playhead enters that frame, and you should be careful to remove listeners when appropriate for memory leak purposes.
So one approach would be to place this code on the frame in question - note that it also nicely removes its listener:
var thisFrame:int = currentFrame;
function handleUpdate(e:Event) {
if (currentFrame==thisFrame) {
// your code here...
} else {
// remove listener if we moved off the frame
removeEventListener(Event.ENTER_FRAME, handleUpdate);
}
}
// call it now because the listener won't fire until next frame
handleUpdate(null);
// add listener in prep for next ENTER_FRAME, though note that
// if we move off this frame, then the listener is removed above
addEventListener(Event.ENTER_FRAME, handleUpdate);
Another approach would be adding the following code on frame 1, so the listener always runs and is never cleaned up, and only performs the code when on frame 12:
addEventListener(Event.ENTER_FRAME, handleUpdate);
function handleUpdate(e:Event):void
{
if (currentFrame==12) {
// your code here...
}
}
I have loaded an external swf file which plays a flv file by default as swf is loaded. Now the problem is how do i remove the swf file from memory. my code :
var myLoader:Loader = new Loader();
var url:URLRequest = new URLRequest("ExternalSWF.swf");
myLoader.load(url);
detailMovieClip.movieHolder.addChild(myLoader);
I have tried many combinations of removeChild, unload and unloadAndStop but none works. I figure its all about not referencing correctly.
Update:
I went with the answer from Jegan, but it only work when i am testing in a dummy project which has only 1 numChildren, howver in real world code example numChildren reported 22 so i am not sure if that would be an issue. here is the real world code:
var myImageLoader:Loader;
var myImageRequest:URLRequest;
var theImagePath:String;
//part from xml processor function
theImagePath = "flash/"+myXML..item_video_link[n];
loadTheMovie(theImagePath);
function loadTheMovie(theImagePath):void{
myImageLoader = new Loader();
myImageRequest= new URLRequest(theImagePath);
myImageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,showMeTheVideo);
myImageLoader.load(myImageRequest);
}
function showMeTheVideo(evt:Event):void{
detailsMovieClip_mc.details_video_holder.dynamicVideoHolder.addChild(myImageLoader);
}
stopVideo(sectionname):viod{
if(detailsMovieClip_mc.details_video_holder.dynamicVideoHolder.numChildren !=0){
trace("what is the number of children: "+numChildren);
myImageLoader.unloadAndStop();
detailsMovieClip_mc.details_video_holder.
dynamicVideoHolder.removeChild(myImageLoader);
}
}
stage.addEventListener(MouseEvent.CLICK, removeSWF);
function removeSWF (e:MouseEvent):void
{
if(detailMovieClip.movieHolder.numChildren !=0){
myLoader.unloadAndStop();
detailMovieClip.movieHolder.removeChild(myLoader);// empty the movieClip memory
}
}
OR
Name your Loader instance and then search by using getChildByName
myLoader.name = "myloader";
function removeSWF (e:MouseEvent):void
{
if(detailMovieClip.movieHolder.numChildren !=0){
Loader(detailMovieClip.movieHolder.getChildByName("myloader")).unloadAndStop();
detailMovieClip.movieHolder.removeChild(detailMovieClip.movieHolder.getChildByName("myloader"));// empty the movieClip memory
}
}
I guess this is because you are adding the loader to the scene itsef.
Either you want to keep this behavior, in this case there is a quick fix, remove the loader from the MovieClip by using removeChild(), then set the reference to null, or use the delete keyword.
Either you want to do it properly, in this case, listen for the LOADED event, adds the MovieClip contained by the loader.content to the target MovieClip. Then, when you want to unload it, remove the clip from the container using removeChild(), then loader.unload().